Includes: - docs/: architecture, block-schema, migration-plan, product-specification - scripts/: bidi_table, decompose-decisions, extract-claims, seed-knowledge, etc. - skill-legal-decision/: SKILL.md + references + block-schema - skill-legal-assistant/: SKILL.md - skill-legal-docx/: SKILL.md + references - .claude/commands/: bidi-table skill - .taskmaster/: task config + PRDs - .gitignore: exclude legacy/, kiryat-yearim/, node_modules/, memory/ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
977 lines
32 KiB
Markdown
977 lines
32 KiB
Markdown
---
|
||
name: legal-docx
|
||
description: >
|
||
יצירת מסמכים משפטיים בעברית בפורמט DOCX עם תמיכה מלאה ב-RTL, עקוב אחר שינויים,
|
||
והערות. משתמש בסקיל הבסיסי docx ומוסיף התמחות בתחום המשפטי הישראלי.
|
||
|
||
טריגרים: "מסמך משפטי", "הסכם", "כתב הגנה", "כתב תביעה", "בקשה", "תצהיר",
|
||
"מכתב התראה", "חוזה", "הסכם שירותים", "ייפוי כוח", "פרוטוקול", "החלטה",
|
||
"צו", "פסק דין", "כתב טענות", בקשה ליצור מסמך DOCX בעברית, מסמך RTL,
|
||
"tracked changes בעברית", "הערות שוליים משפטיות", "עקוב אחר שינויים".
|
||
|
||
גם מתאים כאשר המשתמש מבקש מסמך Word בעברית עם פונט David/FrankRuehl/Miriam,
|
||
שוליים 2.5 ס"מ, או כל מסמך מקצועי בעברית שדורש עיצוב משפטי מדויק.
|
||
|
||
פיצ'רים: טבלאות RTL, הערות שוליים, תוכן עניינים, היפרלינקים,
|
||
לוגו/נייר פירמה, עריכת DOCX קיים, tracked changes, comments,
|
||
מרווח שורות, קו תחתי, מספר סקשנים, זיהוי אוטומטי של סוג מסמך.
|
||
---
|
||
|
||
# Legal DOCX v3.0 — מסמכים משפטיים בעברית
|
||
|
||
סקיל זה מרחיב את סקיל docx הבסיסי עם התמחות במסמכים משפטיים ישראליים.
|
||
|
||
**תמיד לקרוא קודם** את `/mnt/skills/public/docx/SKILL.md` — הסקיל הזה מניח שאתה מכיר את תהליך העבודה הבסיסי (docx-js, unpack/pack, tracked changes, comments).
|
||
|
||
---
|
||
|
||
## 🔴 קריטי: כללי RTL שחייבים לזכור
|
||
|
||
### הכלל המרכזי: START/END במקום LEFT/RIGHT
|
||
|
||
**במסמך עברי עם `bidirectional: true`, לעולם אל תשתמש ב-`AlignmentType.LEFT` או `AlignmentType.RIGHT` לפסקאות ומספור!**
|
||
|
||
| רוצה יישור ל... | ❌ לא להשתמש | ✅ להשתמש |
|
||
|-----------------|-------------|----------|
|
||
| **ימין** | `LEFT` או `RIGHT` | `AlignmentType.START` |
|
||
| **שמאל** | `LEFT` או `RIGHT` | `AlignmentType.END` |
|
||
| **מרכז** | — | `AlignmentType.CENTER` |
|
||
| **שני צדדים** | — | `AlignmentType.BOTH` |
|
||
|
||
> **למה?** כש-`bidirectional: true`, Word מתבלבל עם LEFT/RIGHT. `START` = התחלה = ימין ב-RTL, `END` = סוף = שמאל ב-RTL.
|
||
|
||
### שלוש הגדרות RTL חובה
|
||
|
||
כל מסמך עברי חייב את **שלושת** ההגדרות הבאות בכל הרמות:
|
||
|
||
```javascript
|
||
// 1. ברמת ה-Section
|
||
sections: [{
|
||
properties: {
|
||
bidi: true // ← חובה!
|
||
}
|
||
}]
|
||
|
||
// 2. ברמת כל Paragraph
|
||
new Paragraph({
|
||
bidirectional: true, // ← חובה!
|
||
alignment: AlignmentType.BOTH, // או START/CENTER/END
|
||
})
|
||
|
||
// 3. ברמת כל TextRun
|
||
new TextRun({
|
||
text: "טקסט בעברית",
|
||
rightToLeft: true, // ← חובה!
|
||
font: "David",
|
||
})
|
||
```
|
||
|
||
**חוסר באחת מהן = יישור שגוי או טקסט הפוך!**
|
||
|
||
---
|
||
|
||
## זיהוי סוג מסמך — Document Type Detection
|
||
|
||
**לפני יצירת מסמך, זהה את סוגו.** לכל סוג יש מבנה שונה:
|
||
|
||
| סוג מסמך | דוגמאות | Header בית משפט? | מבנה מיוחד |
|
||
|----------|---------|------------------|------------|
|
||
| **כתב טענות** | תביעה, הגנה, בקשה, ערעור, תצהיר, בר"ע | ✅ כן | טבלת Header עם בית משפט + מספר תיק |
|
||
| **מכתב התראה** | התראה, דרישה, מכתב עו"ד | ❌ לא | לוגו/פרטי משרד, "הנדון:", חתימה |
|
||
| **הסכם/חוזה** | הסכם שירותים, NDA, חוזה שכירות | ❌ לא | הואילים, צדדים, חתימות בשני טורים |
|
||
| **מסמך כללי** | חוות דעת, מזכר, סיכום | ❌ לא | לפי הצורך |
|
||
|
||
### טריגרים לזיהוי
|
||
|
||
```
|
||
כתב טענות ← "בית משפט", "בית הדין", "תביעה", "הגנה", "בקשה",
|
||
"ערעור", "תצהיר", "המבקש", "המשיב", "התובע", "הנתבע", "בר\"ע"
|
||
|
||
מכתב התראה ← "התראה", "דרישה", "לכבוד", "הנדון:", "נשלח מבלי לפגוע"
|
||
|
||
הסכם/חוזה ← "הסכם", "חוזה", "בין:", "לבין:", "הואיל", "צד א'", "צד ב'",
|
||
"ולראיה באו הצדדים"
|
||
```
|
||
|
||
---
|
||
|
||
## פונטים ומידות
|
||
|
||
### פונטים משפטיים
|
||
|
||
| פונט | שימוש | size (half-points) |
|
||
|------|-------|-------------------|
|
||
| **David** | ברירת מחדל, גוף טקסט | 24 (12pt) |
|
||
| **FrankRuehl** | פורמלי/שמרני | 24 (12pt) |
|
||
| **Miriam** | מודרני יותר | 24 (12pt) |
|
||
|
||
**חשוב:** תמיד להגדיר גם `w:cs` (Complex Script) וגם `w:ascii`/`w:hAnsi`:
|
||
```javascript
|
||
new TextRun({ text: "...", font: "David", rightToLeft: true })
|
||
// docx-js מייצר: <w:rFonts w:ascii="David" w:cs="David" w:eastAsia="David" w:hAnsi="David"/>
|
||
```
|
||
|
||
### מידות ושוליים
|
||
|
||
```
|
||
2.5 ס"מ = 1417 DXA (ברירת מחדל משפטי)
|
||
3.0 ס"מ = 1701 DXA
|
||
2.0 ס"מ = 1134 DXA
|
||
1.0 אינץ' = 1440 DXA
|
||
|
||
A4 = 11906 × 16838 DXA
|
||
רוחב תוכן (A4, שוליים 2.5 ס"מ) = 9072 DXA
|
||
רוחב תוכן (A4, שוליים 2.0 ס"מ) = 9638 DXA
|
||
```
|
||
|
||
---
|
||
|
||
## מספור סעיפים משפטיים
|
||
|
||
**⚠️ שים לב: `alignment: AlignmentType.START` — לא LEFT ולא RIGHT!**
|
||
|
||
```javascript
|
||
numbering: {
|
||
config: [{
|
||
reference: "legal-clauses",
|
||
levels: [
|
||
{
|
||
level: 0,
|
||
format: LevelFormat.DECIMAL,
|
||
text: "%1.",
|
||
alignment: AlignmentType.START, // ✅ START — לא RIGHT!
|
||
suffix: "tab",
|
||
style: { paragraph: { indent: { left: 720, hanging: 360 } } }
|
||
},
|
||
{
|
||
level: 1,
|
||
format: LevelFormat.DECIMAL,
|
||
text: "%1.%2",
|
||
alignment: AlignmentType.START, // ✅ START — לא RIGHT!
|
||
suffix: "tab",
|
||
style: { paragraph: { indent: { left: 1440, hanging: 500 } } }
|
||
},
|
||
{
|
||
level: 2,
|
||
format: LevelFormat.DECIMAL,
|
||
text: "%1.%2.%3",
|
||
alignment: AlignmentType.START, // ✅ START
|
||
suffix: "tab",
|
||
style: { paragraph: { indent: { left: 2160, hanging: 640 } } }
|
||
}
|
||
]
|
||
}]
|
||
}
|
||
```
|
||
|
||
**שימוש:**
|
||
```javascript
|
||
new Paragraph({
|
||
bidirectional: true,
|
||
numbering: { reference: "legal-clauses", level: 0 },
|
||
children: [new TextRun({ text: "תוכן הסעיף", font: "David", size: 24, rightToLeft: true })]
|
||
})
|
||
```
|
||
|
||
> **הבעיה שנפתרה:** בלי `AlignmentType.START`, המספור מופיע כ-".1" במקום "1."
|
||
|
||
---
|
||
|
||
## טבלאות RTL
|
||
|
||
**⚠️ קריטי: `visuallyRightToLeft: true` — בלי זה העמודות יהיו הפוכות!**
|
||
|
||
```javascript
|
||
const { Table, TableRow, TableCell, BorderStyle, WidthType, ShadingType } = require('docx');
|
||
|
||
const CONTENT_WIDTH = 9072; // A4 עם שוליים 2.5 ס"מ (11906 - 1417×2)
|
||
const border = { style: BorderStyle.SINGLE, size: 1, color: "999999" };
|
||
const borders = { top: border, bottom: border, left: border, right: border };
|
||
const noBorders = {
|
||
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||
left: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||
right: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" }
|
||
};
|
||
|
||
// Helper function לתאים בעברית
|
||
const rtlCell = (text, width, opts = {}) => new TableCell({
|
||
borders: opts.noBorders ? noBorders : borders,
|
||
width: { size: width, type: WidthType.DXA },
|
||
margins: { top: 80, bottom: 80, left: 120, right: 120 },
|
||
...(opts.shading ? { shading: { fill: opts.shading, type: ShadingType.CLEAR } } : {}),
|
||
children: [new Paragraph({
|
||
bidirectional: true,
|
||
alignment: opts.alignment || AlignmentType.CENTER,
|
||
children: [new TextRun({
|
||
text, font: "David", size: 24, rightToLeft: true, bold: opts.bold
|
||
})]
|
||
})]
|
||
});
|
||
|
||
// טבלה עם גבולות
|
||
new Table({
|
||
visuallyRightToLeft: true, // ✅ קריטי! בלי זה העמודות הפוכות
|
||
width: { size: CONTENT_WIDTH, type: WidthType.DXA },
|
||
columnWidths: [4536, 2268, 2268], // חייב להסתכם ל-CONTENT_WIDTH
|
||
rows: [
|
||
new TableRow({ children: [
|
||
rtlCell("סוג שירות", 4536, { bold: true, shading: "D5E8F0" }),
|
||
rtlCell("תעריף", 2268, { bold: true, shading: "D5E8F0" }),
|
||
rtlCell("הערות", 2268, { bold: true, shading: "D5E8F0" }),
|
||
]}),
|
||
new TableRow({ children: [
|
||
rtlCell("ייעוץ משפטי", 4536),
|
||
rtlCell("850 ש״ח", 2268),
|
||
rtlCell("בתוספת מע״מ", 2268),
|
||
]}),
|
||
]
|
||
})
|
||
|
||
// טבלה ללא גבולות (לחתימות / header)
|
||
new Table({
|
||
visuallyRightToLeft: true,
|
||
width: { size: CONTENT_WIDTH, type: WidthType.DXA },
|
||
columnWidths: [CONTENT_WIDTH / 2, CONTENT_WIDTH / 2],
|
||
rows: [
|
||
new TableRow({ children: [
|
||
rtlCell("חתימה: ________", CONTENT_WIDTH / 2, { noBorders: true, alignment: AlignmentType.CENTER }),
|
||
rtlCell("חתימה: ________", CONTENT_WIDTH / 2, { noBorders: true, alignment: AlignmentType.CENTER }),
|
||
]})
|
||
]
|
||
})
|
||
```
|
||
|
||
**כללים:**
|
||
- **`visuallyRightToLeft: true`** — חובה! בלי זה העמודות משמאל לימין
|
||
- **`WidthType.DXA`** — לא PERCENTAGE (פחות אמין ב-RTL)
|
||
- **`columnWidths`** — סכום חייב להיות שווה ל-`CONTENT_WIDTH`
|
||
- **`bidirectional: true` + `rightToLeft: true`** — בכל תא
|
||
|
||
---
|
||
|
||
## Tracked Changes — עקוב אחר שינויים
|
||
|
||
### שם מחבר בעברית
|
||
```xml
|
||
<w:del w:id="10" w:author="עו"ד כהן" w:date="2026-02-06T09:00:00Z">
|
||
```
|
||
|
||
### שינוי ערך (סכום, תאריך, תקופה)
|
||
פצל את הטקסט ועטוף רק את הערך שמשתנה:
|
||
```xml
|
||
<w:r><w:rPr>...RTL PROPS...</w:rPr>
|
||
<w:t xml:space="preserve">שכר הטרחה יעמוד על סך של </w:t></w:r>
|
||
<w:del w:id="10" w:author="עו"ד כהן" w:date="...">
|
||
<w:r><w:rPr>...RTL PROPS...</w:rPr><w:delText>750</w:delText></w:r>
|
||
</w:del>
|
||
<w:ins w:id="11" w:author="עו"ד כהן" w:date="...">
|
||
<w:r><w:rPr>...RTL PROPS...</w:rPr><w:t>850</w:t></w:r>
|
||
</w:ins>
|
||
<w:r><w:rPr>...RTL PROPS...</w:rPr>
|
||
<w:t xml:space="preserve"> ש״ח לשעת עבודה</w:t></w:r>
|
||
```
|
||
|
||
### מחיקת סעיף שלם
|
||
סמן גם את ה-paragraph mark כ-deleted:
|
||
```xml
|
||
<w:p>
|
||
<w:pPr>
|
||
<w:bidi/>
|
||
<w:jc w:val="both"/>
|
||
<w:rPr>
|
||
<w:del w:id="20" w:author="עו"ד כהן" w:date="..."/>
|
||
</w:rPr>
|
||
</w:pPr>
|
||
<w:del w:id="21" w:author="עו"ד כהן" w:date="...">
|
||
<w:r><w:rPr>...RTL PROPS...</w:rPr>
|
||
<w:delText>הסעיף שנמחק</w:delText></w:r>
|
||
</w:del>
|
||
</w:p>
|
||
```
|
||
|
||
### RTL PROPS — בלוק rPr מלא לכל run
|
||
```xml
|
||
<w:rPr>
|
||
<w:rFonts w:ascii="David" w:cs="David" w:eastAsia="David" w:hAnsi="David"/>
|
||
<w:sz w:val="24"/>
|
||
<w:szCs w:val="24"/>
|
||
<w:rtl/>
|
||
</w:rPr>
|
||
```
|
||
|
||
### קבלה/דחייה של שינויים
|
||
|
||
**קבלת Insertion:**
|
||
```
|
||
לפני: <w:ins w:id="5" w:author="..."><w:r>...<w:t>טקסט חדש</w:t></w:r></w:ins>
|
||
אחרי: <w:r>...<w:t>טקסט חדש</w:t></w:r>
|
||
→ הסר את תגית <w:ins> ושמור את התוכן הפנימי.
|
||
```
|
||
|
||
**דחיית Insertion:**
|
||
```
|
||
לפני: <w:ins w:id="5" w:author="..."><w:r>...<w:t>טקסט חדש</w:t></w:r></w:ins>
|
||
אחרי: (הסר לחלוטין)
|
||
→ מחק את כל בלוק ה-<w:ins> כולל תוכנו.
|
||
```
|
||
|
||
**קבלת מחיקה:**
|
||
```
|
||
לפני: <w:del w:id="10" w:author="..."><w:r>...<w:delText>טקסט שנמחק</w:delText></w:r></w:del>
|
||
אחרי: (הסר לחלוטין)
|
||
→ מחק את כל בלוק ה-<w:del> כולל תוכנו — המחיקה מתקבלת.
|
||
```
|
||
|
||
**שחזור טקסט מקורי (דחיית מחיקה):**
|
||
```
|
||
לפני: <w:del w:id="10" w:author="..."><w:r>...<w:delText>טקסט מקורי</w:delText></w:r></w:del>
|
||
אחרי: <w:r>...<w:t>טקסט מקורי</w:t></w:r>
|
||
→ הסר <w:del>, החלף <w:delText> ב-<w:t>, הסר <w:del> מ-rPr אם קיים.
|
||
```
|
||
|
||
---
|
||
|
||
## הערות (Comments)
|
||
|
||
הערות משמשות בסקירה משפטית להסביר *למה* בוצע שינוי:
|
||
|
||
```bash
|
||
python /mnt/skills/public/docx/scripts/comment.py unpacked/ 0 "הערה בעברית" --author "עו״ד כהן"
|
||
```
|
||
|
||
שימושים נפוצים:
|
||
- הסבר לשינוי סכום או תאריך
|
||
- דגל על סעיף בעייתי
|
||
- הפניה לפסיקה או חקיקה
|
||
- שאלה ללקוח / לצד השני
|
||
|
||
> **הערה:** `comment.py` מטפל אוטומטית ב-Content_Types ו-relationships.
|
||
|
||
---
|
||
|
||
## עריכת DOCX קיים (Unpack → Edit → Pack)
|
||
|
||
### תהליך מאומת
|
||
```bash
|
||
# 1. פתיחת הקובץ
|
||
python /mnt/skills/public/docx/scripts/unpack.py input.docx unpacked/
|
||
|
||
# 2. עריכת word/document.xml (או קבצי XML אחרים)
|
||
|
||
# 3. ארגון מחדש
|
||
python /mnt/skills/public/docx/scripts/pack.py unpacked/ output.docx --original input.docx
|
||
```
|
||
|
||
### מיקום הוספת תוכן — כלל קריטי
|
||
```
|
||
⚠️ פסקאות חדשות חייבות להיכנס *לפני* <w:sectPr> האחרון בגוף המסמך.
|
||
הוספה *אחרי* sectPr תיכשל בוולידציה.
|
||
|
||
מבנה תקין:
|
||
<w:body>
|
||
<w:p>...</w:p> ← פסקאות קיימות
|
||
<w:p>...</w:p> ← פסקה חדשה כאן ✅
|
||
<w:sectPr>...</w:sectPr> ← תמיד אחרון
|
||
</w:body>
|
||
```
|
||
|
||
### דוגמה — הוספת פסקה בעברית
|
||
```xml
|
||
<w:p>
|
||
<w:pPr>
|
||
<w:bidi/>
|
||
<w:jc w:val="both"/>
|
||
</w:pPr>
|
||
<w:r>
|
||
<w:rPr>
|
||
<w:rFonts w:ascii="David" w:cs="David" w:eastAsia="David" w:hAnsi="David"/>
|
||
<w:sz w:val="24"/>
|
||
<w:szCs w:val="24"/>
|
||
<w:rtl/>
|
||
</w:rPr>
|
||
<w:t>הטקסט החדש</w:t>
|
||
</w:r>
|
||
</w:p>
|
||
```
|
||
|
||
---
|
||
|
||
## הערות שוליים (Footnotes)
|
||
|
||
**השימוש המרכזי:** הפניות לחקיקה ופסיקה.
|
||
|
||
```javascript
|
||
const { FootnoteReferenceRun } = require('docx');
|
||
|
||
// 1. הגדרה ב-Document:
|
||
const doc = new Document({
|
||
footnotes: {
|
||
1: { children: [new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.START, // ✅ START
|
||
children: [new TextRun({
|
||
text: "חוק החוזים (חלק כללי), התשל״ג-1973, סעיף 12.",
|
||
font: "David", size: 20, rightToLeft: true // 10pt להערות שוליים
|
||
})]
|
||
})] },
|
||
2: { children: [new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.START,
|
||
children: [new TextRun({
|
||
text: "ע״א 1234/20 כהן נ׳ לוי, פסקה 15 (פורסם בנבו, 1.1.2024).",
|
||
font: "David", size: 20, rightToLeft: true
|
||
})]
|
||
})] },
|
||
},
|
||
// ...sections
|
||
});
|
||
|
||
// 2. הפניה בגוף הטקסט:
|
||
new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.BOTH,
|
||
children: [
|
||
new TextRun({ text: "חובת תום הלב", font: "David", size: 24, rightToLeft: true }),
|
||
new FootnoteReferenceRun(1),
|
||
new TextRun({ text: " חלה על כל שלבי המשא ומתן", font: "David", size: 24, rightToLeft: true }),
|
||
new FootnoteReferenceRun(2),
|
||
new TextRun({ text: ".", font: "David", size: 24, rightToLeft: true }),
|
||
]
|
||
})
|
||
```
|
||
|
||
### תיקון RTL בהערות שוליים (post-unpack)
|
||
docx-js לא מגדיר RTL מלא בהערות שוליים. אחרי unpack, צריך לתקן ב-`word/footnotes.xml`:
|
||
```xml
|
||
<!-- 1. הוסף pStyle + bidi לכל הערת שוליים: -->
|
||
<w:footnote w:id="1">
|
||
<w:p>
|
||
<w:pPr>
|
||
<w:pStyle w:val="FootnoteText"/>
|
||
<w:bidi/>
|
||
<w:jc w:val="start"/>
|
||
</w:pPr>
|
||
...
|
||
|
||
<!-- 2. הוסף rtl ל-footnoteRef run: -->
|
||
<w:r>
|
||
<w:rPr>
|
||
<w:rStyle w:val="FootnoteReference"/>
|
||
<w:rtl/>
|
||
</w:rPr>
|
||
<w:footnoteRef/>
|
||
</w:r>
|
||
```
|
||
|
||
---
|
||
|
||
## מרווח שורות (Line Spacing)
|
||
|
||
**דרישת בתי המשפט:** בדרך כלל 1.5 שורות.
|
||
|
||
```javascript
|
||
const { LineRuleType } = require('docx');
|
||
|
||
// LineRuleType.AUTO — הערך הוא ב-1/240 שורה
|
||
spacing: { line: 240, lineRule: LineRuleType.AUTO } // 1.0 — צפוף
|
||
spacing: { line: 276, lineRule: LineRuleType.AUTO } // 1.15 — ברירת מחדל Word
|
||
spacing: { line: 360, lineRule: LineRuleType.AUTO } // 1.5 — נדרש בבתי משפט
|
||
spacing: { line: 480, lineRule: LineRuleType.AUTO } // 2.0 — כפול
|
||
|
||
// שילוב עם before/after:
|
||
spacing: { line: 360, lineRule: LineRuleType.AUTO, before: 120, after: 120 }
|
||
```
|
||
|
||
---
|
||
|
||
## תוכן עניינים (TOC)
|
||
|
||
**⚠️ חובה: TOC ידני (לא TableOfContents).**
|
||
`TableOfContents` של docx-js מייצר שדה שוורד מעדכן ב-F9 ומאבד הגדרות RTL.
|
||
|
||
```javascript
|
||
const { Tab, TabStopType, LeaderType, PageBreak } = require('docx');
|
||
|
||
// שורת TOC ידנית
|
||
const tocEntry = (text, pageNum, opts = {}) => new Paragraph({
|
||
bidirectional: true,
|
||
spacing: { after: 60, line: 276, lineRule: LineRuleType.AUTO },
|
||
...(opts.indent ? { indent: { right: opts.indent } } : {}),
|
||
tabStops: [{ type: TabStopType.RIGHT, position: 9026, leader: LeaderType.DOT }],
|
||
children: [
|
||
new TextRun({
|
||
text, font: "David", size: 24, rightToLeft: true,
|
||
bold: opts.bold || false,
|
||
}),
|
||
new TextRun({ children: [new Tab()], font: "David", rightToLeft: true }),
|
||
new TextRun({
|
||
text: String(pageNum), font: "David", size: 24, rightToLeft: true,
|
||
}),
|
||
]
|
||
});
|
||
|
||
// שימוש:
|
||
new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.CENTER,
|
||
spacing: { after: 200 },
|
||
children: [new TextRun({
|
||
text: "תוכן עניינים", font: "David", size: 32, bold: true, rightToLeft: true
|
||
})]
|
||
}),
|
||
tocEntry("פרק א׳ — הגדרות כלליות", 2, { bold: true }),
|
||
tocEntry("1. הגדרות יסוד", 2, { indent: 400 }),
|
||
tocEntry("פרק ב׳ — השירותים", 3, { bold: true }),
|
||
new Paragraph({ children: [new PageBreak()] }),
|
||
```
|
||
|
||
---
|
||
|
||
## קו תחתי (Underline)
|
||
|
||
```javascript
|
||
const { UnderlineType } = require('docx');
|
||
|
||
// קו תחתי רגיל:
|
||
new TextRun({
|
||
text: "נושא: הסכם שירותים",
|
||
font: "David", size: 24, rightToLeft: true,
|
||
underline: { type: UnderlineType.SINGLE }
|
||
})
|
||
|
||
// קו תחתי כפול (לכותרות חשובות):
|
||
underline: { type: UnderlineType.DOUBLE }
|
||
|
||
// סוגים שימושיים: SINGLE, DOUBLE, THICK, DOTTED, DASH, WAVE
|
||
```
|
||
|
||
---
|
||
|
||
## מספר סקשנים (Multiple Sections)
|
||
|
||
**שימוש:** כותרות שונות לנספחים, עמוד לרוחב לטבלאות, שוליים שונים.
|
||
|
||
```javascript
|
||
const doc = new Document({
|
||
sections: [
|
||
// סקשן 1 — גוף ההסכם
|
||
{
|
||
properties: {
|
||
page: { size: { width: 11906, height: 16838 },
|
||
margin: { top: 1417, right: 1417, bottom: 1417, left: 1417 } },
|
||
bidi: true,
|
||
},
|
||
headers: {
|
||
default: new Header({ children: [new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.CENTER,
|
||
children: [new TextRun({ text: "הסכם שירותים", font: "David", size: 20, bold: true, rightToLeft: true })]
|
||
})] })
|
||
},
|
||
children: [ /* ... */ ]
|
||
},
|
||
// סקשן 2 — נספח עם כותרת שונה
|
||
{
|
||
properties: {
|
||
page: { size: { width: 11906, height: 16838 },
|
||
margin: { top: 1417, right: 1417, bottom: 1417, left: 1417 } },
|
||
bidi: true,
|
||
},
|
||
headers: {
|
||
default: new Header({ children: [new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.START, // ✅ START
|
||
children: [new TextRun({ text: "נספח א׳ — לוח תעריפים", font: "David", size: 20, bold: true, rightToLeft: true })]
|
||
})] })
|
||
},
|
||
children: [ /* ... */ ]
|
||
}
|
||
]
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## לוגו/תמונה בכותרת (Letterhead)
|
||
|
||
```javascript
|
||
const { ImageRun } = require('docx');
|
||
|
||
const logoBuffer = fs.readFileSync('/path/to/logo.png');
|
||
|
||
headers: {
|
||
default: new Header({
|
||
children: [
|
||
new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
children: [
|
||
new ImageRun({
|
||
data: logoBuffer,
|
||
transformation: { width: 200, height: 60 }, // pixels
|
||
type: "png",
|
||
}),
|
||
],
|
||
}),
|
||
new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.CENTER,
|
||
children: [new TextRun({
|
||
text: "משרד עורכי דין ישראלי ושות׳",
|
||
font: "David", size: 20, bold: true, rightToLeft: true
|
||
})],
|
||
}),
|
||
],
|
||
}),
|
||
}
|
||
```
|
||
|
||
**הערה:** תמונה חייבת להיות קובץ אמיתי — לבקש מהמשתמש אם אין.
|
||
|
||
---
|
||
|
||
## היפרלינקים
|
||
|
||
```javascript
|
||
const { ExternalHyperlink, UnderlineType } = require('docx');
|
||
|
||
new Paragraph({
|
||
bidirectional: true,
|
||
children: [
|
||
new TextRun({ text: "ראה: ", font: "David", size: 24, rightToLeft: true }),
|
||
new ExternalHyperlink({
|
||
link: "https://www.nevo.co.il/law_html/law01/073_002.htm",
|
||
children: [new TextRun({
|
||
text: "חוק החוזים באתר נבו",
|
||
font: "David", size: 24, rightToLeft: true,
|
||
color: "0563C1",
|
||
underline: { type: UnderlineType.SINGLE },
|
||
})],
|
||
}),
|
||
]
|
||
})
|
||
```
|
||
|
||
**⚠️ אזהרות:**
|
||
- **לא להשתמש ב-`style: "Hyperlink"`** — מפריע ל-RTL!
|
||
- **לא להוסיף `alignment: AlignmentType.RIGHT`** — `bidirectional: true` מספיק
|
||
|
||
---
|
||
|
||
## תבניות מסמכים — Document Templates
|
||
|
||
### תבנית 1: כתב טענות (בקשה, תביעה, הגנה, ערעור)
|
||
|
||
```javascript
|
||
const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
|
||
AlignmentType, LevelFormat, BorderStyle, WidthType } = require('docx');
|
||
|
||
const PAGE_WIDTH = 11906;
|
||
const MARGINS = { top: 1134, right: 1134, bottom: 1134, left: 1134 };
|
||
const CONTENT_WIDTH = PAGE_WIDTH - MARGINS.left - MARGINS.right;
|
||
|
||
const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
|
||
const noBorders = { top: noBorder, bottom: noBorder, left: noBorder, right: noBorder };
|
||
|
||
// Header בית משפט — טבלה עם שם בית המשפט (ימין) ומספר תיק (שמאל)
|
||
function courtHeader(courtName, caseNumber) {
|
||
return new Table({
|
||
width: { size: CONTENT_WIDTH, type: WidthType.DXA },
|
||
columnWidths: [CONTENT_WIDTH / 2, CONTENT_WIDTH / 2],
|
||
visuallyRightToLeft: true,
|
||
rows: [
|
||
new TableRow({
|
||
children: [
|
||
new TableCell({
|
||
width: { size: CONTENT_WIDTH / 2, type: WidthType.DXA },
|
||
borders: noBorders,
|
||
children: [new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.START,
|
||
children: [new TextRun({ text: courtName, bold: true, font: "David", size: 26, rightToLeft: true })]
|
||
})]
|
||
}),
|
||
new TableCell({
|
||
width: { size: CONTENT_WIDTH / 2, type: WidthType.DXA },
|
||
borders: noBorders,
|
||
children: [new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.END,
|
||
children: [new TextRun({ text: caseNumber, bold: true, font: "David", size: 26, rightToLeft: true })]
|
||
})]
|
||
})
|
||
]
|
||
})
|
||
]
|
||
});
|
||
}
|
||
|
||
// כותרת ראשית ממורכזת עם קו תחתון
|
||
function mainTitle(text) {
|
||
return new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.CENTER,
|
||
spacing: { before: 300, after: 300 },
|
||
children: [new TextRun({ text, bold: true, font: "David", size: 28, rightToLeft: true, underline: {} })]
|
||
});
|
||
}
|
||
|
||
// כותרת משנה מיושרת לימין עם קו תחתון
|
||
function subHeading(text) {
|
||
return new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.START,
|
||
spacing: { before: 240, after: 120 },
|
||
children: [new TextRun({ text, bold: true, font: "David", size: 24, rightToLeft: true, underline: {} })]
|
||
});
|
||
}
|
||
|
||
// שימוש:
|
||
const doc = new Document({
|
||
numbering: {
|
||
config: [{
|
||
reference: "legal-clauses",
|
||
levels: [{
|
||
level: 0, format: LevelFormat.DECIMAL, text: "%1.",
|
||
alignment: AlignmentType.START, suffix: "tab",
|
||
style: { paragraph: { indent: { left: 360, hanging: 360 } } }
|
||
}]
|
||
}]
|
||
},
|
||
sections: [{
|
||
properties: {
|
||
page: { size: { width: PAGE_WIDTH, height: 16838 }, margin: MARGINS },
|
||
bidi: true
|
||
},
|
||
children: [
|
||
courtHeader("בית המשפט המחוזי בתל אביב", "ת\"א 12345-01-26"),
|
||
mainTitle("כתב תביעה"),
|
||
// ... פרטי צדדים, סעיפים, חתימה
|
||
]
|
||
}]
|
||
});
|
||
```
|
||
|
||
### תבנית 2: מכתב התראה
|
||
|
||
```javascript
|
||
// מכתב התראה — ללא header בית משפט, עם פרטי משרד
|
||
|
||
function letterHeader(firmName, address, phone, email) {
|
||
return [
|
||
new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.START,
|
||
children: [new TextRun({ text: firmName, bold: true, font: "David", size: 28, rightToLeft: true })]
|
||
}),
|
||
new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.START,
|
||
children: [new TextRun({ text: address, font: "David", size: 22, rightToLeft: true })]
|
||
}),
|
||
new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.START,
|
||
spacing: { after: 300 },
|
||
children: [new TextRun({ text: `טל': ${phone} | ${email}`, font: "David", size: 22, rightToLeft: true })]
|
||
}),
|
||
];
|
||
}
|
||
|
||
function subjectLine(text) {
|
||
return new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.CENTER,
|
||
spacing: { before: 200, after: 200 },
|
||
children: [
|
||
new TextRun({ text: "הנדון: ", bold: true, font: "David", size: 24, rightToLeft: true }),
|
||
new TextRun({ text, bold: true, font: "David", size: 24, rightToLeft: true, underline: {} })
|
||
]
|
||
});
|
||
}
|
||
|
||
// שימוש:
|
||
sections: [{
|
||
properties: { page: { ... }, bidi: true },
|
||
children: [
|
||
...letterHeader("משרד עו\"ד כהן ושות'", "רח' הרצל 1, תל אביב", "03-1234567", "office@cohen-law.co.il"),
|
||
new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.START,
|
||
children: [new TextRun({ text: "תאריך: 10.2.2026", font: "David", size: 24, rightToLeft: true })]
|
||
}),
|
||
new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.START,
|
||
spacing: { before: 200 },
|
||
children: [new TextRun({ text: "לכבוד: [שם הנמען]", font: "David", size: 24, rightToLeft: true })]
|
||
}),
|
||
subjectLine("התראה בטרם נקיטת הליכים משפטיים"),
|
||
// ... גוף המכתב
|
||
]
|
||
}]
|
||
```
|
||
|
||
### תבנית 3: הסכם/חוזה
|
||
|
||
```javascript
|
||
// הסכם — הואילים, צדדים, חתימות בשני טורים
|
||
|
||
function contractTitle(text) {
|
||
return new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.CENTER,
|
||
spacing: { after: 300 },
|
||
children: [new TextRun({ text, bold: true, font: "David", size: 32, rightToLeft: true })]
|
||
});
|
||
}
|
||
|
||
function partyClause(label, name, id, address, alias) {
|
||
return new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.BOTH,
|
||
spacing: { after: 120 },
|
||
children: [
|
||
new TextRun({ text: `${label}: `, bold: true, font: "David", size: 24, rightToLeft: true }),
|
||
new TextRun({ text: `${name}, ח.פ./ת.ז. ${id}, מ${address} (להלן: "`, font: "David", size: 24, rightToLeft: true }),
|
||
new TextRun({ text: alias, bold: true, font: "David", size: 24, rightToLeft: true }),
|
||
new TextRun({ text: '")', font: "David", size: 24, rightToLeft: true }),
|
||
]
|
||
});
|
||
}
|
||
|
||
function signatureTable() {
|
||
return new Table({
|
||
width: { size: CONTENT_WIDTH, type: WidthType.DXA },
|
||
columnWidths: [CONTENT_WIDTH / 2, CONTENT_WIDTH / 2],
|
||
visuallyRightToLeft: true,
|
||
rows: [
|
||
new TableRow({
|
||
children: [
|
||
new TableCell({
|
||
borders: noBorders,
|
||
children: [
|
||
new Paragraph({ bidirectional: true, alignment: AlignmentType.CENTER,
|
||
children: [new TextRun({ text: "_________________", font: "David", size: 24, rightToLeft: true })] }),
|
||
new Paragraph({ bidirectional: true, alignment: AlignmentType.CENTER,
|
||
children: [new TextRun({ text: "צד א'", font: "David", size: 24, rightToLeft: true })] })
|
||
]
|
||
}),
|
||
new TableCell({
|
||
borders: noBorders,
|
||
children: [
|
||
new Paragraph({ bidirectional: true, alignment: AlignmentType.CENTER,
|
||
children: [new TextRun({ text: "_________________", font: "David", size: 24, rightToLeft: true })] }),
|
||
new Paragraph({ bidirectional: true, alignment: AlignmentType.CENTER,
|
||
children: [new TextRun({ text: "צד ב'", font: "David", size: 24, rightToLeft: true })] })
|
||
]
|
||
})
|
||
]
|
||
})
|
||
]
|
||
});
|
||
}
|
||
|
||
// שימוש:
|
||
sections: [{
|
||
properties: { page: { ... }, bidi: true },
|
||
children: [
|
||
contractTitle("הסכם שירותים"),
|
||
new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.CENTER,
|
||
children: [new TextRun({ text: "נערך ונחתם בתל אביב ביום __________", font: "David", size: 24, rightToLeft: true })]
|
||
}),
|
||
partyClause("מצד אחד", "[שם]", "[מספר]", "[כתובת]", "המזמין"),
|
||
partyClause("מצד שני", "[שם]", "[מספר]", "[כתובת]", "הספק"),
|
||
// הואילים...
|
||
// סעיפים...
|
||
new Paragraph({
|
||
bidirectional: true, alignment: AlignmentType.CENTER,
|
||
spacing: { before: 400, after: 300 },
|
||
children: [new TextRun({ text: "ולראיה באו הצדדים על החתום:", bold: true, font: "David", size: 24, rightToLeft: true })]
|
||
}),
|
||
signatureTable()
|
||
]
|
||
}]
|
||
```
|
||
|
||
---
|
||
|
||
## Quick Reference — טבלת עזר מהיר
|
||
|
||
### יישור
|
||
|
||
| רוצה | השתמש ב... | ❌ לא להשתמש |
|
||
|------|-----------|-------------|
|
||
| ימין | `AlignmentType.START` | `LEFT`, `RIGHT` |
|
||
| שמאל | `AlignmentType.END` | `LEFT`, `RIGHT` |
|
||
| מרכז | `AlignmentType.CENTER` | — |
|
||
| שני צדדים | `AlignmentType.BOTH` | `JUSTIFIED` |
|
||
|
||
### גדלי טקסט (half-points)
|
||
|
||
| שימוש | size | נקודות |
|
||
|-------|------|--------|
|
||
| גוף טקסט | 24 | 12pt |
|
||
| כותרת משנה | 26 | 13pt |
|
||
| כותרת ראשית | 28-32 | 14-16pt |
|
||
| הערות שוליים | 20 | 10pt |
|
||
| Header/Footer | 18-20 | 9-10pt |
|
||
|
||
### מרווחי שורות
|
||
|
||
| רווח | line value |
|
||
|------|-----------|
|
||
| 1.0 | 240 |
|
||
| 1.15 | 276 |
|
||
| 1.5 | 360 |
|
||
| 2.0 | 480 |
|
||
|
||
### Checklist — הגדרות חובה
|
||
|
||
```
|
||
☐ Section: bidi: true
|
||
☐ Paragraph: bidirectional: true
|
||
☐ TextRun: rightToLeft: true
|
||
☐ Numbering: alignment: AlignmentType.START
|
||
☐ Table: visuallyRightToLeft: true
|
||
☐ Footnotes: alignment: AlignmentType.START
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 Troubleshooting
|
||
|
||
**מספור מופיע הפוך (.1 במקום 1.):**
|
||
→ וודא `alignment: AlignmentType.START` במספור (לא RIGHT!)
|
||
|
||
**טקסט מופיע משמאל לימין:**
|
||
→ וודא את שלושת ההגדרות: Section `bidi`, Paragraph `bidirectional`, TextRun `rightToLeft`
|
||
|
||
**עמודות טבלה הפוכות:**
|
||
→ הוסף `visuallyRightToLeft: true` לטבלה
|
||
|
||
**columnWidths לא מסתכם:**
|
||
→ וודא שסכום כל הרוחבים = CONTENT_WIDTH (9072 לשוליים 2.5 ס"מ, חישוב: 11906 - 1417×2)
|
||
|
||
**המסמך לא נפתח / שגיאה ב-Word:**
|
||
→ בדוק שלא הוספת פסקה אחרי `<w:sectPr>` (חייב להיות אחרון ב-body)
|
||
→ וודא `npm list docx` >= 8.0.0
|
||
|
||
**הערות שוליים לא ב-RTL:**
|
||
→ אחרי unpack, תקן ידנית ב-word/footnotes.xml (ראה סעיף הערות שוליים)
|
||
|
||
---
|
||
|
||
## טעויות נפוצות — Common Pitfalls
|
||
|
||
| ❌ טעות | ✅ תיקון |
|
||
|--------|---------|
|
||
| `AlignmentType.RIGHT` במספור | `AlignmentType.START` |
|
||
| `AlignmentType.LEFT` ליישור שמאלי | `AlignmentType.END` |
|
||
| טבלה בלי `visuallyRightToLeft` | הוסף `visuallyRightToLeft: true` |
|
||
| שכחת `bidirectional: true` בפסקה | הוסף לכל פסקה |
|
||
| שכחת `rightToLeft: true` ב-TextRun | הוסף לכל TextRun |
|
||
| שכחת `bidi: true` ב-Section | הוסף ל-properties |
|
||
| הוספת פסקה אחרי `sectPr` | הוסף לפני `sectPr` |
|
||
| שימוש ב-`style: "Hyperlink"` | הגדר ידנית `color` + `underline` |
|
||
| `columnWidths` לא מסתכם נכון | וודא שהסכום = `CONTENT_WIDTH` |
|
||
|
||
---
|
||
|
||
## קבצי עזר
|
||
|
||
- **`references/document-types.md`** — מבנים מפורטים ל-9 סוגי מסמכים משפטיים
|
||
- **`scripts/create-legal-doc.js`** — סקריפט בסיסי עם כל הגדרות ה-RTL המתוקנות
|
||
|
||
---
|
||
|
||
## Dependencies
|
||
|
||
```bash
|
||
npm install docx
|
||
```
|
||
|
||
**גרסה מומלצת:** docx >= 8.0.0
|