עזרה | עזרה בדף html עם js וcss - בעיות בקוד
-
לצורך "אוצריא", יזם @יום-חדש-מתחיל יצירת עורך טקסט שיותאם לאוצריא.
אחרי אין ספור קשקושים עם o1, ומאות גרסאות, עדיין ישנם באגים בקוד.
אנו לא יודעים להתעסק עם זה, ולכן אנו מבקשים את עזרתכם...
הנה הגרסה שנראית [כנראה] הכי פחות באגית.
https://github.com/Y-PLONI/otsria-books-editing?tab=readme-ov-file
תוכלו לראות [ולהתנסות] בקישור שברדמי.
בעיות:
רוצים להוסיף גרירה לחלונית הכותרות להרחבתה/הצרתה.
בעיות שורות ריקות [לצרף תמונה]
אם משפט מוגדל [מתחיל ומסתיים ב"big", ומקטינים באמצע, הוא רק עוטף את המילה המוקטנת ב"small" במקום לשבור את כל המשפט [כלומר, להוסיף סיום ביג ותחילתו]. אותו הדבר במשפט מודגש או נטוי, וביטול מילה אחת באמצע.
בבחירת מילה שלא יבחר את הרווח שאחריה, אלא רק את המילה עצמה.
בהקלקה משולשת [בחירת קטע שלם], נבחר גם הרווח האחרון - מה גורם שה"b" הסוגר נכתב רק בשורה שאח"כ [זה לא יוצר בעיה למעשה, כי אוצריא מכירה בתגים שנפתחים ולא נסגרים... אבל זה לא תקין].
בעיות עיצוב קלות: חוסר תאימות בין מראה חלונית הכותרות לחלונית הטקסט.
הזזת שם הספר הנטען ליד כפתור "טען קובץ".
הזזת סרגל הגלילה השמאלי לתוך החלונית עצמה [כמו בחלונית הכותרות].
יצירת בועות הסבר על הכפתורים.
הגדרה שאם אין את הגופן, שיעבור לאריאל [בעקבות בעיה שקרתה!]
לא זוכר עוד כרגע...
מסמא יש המון זבל בקוד, מדרים שניסינו ולא צלחו... כמו כתיבה ישר לקובץ, ביטול [קונטרול + Z], ועוד.
אני יודע שיש שישאלו למה הכל בתוך קוד אחד, ולא בקבצים מחולקים. התשובה פשוטה: זה נועד להנתן לכאלו שצריכים את זה... ולא כאלו שיכולים לערוך ישר מפנקס רשימות...
הנה הקוד, אבל לא יעזור לנו כלום אם לא תדחפו לגיט!!!!
חושב שאתם תדעו לעזור: @sivan22 @pcinfogmach @יעקב-מ-פינס @לא-מתייאש @יום_שמח @מוטי-אורן מי עוד???
[תמונה יפה יותר נמצאת בגיטהאב, אך אני לא יודע מה צריך בדיוק כדי לייבא את השינויים מהגרסה ההיא לזו... מי שרוצה גרסאות שיש בהן חלק גדול מהשינויים שכתבתי, שיכתוב לי. יש בהן אלף בעיות אחרות... מה שנקרא AI...]
<!DOCTYPE html> <html lang="he" dir="rtl"> <head> <meta charset="UTF-8"> <title>עורך הספרים לאוצריא</title> <style> /* איפוס ועיצוב בסיסי */ * { box-sizing: border-box; } html, body { margin: 0; padding: 0; height: 100%; width: 100%; font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; background-color: #f2f2f2; } /* תפריט עליון (fixed), כך שתמיד יהיה מעל (לא יגלול יחד עם הטקסט). גובה: 50px */ .top-bar { position: fixed; top: 0; right: 0; left: 0; height: 50px; background: #1976d2; color: #fff; display: flex; align-items: center; justify-content: space-between; /* שמאל/ימין */ box-shadow: 0 2px 4px rgba(0,0,0,0.2); z-index: 9999; padding: 0 8px; } /* בלוק אמצעי - כפתורי עיצוב */ .top-bar-middle { display: flex; align-items: center; gap: 8px; margin-left: 8px; margin-right: 8px; } /* בלוק שמאלי - כפתור טעינת קובץ */ .top-bar-left { display: flex; align-items: center; gap: 8px; margin-left: 8px; margin-right: 8px; } /* בלוק ימני - כפתור שם קובץ, רענן, שמירה */ .top-bar-right { display: flex; align-items: center; gap: 8px; margin-right: 8px; margin-left: 8px; } /* כפתורים */ button, .file-label { display: inline-flex; align-items: center; justify-content: center; border: none; border-radius: 16px; padding: 6px 12px; background: #1565c0; color: #fff; cursor: pointer; font-size: 0.9rem; transition: background 0.3s ease; text-decoration: none; } button:hover, .file-label:hover { background: #0d47a1; } /* כפתורים מיוחדים */ .save-button { background: #388e3c; /* ירוק Material */ } .save-button:hover { background: #2e7d32; } .refresh-button { background: #5d4037; /* חום */ } .refresh-button:hover { background: #4e342e; } /* תווית שם הקובץ */ .file-name-display { font-weight: bold; color: #ffeb3b; /* צהבהב */ margin-left: 4px; } /* מסתירים input[type=file], מציגים רק label */ input[type="file"] { display: none; } /* חלונית כותרות בצד ימין (fixed) - לא תזוז בגלילה */ .side-panel { position: fixed; top: 65px; /* מתחת ל-top-bar */ right: 0; bottom: 0; width: 220px; background: #fff; box-shadow: -2px 0 4px rgba(0,0,0,0.1); overflow-y: auto; padding: 16px; z-index: 10; direction: rtl; /* שמירה על כיוון שמאל לימין עבור התוכן */ } .side-panel::-webkit-scrollbar { width: 8px; /* רוחב פס הגלילה */ direction: rtl; /* כיוון פס הגלילה */ } .side-panel::-webkit-scrollbar-thumb { background-color: #b6b4b4; /* צבע הפס */ border-radius: 4px; } .side-panel::-webkit-scrollbar-thumb:hover { background-color: #555; /* צבע הפס כשעוברים מעליו */ } .side-panel h2 { font-size: 1rem; margin: 8px 0; } .outline-list { list-style-type: none; margin: 0; padding: 0; } .outline-list li { margin-bottom: 6px; cursor: pointer; color: #1976d2; word-wrap: break-word; } .outline-list li:hover { text-decoration: underline; } /* אזור התוכן (Scrollable) - נקבע כ-FIXED כך שהטקסט יגלול בתוכו באופן עצמאי, בעוד ה-top-bar וה-side-panel נשארים מעליו. */ .main-container { position: fixed; top: 50px; /* מתחת ל-toolbar */ left: 0; right: 220px; /* כדי לא להתנגש ב-side-panel */ bottom: 0; overflow-y: auto; /* גלילה עצמאית לתוכן */ /* רקע מסוים */ background: #f2f2f2; } /* במסגרת התוכן הגולל הזה, נשים editor-container וכו' */ .editor-container { margin: 16px; background: #fff; box-shadow: 0 2px 4px rgba(0,0,0,0.1); border-radius: 6px; padding: 16px; min-height: 80vh; direction: ltr; /* scrollbar מימין */ } #editor { min-height: 70vh; outline: none; cursor: text; width: 100%; direction: rtl; /* כתיבה מימין לשמאל */ text-align: right; white-space: pre-wrap; /* \n => ירידת שורה */ } /* מניעת מרווחים סביב כותרות */ #editor h1, #editor h2, #editor h3, #editor h4, #editor h5, #editor h6 { margin: 0; padding: 0; } </style> </head> <body> <!-- תפריט עליון (Fixed) --> <div class="top-bar"> <!-- צד שמאל: כפתורי עיצוב --> <div class="top-bar-left"> <label class="file-label" for="fileInput">טען קובץ</label> </div> <!-- צד אמצע: כפתורי עיצוב --> <div class="top-bar-middle"> <button onclick="toggleHeading(1)">H1</button> <button onclick="toggleHeading(2)">H2</button> <button onclick="toggleHeading(3)">H3</button> <button onclick="toggleHeading(4)">H4</button> <button onclick="toggleHeading(5)">H5</button> <button onclick="toggleHeading(6)">H6</button> <button onclick="toggleBold()">הדגש</button> <button onclick="toggleItalic()">נטוי</button> <button onclick="toggleBig()">גדול</button> <button onclick="toggleSmall()">קטן</button> </div> <!-- צד ימין: טעינת קובץ, שם קובץ, רענן, שמירה --> <div class="top-bar-right"> <input type="file" id="fileInput" accept=".txt,.html" /> <span id="fileNameSpan" class="file-name-display"></span> <button class="refresh-button" onclick="refreshEditor()">רענן טקסט</button> <button class="save-button" onclick="saveFile()">שמירה</button> </div> </div> <!-- חלונית כותרות בצד ימין (Fixed) --> <div class="side-panel"> <h2>כותרות במסמך</h2> <ul class="outline-list" id="outlineList"></ul> </div> <!-- אזור התוכן, גולל באופן עצמאי (Fixed) --> <div class="main-container"> <div class="editor-container"> <div id="editor" contenteditable="true" onkeydown="handleEnter(event)" > <p>טען קובץ והתחל לערוך אותו!</p> </div> </div> </div> <script> /** * משתנים גלובליים */ let originalContent = ""; let lastLoadedFile = null; let lastLoadedFileName = ""; /** 1) מניעת <div> או <br> ב-Enter: רק \n */ function handleEnter(e) { if (e.key === "Enter") { e.preventDefault(); const sel = window.getSelection(); if (!sel.rangeCount) return; const range = sel.getRangeAt(0); const newLine = document.createTextNode("\n"); range.insertNode(newLine); range.setStartAfter(newLine); range.setEndAfter(newLine); sel.removeAllRanges(); sel.addRange(range); } } /** 2) הדגשה/ נטוי/ גדול/ קטן - בלי מצבי כפילות */ function toggleBold() { toggleSize('b'); updateOutline(); } function toggleItalic() { toggleSize('i'); updateOutline(); } function toggleBig() { toggleSize('big'); updateOutline(); } function toggleSmall() { toggleSize('small'); updateOutline(); } /** * 3) לוגיקה כללית לכל תגי b,i,big,small: * - אם יש כבר (b,i,big,small) => הסר הכל. * - אחרת עוטפים ב-tagRequested. * - בעת הייצוא נמחק גם צמדים <big><small> וכו' או ריקים. * - ביטול הדגשה חלקי => נטפל ע"י פיצול טקסט בהמשך (אם תרצה). */ function toggleSize(tagRequested) { const sel = window.getSelection(); if (!sel.rangeCount) return; removeSelectionSpaces(sel); // לא עוטף רווחים const range = sel.getRangeAt(0); const htmlBefore = getRangeHTML(range); // מזהים אם כבר יש tagRequested, או תגים אחרים const hasTag = new RegExp(`<${tagRequested}[^>]*>`,'i').test(htmlBefore); const hasAnyTag = /<(?:b|i|big|small)\b[^>]*>/i.test(htmlBefore); // שליפה const extracted = range.extractContents(); const container = document.createElement("div"); container.appendChild(extracted); let inner = container.innerHTML; // הסרה מוחלטת של b,i,big,small inner = removeAllSizeTags(inner); // אם כבר היה tag כלשהו => ביטלנו, לא מוסיפים. // אם לא היה => נוסיף tagRequested if (!hasAnyTag) { inner = `<${tagRequested}>${inner}</${tagRequested}>`; } // החזרה למסמך const newFrag = document.createRange().createContextualFragment(inner); range.insertNode(newFrag); // מקמים את הסמן אחרי const selRange = document.createRange(); selRange.setStartAfter(newFrag); selRange.collapse(true); sel.removeAllRanges(); sel.addRange(selRange); } /** 4) הסרת כל <b>/<i>/<big>/<small> ממחרוזת */ function removeAllSizeTags(html) { return html .replace(/<\s*(?:b|i|big|small)\s*>/gi, "") .replace(/<\s*\/\s*(?:b|i|big|small)\s*>/gi, ""); } /** קריאת ה-HTML מתוך ה-Range */ function getRangeHTML(range) { const tempDiv = document.createElement("div"); tempDiv.appendChild(range.cloneContents()); return tempDiv.innerHTML; } /** הסרת רווחים מובילים/סוגרים מהבחירה (שלא ייכנסו לתג) */ function removeSelectionSpaces(sel) { if (!sel.rangeCount) return; const range = sel.getRangeAt(0); trimSelectionStart(range, sel); trimSelectionEnd(range, sel); } function trimSelectionStart(range, sel) { const startNode = range.startContainer; const offset = range.startOffset; if (startNode.nodeType === Node.TEXT_NODE) { const textVal = startNode.nodeValue; let i = offset; while (i < textVal.length && textVal[i] === " ") i++; if (i > offset) { range.setStart(startNode, i); sel.removeAllRanges(); sel.addRange(range); } } } function trimSelectionEnd(range, sel) { const endNode = range.endContainer; let offset = range.endOffset; if (endNode.nodeType === Node.TEXT_NODE) { const textVal = endNode.nodeValue; let i = offset - 1; while (i >= 0 && textVal[i] === " ") i--; const newEnd = i + 1; if (newEnd < offset) { range.setEnd(endNode, newEnd); sel.removeAllRanges(); sel.addRange(range); } } } /** 5) כותרות: מוסיף \n לפני (אם חסר). אם כבר עטוף => מסיר */ function toggleHeading(level) { const sel = window.getSelection(); if (!sel.rangeCount) return; const range = sel.getRangeAt(0); const extracted = range.collapsed ? null : range.extractContents(); const container = document.createElement('div'); if (extracted) container.appendChild(extracted); let inner = container.innerHTML.trim(); const openTag = `<h${level}>`; const closeTag = `</h${level}>`; addNewlineBefore(range); // אם כבר עטוף => הסר if (inner.startsWith(openTag) && inner.endsWith(closeTag)) { inner = inner.slice(openTag.length, inner.length - closeTag.length); const frag = document.createRange().createContextualFragment(inner); range.insertNode(frag); } else { // אחרת עוטפים const headingElem = document.createElement(`h${level}`); headingElem.innerHTML = inner; range.insertNode(headingElem); } range.setStartAfter(range.endContainer); range.setEndAfter(range.endContainer); sel.removeAllRanges(); sel.addRange(range); updateOutline(); } /** מוסיף \n לפני הכותרת, אם אין */ function addNewlineBefore(range) { const textNode = range.startContainer; if (textNode && textNode.nodeType === 3) { if (!textNode.nodeValue.endsWith("\n")) { const newLine = document.createTextNode("\n"); range.insertNode(newLine); range.setStartAfter(newLine); range.setEndAfter(newLine); const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } } else { const newLine = document.createTextNode("\n"); range.insertNode(newLine); range.setStartAfter(newLine); range.setEndAfter(newLine); const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } } /** 6) עדכון חלונית כותרות (צד ימין) */ function updateOutline() { const editor = document.getElementById("editor"); const outlineList = document.getElementById("outlineList"); outlineList.innerHTML = ""; const headings = editor.querySelectorAll("h1, h2, h3, h4, h5, h6"); headings.forEach((heading) => { const headingText = heading.textContent.trim() || heading.tagName + " ריקה"; const li = document.createElement("li"); li.textContent = headingText; const level = parseInt(heading.tagName.substring(1), 10); const indent = (level - 1) * 20; li.style.marginRight = indent + "px"; li.onclick = () => { heading.scrollIntoView({ behavior: "smooth", block: "start" }); }; outlineList.appendChild(li); }); } /** 7) טעינת קובץ + הצגת שמו */ document.getElementById("fileInput").addEventListener("change", function (event) { const file = event.target.files[0]; if (!file) return; lastLoadedFile = file; lastLoadedFileName = file.name; // מציג את השם document.getElementById("fileNameSpan").textContent = lastLoadedFileName; const reader = new FileReader(); reader.onload = function (e) { const content = e.target.result; originalContent = content; document.getElementById("editor").innerHTML = content; updateOutline(); }; reader.readAsText(file, "UTF-8"); }); /** 8) רענן טקסט (טוען מחדש את הקובץ האחרון) */ function refreshEditor() { if (!lastLoadedFile) return; const reader = new FileReader(); reader.onload = function(e) { const content = e.target.result; document.getElementById('editor').innerHTML = content; updateOutline(); }; reader.readAsText(lastLoadedFile, "UTF-8"); } /** * 9) prettifyHTML לצורכי שמירה: * - מוסיף \n לפני <hX>, \n אחרי </hX> * - מוחק צמדים <big><small>... ו </small></big>... * (בין אם יש תוכן או לא?) * - מוחק תגים ריקים (b,i,big,small) * - מצמצם \n כפולים */ function prettifyHTML(html) { // כותרות html = html.replace(/<h([1-6])>/g, '\n<h$1>'); html = html.replace(/<\/h([1-6])>/g, '</h$1>\n'); // מחיקת צמדים <big><small>......</small></big> => נעלם לגמרי // או <small><big>......</big></small> // נשתמש ב-regex g, מוודאים שאין nested tags חיצוניים. // אם תרצה למחוק רק אם ריק – אפשר לצמצם. // כאן נניח שכל המופע מנוטרל. // נפעיל בלולאה עד שאין יותר: let prev; do { prev = html; // סוג ראשון <big><small>...</small></big> html = html.replace(/<big><small>([\s\S]*?)<\/small><\/big>/gi, '$1'); // סוג שני <small><big>...</big></small> html = html.replace(/<small><big>([\s\S]*?)<\/big><\/small>/gi, '$1'); } while (html !== prev); // מחיקת תגים ריקים (b,i,big,small), שוב בלולאה do { prev = html; html = html.replace(/<(b|i|big|small)\s*>\s*<\/\1>/gi, ''); } while (html !== prev); // צמצום שורות ריקות html = html.replace(/\n\s*\n+/g, '\n'); return html.trim(); } /** 10) שמירה (אין שמירה-עותק או פונקציות נוספות) */ function saveFile() { if (!lastLoadedFileName) { alert("לא נטען קובץ. אנא טען קובץ קודם."); return; } let editorContent = document.getElementById('editor').innerHTML; editorContent = prettifyHTML(editorContent); const blob = new Blob([editorContent], { type: "text/plain;charset=utf-8" }); const link = document.createElement("a"); link.download = lastLoadedFileName; link.href = window.URL.createObjectURL(blob); link.click(); originalContent = editorContent; } /* בעת עליית הדף */ document.addEventListener('DOMContentLoaded', () => { updateOutline(); }); </script> </body> </html>
-
-
@mendelg כתב בעזרה | עזרה בדף html עם js וcss - בעיות בקוד:
שלא נשתמש במשהו
יש בעיה בסיסית בכל עורכי הטקסט שקיימים ושיבנו אי-פעם.
או שאתה עובד על דף HTML, כולל כל התגים או שאתה עובד על טקסט.
"אוצריא" משחקת עם זה: ירידת שורה היא /n ולא P, למשל.
זה אומר שהכל ישתבש אם נעבוד עם עורך רגיל.
לא בדקתי את זה ספציפית, אבל בדקנו 5-6 עורכים שונים לפני זה. -
@pcinfogmach כתב בעזרה | עזרה בדף html עם js וcss - בעיות בקוד:
@י-פל
עדיין ספק לי אפ אתה צריך עורך אחר זה סיפר של כמה שורות קוד להחליף את P ב- \n
ברוב המקרים גם את זה לא תצטרך רק תצטרך להסיר את ה-p כי הוא כבר יהיה בשורה בפני עצמו@mendelg
אבל אין באמת N!!! זה ירידת שורה רגילה! [אנטר בלע"ז].אולי אני לא מבין מהחיים שלי, אבל למשל כאן: אין אנטר, אלא ירידת שורה. זה מה שאני מתכוון.אם אני טועה, אדרבה, הרבה יותר טוב!!!
-
@mendelg כתב בעזרה | עזרה בדף html עם js וcss - בעיות בקוד:
אולי אוכל לבנות את זה בפלאטר אם צריך..
אני מציע אם @mendelg רוצה שתגידו לו מה אתם צריכים וכנראה הוא יעשה את זה בקלות
כיוון אחר אם תרצו הוא לתת לאנשים לעשות בוורד ואז לעשות מאקרו פשוט שיייצא על ידי הופסת התגים הנכונים עם שימוש פשוט של חיפוש והחלפה אוכל לעזור בלי נדר אם זה הכיוון
-
פוסט זה נמחק!
-
-
@mendelg כתב בעזרה | עזרה בדף html עם js וcss - בעיות בקוד:
אם אני מבין את @י-פל נכון, אתה אומר ששורה חדשה בקוד הוא ב \n אבל באוצריא אין את ה \n אלא אנטר? (משהו כזה אתה מנסה להגיד)
אם הבנתי נכון את מה שאמרתם:
אתם הגבתם על מה שאמרתי שבאוצריא זה לא P אלא N, שנעשה החלפה וזהו. ואני הגבתי שזה לא באמת N, אלא "אנטר" [שנקרא N].
אני מובן?
כעת אפשר לסדר את זה או לא?
@mendelg ? -
@pcinfogmach כתב בעזרה | עזרה בדף html עם js וcss - בעיות בקוד:
אגב אוצריא תומך ב-css?
תומך רק בinline
-
@י-פל מה אתה אומר על זה? (זה מבוסס על quill)
https://sivan22.github.io/otzaria-editor/
הקוד כאן: https://github.com/Sivan22/otzaria-editor/blob/main/index.html
-
@sivan22 כתב בעזרה | עזרה בדף html עם js וcss - בעיות בקוד:
@י-פל מה אתה אומר על זה? (זה מבוסס על quill)
https://sivan22.github.io/otzaria-editor/
הקוד כאן: https://github.com/Sivan22/otzaria-editor/blob/main/index.html
לא הבנתי מה עשית בכלל... [סליחה על הסגנון, אבל זה כ"כ לא מתאים למושלמות שלך]
נתחיל:
למי מיועד העורך?
העורך מיועד לאלו שלא יודעים מהחיים שלהם, כן?!
ולכן, אני לא יכול לומר להם "תעשו H1 ויהיה לכם כותרת", ולכן הם גם לא עורכים בפנקס רשימות...
כל העניין זה שהוא ל--------א יציג את הH1, B ,I וכדומה, אלא יציג אותם "כאילו זה אוצריא", ורק בייצוא חזרה יהיה לנו קובץ טקסט עם התגים.
בקישור שנתת רואים את התגים.
חלונית הכותרות נמצאת איפה שהפך השני עם השמן [זה שעוד לא מצאו.... ]
בכפתורים לגודל טקסט אינם נוחים [רוצה כפתור לכל דבר. אגב, בעיקרון בתחילה הכפתור להגדלה/הקטנה גם ביטל אותה בלחיצה שוב, אך באחת הגרסאות זה נעלם... ואיננו כי לקח אותה הGPT...]
לחיצה על כותרת מדגישה את כל הבלוק.
אין נטוי.
אין "רענן טקסט" [כלומר - טעינת הטקסט מחדש. הסיבה לכפתור זה, היא הצורך בהרצת סקריפטים תוכ"ד, ואז ריענון כדי לראות את התוצאה].
יש בטל, שזו מעלה גדולה! -
@pcinfogmach כתב בעזרה | עזרה בדף html עם js וcss - בעיות בקוד:
אגב אוצריא תומך ב-css?
התשובה היא, וודאי!!
תוכל לראות כאן את הרשימה המדוייקת מה נתמך -
https://pub.dev/packages/flutter_html#currently-supported-html-tagsאם תרצה לראות בעיניים חלק גדול מהאפשרויות ש'אוצריא' תומכת, תוכל להכניס את הקובץ הבא לתיקיית ספרי אוצריא, ותראה בעצמך...
עריכת ספר לאוצריא.zip