Reorganize: skills/ directory + move memory to docs/
skill-legal-decision/ → skills/decision/ skill-legal-assistant/ → skills/assistant/ skill-legal-docx/ → skills/docx/ memory/*.md → docs/ Also removed: TASKS.md (use TaskMaster), classifier.py (replaced by local_classifier.py) Updated all references in CLAUDE.md, scripts, PRDs, docs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
976
skills/docx/SKILL.md
Normal file
976
skills/docx/SKILL.md
Normal file
@@ -0,0 +1,976 @@
|
||||
---
|
||||
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
|
||||
Reference in New Issue
Block a user