webapp.sticky_notes_api module
Sticky Notes API for Markdown preview - Stores user-specific notes per file in MongoDB - Endpoints: list, create, update, delete
- webapp.sticky_notes_api.kickoff_index_warmup(*, background=True, delay_seconds=0.0)[מקור]
Run index warmup once during startup so requests won’t block on it.
- webapp.sticky_notes_api.list_notes(file_id)
List all sticky notes for current user and file.
- פרמטרים:
file_id (str)
- webapp.sticky_notes_api.BADGE_REFRESH_SECONDS = 300
כשיש בועה על המסך השרת מבקש מהלקוח לחזור לכל היותר בעוד חמש דקות — המרווח הקבוע שהיה כאן לפני הדגימה לפי השרת, וזה שריענן גם ניקוי ממכשיר אחר וגם מונה שהשתנה. הערך יושב בשרת ולא בלקוח כדי שללקוח יישאר כלל אחד: ”ישן כמה שהשרת אמר, בין הרצפה לתקרה“. תזכורת נוספת שמבשילה מוקדם יותר גוברת עליו.
- webapp.sticky_notes_api.reminders_summary()
Return minimal summary for persistent UI badge.
- Response:
{ ok, has_due: bool, count_due: int, next_in_seconds: int | null }
``next_in_seconds`` הוא מה שמחליף את הדגימה הקבועה. עד כאן הלקוח דגם כל חמש דקות בלי קשר למצב, כי ”כן/לא“ היה כל מה שקיבל — 954 קריאות ביממה שכולן החזירו ”אין“. השרת הוא היחיד שיודע גם מתי התזכורת הבאה, ולכן הוא זה שאומר ללקוח מתי לחזור — גם כשיש בועה: אז התשובה היא לכל היותר
BADGE_REFRESH_SECONDS, כדי שבועה שנוקתה ממכשיר אחר ומונה שהשתנה ייראו תוך דקות ולא אחרי חצי שעה. אין תזכורת עתידית ואין בועה ←None, והלקוח נרדם עד התקרה שלו (חצי שעה) — לא לנצח, כדי שתזכורת שתיקבע ממכשיר אחר עדיין תתגלה.והערך יחסי, לא חותמת.
remind_atשנקרא מהמסד הוא מודע-אזור רק כל עוד הלקוח נבנה עםtz_aware— ומחרוזת ISO בלי offset נקראת ב-JavaScript כזמן מקומי, כלומר שלוש שעות סטייה בלי שגיאה. מספר שניות אין לו אזור זמן, והוא גם חסין לשעון לקוח שסוטה.
- webapp.sticky_notes_api.reminders_list()
Return a list of due sticky‑note reminders for the current user.
Response:
{ "ok": true, "items": [ { "note_id": "...", "file_id": "...", "preview": "...", "anchor_id": "h2-intro", "anchor_text": "Intro", "remind_at": "2026-09-20T09:00:00+00:00" } ], "count": 1 }
remind_atהוא המועד שהפריט הזה נורה עליו; החלונית מחזירה אותו ב-ack, כדי שהאישור ייקשר למועד הזה ולא ל“איזו שהיא“ תזכורת של הפתק.
- webapp.sticky_notes_api.reminders_ack()
Mark current due reminder as acknowledged (user opened it).
סוגר את שני שדות המצב יחד. עד כאן נכתב
ack_atבלבד, ו-statusנשארpendingלנצח — כך שכרטיס הדשבורד, שסופר לפיstatus, דיווח תזכורות ”בהמתנה“ שאיש לא המתין להן. השדות מגיעים מ-note_reminder_state.acknowledge_fields(), שמחזירה את שניהם או אף אחד, ומונגו מחילה אותם ב-$setיחיד.ונקשר למועד שההתראה נשאה. בלי
remind_atבגוף, האישור סגר ”איזו שהיא“ תזכורת של הפתק; וכשהפתק נדרך מחדש אחרי שההתראה נורתה — בדיוק מה שעושים כשתזכורת מגיעה ברגע לא נוח — התראה ישנה במגש סגרה את התזכורת של מחר, כיset_note_reminderעושה upsert על אותו מסמך ויש תמיד שורה אחת לפגוע בה. עם המועד, הפילטר תופס רק את המסמך שעדיין נושא אותו: מסמך שנדרך מחדש או נדחה מאז עונה 404, ונשאר פעיל. לקוח שאינו שולח מועד (SW ישן במטמון, התראה שהוצגה לפני השדה) מאשר בלי קשירה — ההתנהגות של קודם.
- webapp.sticky_notes_api.create_note(file_id)
Create a new sticky note for a file.
- פרמטרים:
file_id (str)
- webapp.sticky_notes_api.update_note(note_id)
Update existing note; only owner can update.
- פרמטרים:
note_id (str)
- webapp.sticky_notes_api.delete_note(note_id)
Delete a note; only owner can delete.
- פרמטרים:
note_id (str)
- webapp.sticky_notes_api.batch_update_notes()
Batch update multiple notes in one request.
Body format (JSON):
{ "updates": [ { "id": "...", "content": "...", "position": {"x": 120, "y": 240}, "size": {"width": 260, "height": 200}, "color": "yellow_light", "is_minimized": false, "line_start": 10, "line_end": null, "anchor_id": "h2-intro", "anchor_text": "Intro", "mode": "surface", "prev_updated_at": "2024-01-01T00:00:00+00:00" } ] }
Response JSON contains
resultswith per-item status, e.g. 200/409.
- webapp.sticky_notes_api.list_board_notes(board_id)
פתקי לוח. שאילתה ישירה, בלי
$orובלי מעבר ב-code_snippets.- פרמטרים:
board_id (str)
- webapp.sticky_notes_api.list_repo_notes(repo_name, repo_path)
פתקים על קובץ בריפו ממורר.
סימון היתומים נעשה בקריאה, ולא בכתיבה. אין סריקה תקופתית ואין
update_manyעל מסלול קריאה — זה הלקח מסריקת היתומים של הלוחות. הפתק ממשיך להיות מוחזר עם הנתיב האחרון הידוע, ורק נושא דגל.
- webapp.sticky_notes_api.create_repo_note(repo_name, repo_path)
פתק חדש על קובץ בריפו ממורר.
- webapp.sticky_notes_api.list_repo_orphans(repo_name)
קומה ראשונה: פתקים בריפו הזה שהקובץ שלהם כבר אינו בעץ.
”מיותם“ נגזר מהמראה, לא מ-GitHub. דפדפן הריפו מציג עותק מסונכרן, ולכן קובץ שנמחק ב-GitHub לפני שעתיים עדיין קיים כאן והפתק עליו ייראה תקין עד הסנכרון הבא. לכן מוחזר גם
last_sync_time— זה לא באג אם כתוב, וכן באג אם לא.- פרמטרים:
repo_name (str)
- webapp.sticky_notes_api.list_orphan_repos()
קומה שנייה: ריפואים שיש להם פתקים ואינם ממוררים עוד.
בלי הקומה הזו פתקים של ריפו שהוסר לא מופיעים בשום תצוגה — לא בעץ שלו, כי אין עץ, ולא ברשימת היתומים, כי היא פר-ריפו. זה בדיוק ”המצב שנעלם בשקט“.
- webapp.sticky_notes_api.toggle_note_task(note_id)
מסמן או מבטל צ’קבוקס בתוך פתק.
הראוט הזה קיים כדי שאפשר יהיה לאמת את הכתיבה. האלטרנטיבה — לשלוח את התוכן המלא ב-
PUT /note/<id>— הופכת כל קליק לדריסת last-writer-wins של עריכות מקבילות, ובעיקר הופכת אימות לבלתי אפשרי: אפשר לאמת רק שכתבנו את מה ששלחנו. הבקשה כאן נושאת כוונה (מספר סידורי + מצב רצוי), וזה הדבר היחיד שניתן לאמת מול המסד.הסדר, וכל שלב בו מגן על משהו:
הפתק קיים ושייך למשתמש — אחרת 404.
prev_updated_at— אחרת 409, בדיוק כמו ב-update_note.סידורי שאינו קיים ⇒ 409, לא 200. התצוגה של הלקוח מיושנת.
כבר במצב המבוקש ⇒ 200 בלי כתיבה. אידמפוטנטי.
Compare-and-swap: הפילטר כולל את התוכן הקודם, כך שכותב מקביל אינו נדרס.
קריאה חוזרת מהמסד ובדיקה שהתו אכן השתנה. לא
modified_count, לאok: true— אלה מדווחים על הקריאה, לא על המצב.
בכל מסלול התשובה נושאת את התוכן הסמכותי, כדי שהלקוח יסתנכרן בלי סיבוב נוסף — וגם במסלול הכשל, שם זה מה שמאפשר לו להחזיר את התצוגה למה שבאמת שמור.
- פרמטרים:
note_id (str)
- webapp.sticky_notes_api.NOTE_SEARCH_DEFAULT_LIMIT = 30
כמה תוצאות מוחזרות כשלא נתבקש אחרת, וכמה הכי הרבה אפשר לבקש.
התקרה אינה קוסמטית.
limitמגיע מה-URL, כלומר מחוץ לתהליך, ובלי חסם עליון בקשה אחת יכולה לגרור את כל מכסת המשתמש (MAX_NOTES_PER_USER) לתוך המיון ולתוך התשובה. חמישים תוצאות הן יותר ממה שמישהו סורק בעין; מי שצריך יותר מצמצם את החיפוש.
- webapp.sticky_notes_api.NOTE_SEARCH_MAX_NEEDLE = 200
אורך מרבי למחט החיפוש עצמה.
קבוע נפרד מ-
NOTE_SEARCH_PREVIEW_CHARSאף ששניהם 200 היום: זה חוסם קלט שנכנס לרג’קס, וזה קובע כמה מהגוף מוצג. מספר משותף לשני דברים שאינם אותו דבר הוא מספר שמישהו ישנה בשביל האחד וישבור את השני.
- webapp.sticky_notes_api.build_note_search_pipeline(user_id, needle, *, color_id, limit)[מקור]
הצינור שמחפש פתקים — בנוי כאן, ונבדק כפי שנשלח.
סדר השלבים אינו קוסמטי, ושלושה מהם קיימים בגלל תקלות שכבר קרו בריפו הזה:
$match— הפילטר המשותף עם ה-MCP (note_search_filter()). אותה פונקציה ולא העתק: שני חיפושי פתקים שמתאימים אחרת הם שני מוצרים שמתחזים לאחד.$addFields— הדירוג והתצוגה המקדימה, שניהם לפני שהגוף יורד, כי שניהם נגזרים ממנו.$projectבהחרגה — הגוף יורד לפני ה-$sort. בסדר ההפוך מונגו החזירה בפרודקשן שגיאה 292 (QueryExceededMemoryLimitNoDiskUseAllowed) והקוד נפל למסלול איטי;allowDiskUseאינו מציל כי Atlas מתעלם ממנו ב-Flex. מתועד ב-tests/test_files_pipelines_drop_heavy_fields_early.py.$sort— כותרת לפני גוף, ובתוך כל קבוצה לפי עדכון אחרון. ``false < true`` בסדר ה-BSON, ולכן-1מעלה את פגיעות הכותרת.$limitעם שורת סנטינל (limit + 1), כמו ב-MCP: שורה נוספת שחזרה היא ראיה שיש עוד, ולא ניחוש מתוך ספירה ששווה לתקרה.$unset— שדה העזר של הדירוג אינו חלק מהתשובה.
``$substrCP`` ו-``$strLenCP``, ולעולם לא הגרסאות ב-Bytes. אות עברית היא שני בייטים, וחיתוך בבייטים על מספר שנספר בתווים נוחת באמצע תו ומפיל את השאילתה (
Location28657). זהamir-bug-patternsH6, והאירוע עצמו קרה כאן.הדירוג פשוט בכוונה. אין כאן משקלות מכוילים:
_calculate_relevance_scoreשהתיעוד מזכיר אינה קיימת בקוד כלל, וניקוד שאיש אינו יכול להסביר הוא ניקוד שאיש לא יתחזק. פתק הוא נושא שלם, ולכן השאלה היא איזה פתק — ומופע בשם הוא עדות חזקה יותר ממופע בגוף.
- webapp.sticky_notes_api.search_notes()
חיפוש בפתקים של המשתמש — בשם ובגוף, חוצה את שלושת היעדים.
אותה התאמה כמו ב-MCP. שניהם עוברים דרך
note_search_filter(), ולכן חיפוש של אותה מילה בעמוד ובסוכן מחזיר את אותה קבוצת פתקים. מה שנבדל הוא רק מה שכל צרכן מציג: ה-MCP מחזיר הפניות בלבד, והעמוד מוסיף תצוגה מקדימה.הרשאות: הפתקים של המשתמש, כל שלושת הסוגים — בדיוק כמו
codekeeper_search_notes, שאינו מגודר לאדמין (הגידור שם חל עלlist_repo_notes/create_repo_noteבלבד). גם/api/sticky-notes/repo/…בוובאפ נושא@require_authבלבד, כלומר גייט אדמין כאן היה מסתיר מהמשתמש פתקים שהוא כבר מושך בקריאה אחרת — גייט שאינו מגן על דבר.