MetaList 0.9.0__tar.gz → 0.11.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. {metalist-0.9.0 → metalist-0.11.0}/PKG-INFO +18 -1
  2. {metalist-0.9.0 → metalist-0.11.0}/README.md +17 -0
  3. {metalist-0.9.0 → metalist-0.11.0}/app/api/middleware/auth.py +0 -16
  4. {metalist-0.9.0 → metalist-0.11.0}/app/api/note_requests.py +17 -2
  5. {metalist-0.9.0 → metalist-0.11.0}/app/api/routes/auth.py +2 -1
  6. metalist-0.11.0/app/api/routes/files.py +244 -0
  7. {metalist-0.9.0 → metalist-0.11.0}/app/api/routes/notes.py +41 -152
  8. {metalist-0.9.0 → metalist-0.11.0}/app/api/routes/ontology.py +0 -4
  9. {metalist-0.9.0 → metalist-0.11.0}/app/api/upload_limits.py +13 -1
  10. metalist-0.11.0/app/db/editable_files_migration.py +50 -0
  11. metalist-0.11.0/app/db/file_schema.py +58 -0
  12. {metalist-0.9.0 → metalist-0.11.0}/app/db/files_sql.py +161 -2
  13. {metalist-0.9.0 → metalist-0.11.0}/app/db/migrations.py +2 -0
  14. {metalist-0.9.0 → metalist-0.11.0}/app/db/notes_sql.py +9 -8
  15. {metalist-0.9.0 → metalist-0.11.0}/app/db/version.py +1 -1
  16. {metalist-0.9.0 → metalist-0.11.0}/app/encryption_audit.py +74 -10
  17. {metalist-0.9.0 → metalist-0.11.0}/app/main.py +27 -0
  18. metalist-0.11.0/app/security/sensitive_cache.py +116 -0
  19. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/help_catalog.py +4 -2
  20. metalist-0.11.0/app/services/agent/markdown_validation.py +35 -0
  21. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/help-formatting.md +3 -0
  22. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/help-menus.md +2 -1
  23. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/help-notes.md +1 -1
  24. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/help-references.md +14 -1
  25. {metalist-0.9.0 → metalist-0.11.0}/app/services/backlinks.py +2 -1
  26. {metalist-0.9.0 → metalist-0.11.0}/app/services/backup_service.py +3 -1
  27. {metalist-0.9.0 → metalist-0.11.0}/app/services/client_state_service.py +2 -0
  28. {metalist-0.9.0 → metalist-0.11.0}/app/services/content_formatting.py +44 -2
  29. {metalist-0.9.0 → metalist-0.11.0}/app/services/embedded_references.py +251 -29
  30. {metalist-0.9.0 → metalist-0.11.0}/app/services/encryption.py +29 -12
  31. metalist-0.11.0/app/services/file_edit_sessions.py +81 -0
  32. {metalist-0.9.0 → metalist-0.11.0}/app/services/file_registry.py +7 -0
  33. {metalist-0.9.0 → metalist-0.11.0}/app/services/file_storage.py +403 -1
  34. {metalist-0.9.0 → metalist-0.11.0}/app/services/html_export.py +9 -1
  35. {metalist-0.9.0 → metalist-0.11.0}/app/services/image_size_formatting.py +1 -1
  36. {metalist-0.9.0 → metalist-0.11.0}/app/services/link_titles.py +16 -0
  37. {metalist-0.9.0 → metalist-0.11.0}/app/services/note_image_tags.py +11 -0
  38. {metalist-0.9.0 → metalist-0.11.0}/app/services/note_store.py +193 -48
  39. {metalist-0.9.0 → metalist-0.11.0}/app/services/request_recovery.py +4 -0
  40. {metalist-0.9.0 → metalist-0.11.0}/app/services/root_sorting.py +125 -59
  41. {metalist-0.9.0 → metalist-0.11.0}/app/services/search_history.py +84 -35
  42. {metalist-0.9.0 → metalist-0.11.0}/app/services/search_index.py +105 -48
  43. {metalist-0.9.0 → metalist-0.11.0}/app/services/snapshot.py +336 -176
  44. {metalist-0.9.0 → metalist-0.11.0}/app/services/store.py +3 -1
  45. {metalist-0.9.0 → metalist-0.11.0}/app/services/sync.py +7 -1
  46. {metalist-0.9.0 → metalist-0.11.0}/app/services/tab_state.py +8 -0
  47. {metalist-0.9.0 → metalist-0.11.0}/app/services/tag_suggestions.py +24 -31
  48. {metalist-0.9.0 → metalist-0.11.0}/app/services/tag_term_matching.py +35 -33
  49. {metalist-0.9.0 → metalist-0.11.0}/app/services/undo_state.py +72 -3
  50. metalist-0.11.0/app/services/view_cache.py +99 -0
  51. {metalist-0.9.0 → metalist-0.11.0}/app/static/config/agent-menu-actions.json +6 -0
  52. {metalist-0.9.0 → metalist-0.11.0}/app/static/config/command_palette_tags.json +5 -1
  53. {metalist-0.9.0 → metalist-0.11.0}/app/static/css/main.css +516 -117
  54. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/api-client.js +202 -6
  55. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/command-palette/command-palette-controller.js +30 -75
  56. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/command-palette/endpoint-registry.js +8 -0
  57. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/command-palette/note-layout-preferences.js +19 -0
  58. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/command-palette/tag-config-loader.js +94 -0
  59. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/config.js +8 -1
  60. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/context-menu/context-menu-registry.js +78 -1
  61. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/context-menu/context-menu-service.js +6 -0
  62. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/editor-selection.js +2 -1
  63. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/editor-toolbar.js +2 -1
  64. metalist-0.11.0/app/static/js/modules/excalidraw/excalidraw-actions.js +93 -0
  65. metalist-0.11.0/app/static/js/modules/excalidraw/excalidraw-autosave.js +159 -0
  66. metalist-0.11.0/app/static/js/modules/excalidraw/excalidraw-editor-service.js +388 -0
  67. metalist-0.11.0/app/static/js/modules/excalidraw/excalidraw-editor-state.js +35 -0
  68. metalist-0.11.0/app/static/js/modules/excalidraw/excalidraw-loader.js +50 -0
  69. metalist-0.11.0/app/static/js/modules/excalidraw/excalidraw-preview-service.js +163 -0
  70. metalist-0.11.0/app/static/js/modules/excalidraw/excalidraw-scene.js +89 -0
  71. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/expected-errors.js +20 -0
  72. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/note-layout-appearance-modal.js +18 -4
  73. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/actions/note-actions.js +6 -1
  74. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/actions/ui-actions.js +76 -29
  75. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/events/context-menu-events.js +17 -2
  76. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/events/focus-events.js +3 -2
  77. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/events/input-events.js +0 -3
  78. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/events/keyboard-events.js +30 -10
  79. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/events/mouse-events.js +41 -15
  80. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/mode-context.js +93 -0
  81. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/mode-manager-controller.js +8 -1
  82. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +50 -21
  83. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/differential-view-service.js +25 -1
  84. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/floating-note-service.js +4 -0
  85. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +4 -2
  86. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +6 -0
  87. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +41 -52
  88. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/note-fullscreen-service.js +2 -0
  89. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/polling-service.js +42 -6
  90. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +1 -1
  91. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/root-sort-service.js +7 -2
  92. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +13 -2
  93. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/tab-state-service.js +17 -4
  94. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +5 -4
  95. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +28 -0
  96. metalist-0.11.0/app/static/js/modules/mode-manager/services/viewport-hold-service.js +75 -0
  97. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/excalidraw.css +1 -0
  98. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-Bold.woff2 +0 -0
  99. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-Medium.woff2 +0 -0
  100. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-Regular.woff2 +0 -0
  101. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-SemiBold.woff2 +0 -0
  102. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Cascadia/CascadiaCode-Regular.woff2 +0 -0
  103. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2 +0 -0
  104. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2 +0 -0
  105. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2 +0 -0
  106. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2 +0 -0
  107. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2 +0 -0
  108. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2 +0 -0
  109. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2 +0 -0
  110. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2 +0 -0
  111. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2 +0 -0
  112. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2 +0 -0
  113. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2 +0 -0
  114. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Lilita/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2 +0 -0
  115. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Lilita/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2 +0 -0
  116. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2 +0 -0
  117. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2 +0 -0
  118. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2 +0 -0
  119. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2 +0 -0
  120. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2 +0 -0
  121. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Virgil/Virgil-Regular.woff2 +0 -0
  122. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1.LICENSE.txt +3135 -0
  123. metalist-0.11.0/app/static/js/vendor/excalidraw-0.18.1.min.js +208 -0
  124. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/bulk_tag_proposals.py +9 -5
  125. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/delete_subtree.py +3 -1
  126. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/delete_tag.py +10 -7
  127. metalist-0.11.0/app/usecases/file_edit_session.py +110 -0
  128. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/rename_tag.py +6 -5
  129. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/resize_image.py +1 -1
  130. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/view.py +3 -3
  131. {metalist-0.9.0 → metalist-0.11.0}/app/utils/text_utils.py +3 -4
  132. {metalist-0.9.0 → metalist-0.11.0}/app/version.py +1 -1
  133. {metalist-0.9.0 → metalist-0.11.0}/metalist.egg-info/PKG-INFO +18 -1
  134. {metalist-0.9.0 → metalist-0.11.0}/metalist.egg-info/SOURCES.txt +39 -0
  135. metalist-0.9.0/app/api/routes/files.py +0 -95
  136. metalist-0.9.0/app/db/file_schema.py +0 -28
  137. metalist-0.9.0/app/security/sensitive_cache.py +0 -78
  138. metalist-0.9.0/app/services/view_cache.py +0 -122
  139. {metalist-0.9.0 → metalist-0.11.0}/MANIFEST.in +0 -0
  140. {metalist-0.9.0 → metalist-0.11.0}/app/__init__.py +0 -0
  141. {metalist-0.9.0 → metalist-0.11.0}/app/api/__init__.py +0 -0
  142. {metalist-0.9.0 → metalist-0.11.0}/app/api/deps.py +0 -0
  143. {metalist-0.9.0 → metalist-0.11.0}/app/api/dev.py +0 -0
  144. {metalist-0.9.0 → metalist-0.11.0}/app/api/request_auth.py +0 -0
  145. {metalist-0.9.0 → metalist-0.11.0}/app/api/routes/ai.py +0 -0
  146. {metalist-0.9.0 → metalist-0.11.0}/app/api/routes/app_updates.py +0 -0
  147. {metalist-0.9.0 → metalist-0.11.0}/app/api/routes/backups.py +0 -0
  148. {metalist-0.9.0 → metalist-0.11.0}/app/api/routes/reminders.py +0 -0
  149. {metalist-0.9.0 → metalist-0.11.0}/app/api/routes/remote_images.py +0 -0
  150. {metalist-0.9.0 → metalist-0.11.0}/app/api/routes/test.py +0 -0
  151. {metalist-0.9.0 → metalist-0.11.0}/app/api/transactions.py +0 -0
  152. {metalist-0.9.0 → metalist-0.11.0}/app/config.py +0 -0
  153. {metalist-0.9.0 → metalist-0.11.0}/app/data_directory.py +0 -0
  154. {metalist-0.9.0 → metalist-0.11.0}/app/db/__init__.py +0 -0
  155. {metalist-0.9.0 → metalist-0.11.0}/app/db/backup_settings_sql.py +0 -0
  156. {metalist-0.9.0 → metalist-0.11.0}/app/db/engine.py +0 -0
  157. {metalist-0.9.0 → metalist-0.11.0}/app/db/file_session.py +0 -0
  158. {metalist-0.9.0 → metalist-0.11.0}/app/db/link_titles_sql.py +0 -0
  159. {metalist-0.9.0 → metalist-0.11.0}/app/db/live_recovery.py +0 -0
  160. {metalist-0.9.0 → metalist-0.11.0}/app/db/ontology_rules_sql.py +0 -0
  161. {metalist-0.9.0 → metalist-0.11.0}/app/db/reminders_sql.py +0 -0
  162. {metalist-0.9.0 → metalist-0.11.0}/app/db/retire_sounds.py +0 -0
  163. {metalist-0.9.0 → metalist-0.11.0}/app/db/schema.py +0 -0
  164. {metalist-0.9.0 → metalist-0.11.0}/app/db/search_history_sql.py +0 -0
  165. {metalist-0.9.0 → metalist-0.11.0}/app/db/session.py +0 -0
  166. {metalist-0.9.0 → metalist-0.11.0}/app/db/settings_sql.py +0 -0
  167. {metalist-0.9.0 → metalist-0.11.0}/app/db/tab_state_sql.py +0 -0
  168. {metalist-0.9.0 → metalist-0.11.0}/app/exception_boundaries.py +0 -0
  169. {metalist-0.9.0 → metalist-0.11.0}/app/https_proxy.py +0 -0
  170. {metalist-0.9.0 → metalist-0.11.0}/app/models/commands.py +0 -0
  171. {metalist-0.9.0 → metalist-0.11.0}/app/models/database.py +0 -0
  172. {metalist-0.9.0 → metalist-0.11.0}/app/models/entities.py +0 -0
  173. {metalist-0.9.0 → metalist-0.11.0}/app/models/enums.py +0 -0
  174. {metalist-0.9.0 → metalist-0.11.0}/app/models/linked_list.py +0 -0
  175. {metalist-0.9.0 → metalist-0.11.0}/app/models/list_operations.py +0 -0
  176. {metalist-0.9.0 → metalist-0.11.0}/app/models/list_traversal.py +0 -0
  177. {metalist-0.9.0 → metalist-0.11.0}/app/models/note_crud.py +0 -0
  178. {metalist-0.9.0 → metalist-0.11.0}/app/models/utils.py +0 -0
  179. {metalist-0.9.0 → metalist-0.11.0}/app/network_settings.py +0 -0
  180. {metalist-0.9.0 → metalist-0.11.0}/app/presentation/__init__.py +0 -0
  181. {metalist-0.9.0 → metalist-0.11.0}/app/presentation/render/__init__.py +0 -0
  182. {metalist-0.9.0 → metalist-0.11.0}/app/presentation/render/note_renderer.py +0 -0
  183. {metalist-0.9.0 → metalist-0.11.0}/app/presentation/templates.py +0 -0
  184. {metalist-0.9.0 → metalist-0.11.0}/app/security/authenticated_logging.py +0 -0
  185. {metalist-0.9.0 → metalist-0.11.0}/app/security/encryption.py +0 -0
  186. {metalist-0.9.0 → metalist-0.11.0}/app/security/http_error_diagnostics.py +0 -0
  187. {metalist-0.9.0 → metalist-0.11.0}/app/security/http_headers.py +0 -0
  188. {metalist-0.9.0 → metalist-0.11.0}/app/security/note_html.py +0 -0
  189. {metalist-0.9.0 → metalist-0.11.0}/app/security/request_boundary.py +0 -0
  190. {metalist-0.9.0 → metalist-0.11.0}/app/security/request_hosts.py +0 -0
  191. {metalist-0.9.0 → metalist-0.11.0}/app/security/sensitive_logging.py +0 -0
  192. {metalist-0.9.0 → metalist-0.11.0}/app/security/shell_execution.py +0 -0
  193. {metalist-0.9.0 → metalist-0.11.0}/app/security/validation_errors.py +0 -0
  194. {metalist-0.9.0 → metalist-0.11.0}/app/server_runtime.py +0 -0
  195. {metalist-0.9.0 → metalist-0.11.0}/app/services/__init__.py +0 -0
  196. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/__init__.py +0 -0
  197. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/actions.py +0 -0
  198. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/cloud_privacy.py +0 -0
  199. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/context.py +0 -0
  200. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/evidence_serialization.py +0 -0
  201. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/history.py +0 -0
  202. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/inference.py +0 -0
  203. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/investigation.py +0 -0
  204. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/judging.py +0 -0
  205. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/menu_actions.py +0 -0
  206. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/model_policy.py +0 -0
  207. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/openai_cost_tracking.py +0 -0
  208. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/openai_inference.py +0 -0
  209. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/permissions.py +0 -0
  210. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/prompt_settings.py +0 -0
  211. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/prompts/__init__.py +0 -0
  212. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/prompts/final-response.md +0 -0
  213. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/prompts/help-response.md +0 -0
  214. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/prompts/system.md +0 -0
  215. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/prompts/tool-result.md +0 -0
  216. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/retrieval_settings.py +0 -0
  217. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/runtime.py +0 -0
  218. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/scope.py +0 -0
  219. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skill_settings.py +0 -0
  220. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/__init__.py +0 -0
  221. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/help-ai.md +0 -0
  222. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/help-data.md +0 -0
  223. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/help-privacy.md +0 -0
  224. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/help-reminders.md +0 -0
  225. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/help-search.md +0 -0
  226. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/help-tags.md +0 -0
  227. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/scoped-investigation.md +0 -0
  228. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/staged-summary.md +0 -0
  229. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/tag-proposals.md +0 -0
  230. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/skills/web-browsing.md +0 -0
  231. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/staged_summary.py +0 -0
  232. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/structured_inference.py +0 -0
  233. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/tagging.py +0 -0
  234. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/tagging_run.py +0 -0
  235. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/token_estimation.py +0 -0
  236. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/tools.py +0 -0
  237. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/trace.py +0 -0
  238. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/web_capabilities.py +0 -0
  239. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/web_evidence.py +0 -0
  240. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/web_fetch.py +0 -0
  241. {metalist-0.9.0 → metalist-0.11.0}/app/services/agent/web_settings.py +0 -0
  242. {metalist-0.9.0 → metalist-0.11.0}/app/services/ai_chat.py +0 -0
  243. {metalist-0.9.0 → metalist-0.11.0}/app/services/ai_chat_rendering.py +0 -0
  244. {metalist-0.9.0 → metalist-0.11.0}/app/services/ai_chat_stream.py +0 -0
  245. {metalist-0.9.0 → metalist-0.11.0}/app/services/app_update_worker.py +0 -0
  246. {metalist-0.9.0 → metalist-0.11.0}/app/services/app_updates.py +0 -0
  247. {metalist-0.9.0 → metalist-0.11.0}/app/services/auth.py +0 -0
  248. {metalist-0.9.0 → metalist-0.11.0}/app/services/auth_cache_state.py +0 -0
  249. {metalist-0.9.0 → metalist-0.11.0}/app/services/auth_service.py +0 -0
  250. {metalist-0.9.0 → metalist-0.11.0}/app/services/backlink_index.py +0 -0
  251. {metalist-0.9.0 → metalist-0.11.0}/app/services/backup_settings_service.py +0 -0
  252. {metalist-0.9.0 → metalist-0.11.0}/app/services/bulk_operation.py +0 -0
  253. {metalist-0.9.0 → metalist-0.11.0}/app/services/content_cache.py +0 -0
  254. {metalist-0.9.0 → metalist-0.11.0}/app/services/diagnostics.py +0 -0
  255. {metalist-0.9.0 → metalist-0.11.0}/app/services/exception_capture.py +0 -0
  256. {metalist-0.9.0 → metalist-0.11.0}/app/services/footnote_layout.py +0 -0
  257. {metalist-0.9.0 → metalist-0.11.0}/app/services/footnote_rendering.py +0 -0
  258. {metalist-0.9.0 → metalist-0.11.0}/app/services/hierarchy.py +0 -0
  259. {metalist-0.9.0 → metalist-0.11.0}/app/services/html_unformatting.py +0 -0
  260. {metalist-0.9.0 → metalist-0.11.0}/app/services/hydration_state.py +0 -0
  261. {metalist-0.9.0 → metalist-0.11.0}/app/services/inline_image_occurrences.py +0 -0
  262. {metalist-0.9.0 → metalist-0.11.0}/app/services/input_errors.py +0 -0
  263. {metalist-0.9.0 → metalist-0.11.0}/app/services/integrity.py +0 -0
  264. {metalist-0.9.0 → metalist-0.11.0}/app/services/latex_rendering.py +0 -0
  265. {metalist-0.9.0 → metalist-0.11.0}/app/services/login_rate_limit.py +0 -0
  266. {metalist-0.9.0 → metalist-0.11.0}/app/services/maintenance_mode.py +0 -0
  267. {metalist-0.9.0 → metalist-0.11.0}/app/services/markdown_rendering.py +0 -0
  268. {metalist-0.9.0 → metalist-0.11.0}/app/services/namespace_deletion_jobs.py +0 -0
  269. {metalist-0.9.0 → metalist-0.11.0}/app/services/namespace_deletion_worker.py +0 -0
  270. {metalist-0.9.0 → metalist-0.11.0}/app/services/namespace_rename_jobs.py +0 -0
  271. {metalist-0.9.0 → metalist-0.11.0}/app/services/namespace_rename_worker.py +0 -0
  272. {metalist-0.9.0 → metalist-0.11.0}/app/services/namespace_runtime_guard.py +0 -0
  273. {metalist-0.9.0 → metalist-0.11.0}/app/services/namespace_switcher.py +0 -0
  274. {metalist-0.9.0 → metalist-0.11.0}/app/services/note_fullscreen.py +0 -0
  275. {metalist-0.9.0 → metalist-0.11.0}/app/services/note_ordering.py +0 -0
  276. {metalist-0.9.0 → metalist-0.11.0}/app/services/ontology_rules_store.py +0 -0
  277. {metalist-0.9.0 → metalist-0.11.0}/app/services/openai_credentials.py +0 -0
  278. {metalist-0.9.0 → metalist-0.11.0}/app/services/password_note_fields.py +0 -0
  279. {metalist-0.9.0 → metalist-0.11.0}/app/services/public_http.py +0 -0
  280. {metalist-0.9.0 → metalist-0.11.0}/app/services/reference_presentation.py +0 -0
  281. {metalist-0.9.0 → metalist-0.11.0}/app/services/reminders.py +0 -0
  282. {metalist-0.9.0 → metalist-0.11.0}/app/services/remote_image_proxy.py +0 -0
  283. {metalist-0.9.0 → metalist-0.11.0}/app/services/resource_limits.py +0 -0
  284. {metalist-0.9.0 → metalist-0.11.0}/app/services/runtime_generation.py +0 -0
  285. {metalist-0.9.0 → metalist-0.11.0}/app/services/runtime_hardening.py +0 -0
  286. {metalist-0.9.0 → metalist-0.11.0}/app/services/runtime_lock.py +0 -0
  287. {metalist-0.9.0 → metalist-0.11.0}/app/services/search_history_storage.py +0 -0
  288. {metalist-0.9.0 → metalist-0.11.0}/app/services/search_query.py +0 -0
  289. {metalist-0.9.0 → metalist-0.11.0}/app/services/search_text.py +0 -0
  290. {metalist-0.9.0 → metalist-0.11.0}/app/services/selected_text_tag.py +0 -0
  291. {metalist-0.9.0 → metalist-0.11.0}/app/services/self_update.py +0 -0
  292. {metalist-0.9.0 → metalist-0.11.0}/app/services/session_timeout_service.py +0 -0
  293. {metalist-0.9.0 → metalist-0.11.0}/app/services/shell_session_service.py +0 -0
  294. {metalist-0.9.0 → metalist-0.11.0}/app/services/structured_note_renderers.py +0 -0
  295. {metalist-0.9.0 → metalist-0.11.0}/app/services/sync_state.py +0 -0
  296. {metalist-0.9.0 → metalist-0.11.0}/app/services/tag_case.py +0 -0
  297. {metalist-0.9.0 → metalist-0.11.0}/app/services/tag_ontology.py +0 -0
  298. {metalist-0.9.0 → metalist-0.11.0}/app/services/tag_rename.py +0 -0
  299. {metalist-0.9.0 → metalist-0.11.0}/app/services/test_reset.py +0 -0
  300. {metalist-0.9.0 → metalist-0.11.0}/app/services/tokens.py +0 -0
  301. {metalist-0.9.0 → metalist-0.11.0}/app/services/update_backups.py +0 -0
  302. {metalist-0.9.0 → metalist-0.11.0}/app/services/update_installer.py +0 -0
  303. {metalist-0.9.0 → metalist-0.11.0}/app/services/update_preflight.py +0 -0
  304. {metalist-0.9.0 → metalist-0.11.0}/app/services/view_diff.py +0 -0
  305. {metalist-0.9.0 → metalist-0.11.0}/app/services/view_state.py +0 -0
  306. {metalist-0.9.0 → metalist-0.11.0}/app/services/windows_process_control.py +0 -0
  307. {metalist-0.9.0 → metalist-0.11.0}/app/startup_environment.py +0 -0
  308. {metalist-0.9.0 → metalist-0.11.0}/app/startup_js_sanity.py +0 -0
  309. {metalist-0.9.0 → metalist-0.11.0}/app/startup_sanity.py +0 -0
  310. {metalist-0.9.0 → metalist-0.11.0}/app/startup_sanity_config.py +0 -0
  311. {metalist-0.9.0 → metalist-0.11.0}/app/static/css/floating-notes.css +0 -0
  312. {metalist-0.9.0 → metalist-0.11.0}/app/static/css/forms.css +0 -0
  313. {metalist-0.9.0 → metalist-0.11.0}/app/static/img/favicon.ico +0 -0
  314. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/locked.js +0 -0
  315. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/main.js +0 -0
  316. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/activity-tracker.js +0 -0
  317. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/agent-menu-actions.js +0 -0
  318. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/agent-prompt-service.js +0 -0
  319. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/agent-retrieval-settings.js +0 -0
  320. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/agent-web-settings.js +0 -0
  321. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/ai-agent-debug-view.js +0 -0
  322. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/ai-chat-api.js +0 -0
  323. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/ai-chat-message-placement.js +0 -0
  324. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/ai-chat-panel-controller.js +0 -0
  325. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/ai-chat-panel-service.js +0 -0
  326. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/ai-thinking-level-service.js +0 -0
  327. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/bulk-elapsed-clock.js +0 -0
  328. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/bulk-proposal-ui.js +0 -0
  329. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/bulk-scope-question.js +0 -0
  330. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/cloud-privacy-policy.js +0 -0
  331. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/proposal-menu.js +0 -0
  332. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/ai-chat/summary-batch-preview.js +0 -0
  333. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/api-error-diagnostics.js +0 -0
  334. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/api-failure-classification-service.js +0 -0
  335. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/app-update-service.js +0 -0
  336. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/application-state.js +0 -0
  337. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/async-result.js +0 -0
  338. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/auth.js +0 -0
  339. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/client-state-api.js +0 -0
  340. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/client-state-migration.js +0 -0
  341. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/command-palette/activation-auth-policy.js +0 -0
  342. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/command-palette/backup-command-availability.js +0 -0
  343. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
  344. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/command-palette/preferences-store.js +0 -0
  345. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/command-palette/usage-store.js +0 -0
  346. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/connectivity-monitor.js +0 -0
  347. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/context-menu/context-menu-target-service.js +0 -0
  348. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/context-menu/input-caret-service.js +0 -0
  349. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/disconnect-editing-state-service.js +0 -0
  350. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/dom-utils-fixed.js +0 -0
  351. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/dom-utils.js +0 -0
  352. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/editor-commands.js +0 -0
  353. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/elapsed-time.js +0 -0
  354. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/error-handler.js +0 -0
  355. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/error-overlay.js +0 -0
  356. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/location-flags.js +0 -0
  357. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/login-namespace-picker.js +0 -0
  358. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/maintenance.js +0 -0
  359. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/agent-prompt-editor-modal.js +0 -0
  360. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/ai-agent-settings-modal.js +0 -0
  361. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
  362. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/backup-restore-modal.js +0 -0
  363. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/backup-result-modal.js +0 -0
  364. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
  365. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/backup-settings-modal.js +0 -0
  366. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/base-modal.js +0 -0
  367. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/confirmation-modal.js +0 -0
  368. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/delete-namespace-modal.js +0 -0
  369. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/delete-namespace-validation.js +0 -0
  370. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/help-modal.js +0 -0
  371. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
  372. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/namespace-modals.js +0 -0
  373. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/namespace-ports-profile.js +0 -0
  374. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/namespace-rename-validation.js +0 -0
  375. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/ontology-modal.js +0 -0
  376. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/password-modal.js +0 -0
  377. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/prioritize-modal.js +0 -0
  378. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/random-password-modal.js +0 -0
  379. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/reminder-modal.js +0 -0
  380. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
  381. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/search-suggestion-statistics-modal.js +0 -0
  382. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/session-timeout-modal.js +0 -0
  383. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/modals/version-info-modal.js +0 -0
  384. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/actions/content-actions.js +0 -0
  385. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/actions/history-actions.js +0 -0
  386. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
  387. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/actions/selection-actions.js +0 -0
  388. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
  389. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/events/search-events.js +0 -0
  390. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
  391. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/add-style-service.js +0 -0
  392. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
  393. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/backlinks-navigation-service.js +0 -0
  394. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
  395. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
  396. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
  397. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/command-gate-service.js +0 -0
  398. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +0 -0
  399. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/deselect-selection-state-service.js +0 -0
  400. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
  401. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
  402. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
  403. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/file-drop-policy-service.js +0 -0
  404. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
  405. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
  406. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
  407. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/floating-note-dom.js +0 -0
  408. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/floating-note-layout.js +0 -0
  409. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/footnote-navigation-service.js +0 -0
  410. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/history-selection-policy-service.js +0 -0
  411. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/history-shortcut-policy-service.js +0 -0
  412. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +0 -0
  413. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
  414. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/link-title-prefetch-service.js +0 -0
  415. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
  416. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/mermaid-render-service.js +0 -0
  417. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
  418. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
  419. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
  420. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
  421. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/note-delete-shortcut-policy-service.js +0 -0
  422. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
  423. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
  424. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
  425. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/note-timestamp-hover-service.js +0 -0
  426. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
  427. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/reference-source-navigation-service.js +0 -0
  428. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/remote-image-proxy-service.js +0 -0
  429. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/remove-formatting-service.js +0 -0
  430. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +0 -0
  431. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
  432. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
  433. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
  434. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
  435. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/search-input-service.js +0 -0
  436. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -0
  437. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
  438. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/search-suggestion-statistics-service.js +0 -0
  439. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/search-suggestion-window-labels-service.js +0 -0
  440. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/search-suggestion-windows-service.js +0 -0
  441. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +0 -0
  442. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/search-syntax-service.js +0 -0
  443. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +0 -0
  444. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/selected-text-tag-service.js +0 -0
  445. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
  446. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
  447. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/tab-drag-service.js +0 -0
  448. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
  449. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/tag-bar-service.js +0 -0
  450. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/tag-input-selection-service.js +0 -0
  451. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/tag-proposal-service.js +0 -0
  452. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
  453. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/untagged-view-indicator-service.js +0 -0
  454. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
  455. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/note-html-sanitizer.js +0 -0
  456. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/password-generator.js +0 -0
  457. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/password-operation-response.js +0 -0
  458. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/password-policy.js +0 -0
  459. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/password-strength.js +0 -0
  460. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/reminder-store.js +0 -0
  461. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/reminder-surface-service.js +0 -0
  462. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/selection-markers.js +0 -0
  463. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/session-auth.js +0 -0
  464. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/tag-token.js +0 -0
  465. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/modules/uuid.js +0 -0
  466. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/namespace-deleted.js +0 -0
  467. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/namespace-renamed.js +0 -0
  468. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/vendor/dompurify-3.4.13.LICENSE.txt +0 -0
  469. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/vendor/dompurify-3.4.13.min.js +0 -0
  470. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/vendor/markdown-it-14.3.2.LICENSE.txt +0 -0
  471. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/vendor/markdown-it-14.3.2.min.js +0 -0
  472. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/vendor/mermaid-11.16.1.LICENSE.txt +0 -0
  473. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/vendor/mermaid-11.16.1.min.js +0 -0
  474. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/vendor/zxcvbn-4.4.2.LICENSE.txt +0 -0
  475. {metalist-0.9.0 → metalist-0.11.0}/app/static/js/vendor/zxcvbn-4.4.2.js +0 -0
  476. {metalist-0.9.0 → metalist-0.11.0}/app/static/note-html-policy.json +0 -0
  477. {metalist-0.9.0 → metalist-0.11.0}/app/static/video/login-startup.mp4 +0 -0
  478. {metalist-0.9.0 → metalist-0.11.0}/app/templates/base.html +0 -0
  479. {metalist-0.9.0 → metalist-0.11.0}/app/templates/index.html +0 -0
  480. {metalist-0.9.0 → metalist-0.11.0}/app/templates/locked.html +0 -0
  481. {metalist-0.9.0 → metalist-0.11.0}/app/templates/maintenance.html +0 -0
  482. {metalist-0.9.0 → metalist-0.11.0}/app/templates/namespace_deleted.html +0 -0
  483. {metalist-0.9.0 → metalist-0.11.0}/app/templates/namespace_renamed.html +0 -0
  484. {metalist-0.9.0 → metalist-0.11.0}/app/templates/notes_list.html +0 -0
  485. {metalist-0.9.0 → metalist-0.11.0}/app/upload_limits.py +0 -0
  486. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/__init__.py +0 -0
  487. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/add_selected_text_tag.py +0 -0
  488. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/alphabetize_root_notes.py +0 -0
  489. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/base.py +0 -0
  490. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/collapse.py +0 -0
  491. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/copy_note.py +0 -0
  492. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/create_child.py +0 -0
  493. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/create_note.py +0 -0
  494. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/create_sibling.py +0 -0
  495. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/expand.py +0 -0
  496. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/indent.py +0 -0
  497. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/lock.py +0 -0
  498. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/move.py +0 -0
  499. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/move_to_top.py +0 -0
  500. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/outdent.py +0 -0
  501. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/paste_child.py +0 -0
  502. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/paste_sibling.py +0 -0
  503. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/prefetch_link_titles.py +0 -0
  504. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/prioritize.py +0 -0
  505. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/redo.py +0 -0
  506. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/reset_updated_at.py +0 -0
  507. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/run_shell.py +0 -0
  508. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/search_comment_autofill.py +0 -0
  509. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/search_context_tags.py +0 -0
  510. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/set_collapse_bulk.py +0 -0
  511. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/set_collapse_in_context.py +0 -0
  512. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/split_note.py +0 -0
  513. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/tag_proposals.py +0 -0
  514. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/toggle_reference_mode.py +0 -0
  515. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/toggle_todo_done.py +0 -0
  516. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/undo.py +0 -0
  517. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/unformat_content.py +0 -0
  518. {metalist-0.9.0 → metalist-0.11.0}/app/usecases/update_content.py +0 -0
  519. {metalist-0.9.0 → metalist-0.11.0}/app/utils/__init__.py +0 -0
  520. {metalist-0.9.0 → metalist-0.11.0}/app/utils/encryption.py +0 -0
  521. {metalist-0.9.0 → metalist-0.11.0}/convert-from-legacy.py +0 -0
  522. {metalist-0.9.0 → metalist-0.11.0}/main.py +0 -0
  523. {metalist-0.9.0 → metalist-0.11.0}/metalist.egg-info/dependency_links.txt +0 -0
  524. {metalist-0.9.0 → metalist-0.11.0}/metalist.egg-info/entry_points.txt +0 -0
  525. {metalist-0.9.0 → metalist-0.11.0}/metalist.egg-info/requires.txt +0 -0
  526. {metalist-0.9.0 → metalist-0.11.0}/metalist.egg-info/top_level.txt +0 -0
  527. {metalist-0.9.0 → metalist-0.11.0}/pyproject.toml +0 -0
  528. {metalist-0.9.0 → metalist-0.11.0}/scripts/generate-lan-cert.sh +0 -0
  529. {metalist-0.9.0 → metalist-0.11.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metalist
3
- Version: 0.9.0
3
+ Version: 0.11.0
4
4
  Summary: A minimalist single-user note-taking app with SSR, fast in-memory tree operations, and efficient sync.
5
5
  Project-URL: Homepage, https://github.com/evolvingstuff/metalist
6
6
  Project-URL: Repository, https://github.com/evolvingstuff/metalist
@@ -49,6 +49,23 @@ A minimalist single-user note-taking app focused on server-side rendering (SSR),
49
49
  - Multi-tab search contexts with server-persisted scroll/search state (survives browser restarts)
50
50
  - Manual namespace backups/restores to a user-selected backup folder with retention controls
51
51
 
52
+ ## Changes in 0.11.0
53
+
54
+ - Large namespaces stay fast while scrolling: only a band of root notes around the viewport is loaded, the server remembers what each tab shows so the browser no longer sends note hashes, and rendered notes are cached.
55
+ - Tag suggestions, searches with only exclusions (`-tag`), and the updated/content-volume sort orders respond much faster on large namespaces.
56
+ - Accepting or removing tag proposals in bulk, renaming or deleting a tag, and undoing a large delete no longer rebuild every note's inherited tags.
57
+ - Unlocking an encrypted namespace is faster, and the startup encryption audit no longer loads every attachment into memory.
58
+ - Fixed undo of deleting a note whose children have several children, and "accept all" failing on tag bars with leading or trailing spaces.
59
+ - Fixed crashes in the status check after returning to a tab and in scroll-position saving.
60
+
61
+ ## Changes in 0.10.0
62
+
63
+ - Excalidraw diagrams: add one from the note's right-click menu or the command palette, and double-click a diagram to edit it full screen. Diagrams autosave, show light and dark previews, resize like images, and each editing session is one undo step. The editor is bundled locally and needs no Node server.
64
+ - The dark theme uses neutral greys with a brighter accent, the light theme header is darker, and Note Layout & Appearance adds note corner and tag style settings.
65
+ - References to a note that starts with an image or diagram show its thumbnail. Collapsed notes, reference titles, and backlink previews show only the first line.
66
+ - Fixed a command palette crash on queries with no matches.
67
+ - The database schema moves to version 10. Password-protected namespaces migrate after unlock, with the usual automatic backup first.
68
+
52
69
  ## Changes in 0.9.0
53
70
 
54
71
  - AI chat can browse the web in disabled, contextual, or unrestricted mode, fetch multiple pages concurrently, and cite the specific pages used as evidence.
@@ -12,6 +12,23 @@ A minimalist single-user note-taking app focused on server-side rendering (SSR),
12
12
  - Multi-tab search contexts with server-persisted scroll/search state (survives browser restarts)
13
13
  - Manual namespace backups/restores to a user-selected backup folder with retention controls
14
14
 
15
+ ## Changes in 0.11.0
16
+
17
+ - Large namespaces stay fast while scrolling: only a band of root notes around the viewport is loaded, the server remembers what each tab shows so the browser no longer sends note hashes, and rendered notes are cached.
18
+ - Tag suggestions, searches with only exclusions (`-tag`), and the updated/content-volume sort orders respond much faster on large namespaces.
19
+ - Accepting or removing tag proposals in bulk, renaming or deleting a tag, and undoing a large delete no longer rebuild every note's inherited tags.
20
+ - Unlocking an encrypted namespace is faster, and the startup encryption audit no longer loads every attachment into memory.
21
+ - Fixed undo of deleting a note whose children have several children, and "accept all" failing on tag bars with leading or trailing spaces.
22
+ - Fixed crashes in the status check after returning to a tab and in scroll-position saving.
23
+
24
+ ## Changes in 0.10.0
25
+
26
+ - Excalidraw diagrams: add one from the note's right-click menu or the command palette, and double-click a diagram to edit it full screen. Diagrams autosave, show light and dark previews, resize like images, and each editing session is one undo step. The editor is bundled locally and needs no Node server.
27
+ - The dark theme uses neutral greys with a brighter accent, the light theme header is darker, and Note Layout & Appearance adds note corner and tag style settings.
28
+ - References to a note that starts with an image or diagram show its thumbnail. Collapsed notes, reference titles, and backlink previews show only the first line.
29
+ - Fixed a command palette crash on queries with no matches.
30
+ - The database schema moves to version 10. Password-protected namespaces migrate after unlock, with the usual automatic backup first.
31
+
15
32
  ## Changes in 0.9.0
16
33
 
17
34
  - AI chat can browse the web in disabled, contextual, or unrestricted mode, fetch multiple pages concurrently, and cite the specific pages used as evidence.
@@ -37,11 +37,6 @@ class AuthMiddleware(BaseHTTPMiddleware):
37
37
  )
38
38
  assert all(path.endswith("/") for path in PUBLIC_PREFIX_PATHS)
39
39
 
40
- # Paths to suppress verbose logging for (frequent polling endpoints)
41
- QUIET_PATHS = [
42
- f"{API_PREFIX}/auth/status",
43
- ]
44
-
45
40
  # Background/automated paths that should NOT refresh tokens (not user activity)
46
41
  NO_TOKEN_REFRESH_PATHS = [
47
42
  f"{API_PREFIX}/auth/status", # Polling service pings this for connectivity
@@ -75,20 +70,9 @@ class AuthMiddleware(BaseHTTPMiddleware):
75
70
  # Redirect all other requests to maintenance page
76
71
  return RedirectResponse(url="/maintenance", status_code=302)
77
72
 
78
- # Check if this is a quiet path (suppress verbose logging)
79
- is_quiet = any(path.startswith(quiet) for quiet in self.QUIET_PATHS)
80
-
81
- if not is_quiet:
82
- print(f"Middleware checking path: {path}")
83
-
84
73
  if self.is_public_path(path=path):
85
- if not is_quiet:
86
- print(f"Path {path} is explicitly public, skipping auth")
87
74
  return await call_next(request)
88
75
 
89
- if not is_quiet:
90
- print(f"Path {path} is NOT public, checking auth")
91
-
92
76
  # Startup and recoverable password transitions publish this memory-owned flag.
93
77
  if path == f"{API_PREFIX}/auth/settings/password/create" and not is_encryption_required():
94
78
  return await call_next(request)
@@ -56,8 +56,9 @@ class ViewDiffRequest(TypedDict):
56
56
  search: Text | None
57
57
  tabId: Identifier
58
58
  undoContext: UndoContext
59
- clientNoteUuidHashes: dict[Identifier, Text]
60
- visibleRootAnchorId: Text | None
59
+ tabViewEmpty: bool
60
+ visibleTopRootId: Text | None
61
+ visibleBottomRootId: Text | None
61
62
  isUntaggedView: bool
62
63
 
63
64
  @with_config(ConfigDict(strict=True))
@@ -76,6 +77,7 @@ class UpdateTabSortModeRequest(TypedDict):
76
77
  @with_config(ConfigDict(strict=True))
77
78
  class CreateNewTabRequest(TypedDict):
78
79
  copyFromTabId: Identifier
80
+ clientId: Identifier
79
81
 
80
82
  @with_config(ConfigDict(strict=True))
81
83
  class DeleteTabRequest(TypedDict):
@@ -206,6 +208,19 @@ class ResizeNoteImageRequest(TypedDict):
206
208
  undoContext: UndoContext
207
209
  viewport: Viewport
208
210
 
211
+ @with_config(ConfigDict(strict=True))
212
+ class FinishFileEditSessionRequest(TypedDict):
213
+ clientId: Identifier
214
+ hostNoteId: Identifier
215
+ contentRevision: Annotated[int, Field(ge=1)]
216
+ isNewDiagram: bool
217
+ undoContext: UndoContext
218
+ viewport: Viewport
219
+
220
+ @with_config(ConfigDict(strict=True))
221
+ class DiscardFileEditSessionRequest(TypedDict):
222
+ contentRevision: Annotated[int, Field(ge=1)]
223
+
209
224
  @with_config(ConfigDict(strict=True))
210
225
  class RunShellEndpointRequest(TypedDict):
211
226
  timeoutSeconds: Annotated[int, Field(ge=0, le=SHELL_SECONDS)]
@@ -713,7 +713,8 @@ def auth_status(
713
713
  "version": VERSION,
714
714
  "database_user_version": database_user_version,
715
715
  "authenticated": token is not None,
716
- "has_password": auth.has_password(),
716
+ # Same answer as AuthService.has_password(), from the row already read.
717
+ "has_password": settings is not None and bool(settings.encryption_enabled),
717
718
  "encryption_enabled": settings.encryption_enabled if settings else False,
718
719
  "encryption_algorithm": settings.encryption_algorithm if settings else None,
719
720
  "vault_version": settings.vault_version if settings else None,
@@ -0,0 +1,244 @@
1
+ from __future__ import annotations
2
+
3
+ from io import BytesIO
4
+ from typing import Annotated
5
+ from urllib.parse import quote
6
+
7
+ from fastapi import APIRouter, File, Form, HTTPException, Request, UploadFile
8
+ from fastapi.responses import Response, StreamingResponse
9
+ from pydantic import BaseModel
10
+
11
+ from app.api.note_requests import DiscardFileEditSessionRequest, FinishFileEditSessionRequest
12
+ from app.api.transactions import transactional_route
13
+ from app.api.upload_limits import read_attachment
14
+ from app.api.request_auth import require_request_auth_token
15
+ from app.services.file_registry import file_registry
16
+ from app.services.note_store import store as note_store
17
+ from app.db.file_schema import FILE_PREVIEW_VARIANTS
18
+ from app.services.file_storage import (
19
+ create_file,
20
+ download_file,
21
+ get_file_preview,
22
+ replace_file_content,
23
+ store_file_previews,
24
+ trim_unused_files,
25
+ )
26
+ from app.usecases.file_edit_session import (
27
+ CmdDiscardFileEditSession,
28
+ CmdFinishFileEditSession,
29
+ CmdStartFileEditSession,
30
+ )
31
+
32
+ router = APIRouter(prefix="/files", tags=["files"])
33
+
34
+ FILE_REVISION_HEADER = "X-MetaList-File-Revision"
35
+
36
+
37
+ class UploadedFileResponse(BaseModel):
38
+ file_id: str
39
+ reference_token: str
40
+ title: str
41
+ original_filename: str
42
+ mime_type: str
43
+ size_bytes: int
44
+ thumbnail_kind: str
45
+
46
+
47
+ class FileContentUpdatedResponse(BaseModel):
48
+ file_id: str
49
+ content_revision: int
50
+ size_bytes: int
51
+
52
+
53
+ class FilePreviewsStoredResponse(BaseModel):
54
+ file_id: str
55
+ content_revision: int
56
+
57
+
58
+ class TrimUnusedFilesResponse(BaseModel):
59
+ deleted_count: int
60
+ deleted_file_ids: list[str]
61
+
62
+
63
+ def _require_bearer_token(request: Request) -> str:
64
+ return require_request_auth_token(request)
65
+
66
+
67
+ def _require_known_file(file_id: str) -> None:
68
+ if not file_registry.has_file(file_id):
69
+ raise HTTPException(status_code=404, detail=f"File not found: {file_id}")
70
+
71
+
72
+ @router.post("/upload", response_model=UploadedFileResponse)
73
+ @transactional_route
74
+ async def upload_file_endpoint(
75
+ request: Request,
76
+ file: Annotated[UploadFile, File()],
77
+ ):
78
+ token = _require_bearer_token(request)
79
+ if not isinstance(file.filename, str) or file.filename == "":
80
+ raise HTTPException(status_code=400, detail="Uploaded file must include a filename")
81
+ if not isinstance(file.content_type, str) or file.content_type == "":
82
+ raise HTTPException(status_code=400, detail="Uploaded file must include a non-empty MIME type")
83
+
84
+ content_bytes = await read_attachment(file)
85
+
86
+ record = create_file(
87
+ original_filename=file.filename,
88
+ mime_type=file.content_type,
89
+ content_bytes=content_bytes,
90
+ token=token,
91
+ )
92
+ return UploadedFileResponse(
93
+ file_id=record.id,
94
+ reference_token=f"![[{record.id}]]",
95
+ title=record.title,
96
+ original_filename=record.original_filename,
97
+ mime_type=record.mime_type,
98
+ size_bytes=record.size_bytes,
99
+ thumbnail_kind=record.thumbnail_kind,
100
+ )
101
+
102
+
103
+ @router.get("/{file_id}/download")
104
+ def download_file_endpoint(request: Request, file_id: str):
105
+ token = _require_bearer_token(request)
106
+ if not file_registry.has_file(file_id):
107
+ raise HTTPException(status_code=404, detail=f"File not found: {file_id}")
108
+
109
+ downloaded = download_file(file_id, token)
110
+ quoted_filename = quote(downloaded.record.original_filename, safe="")
111
+ return StreamingResponse(
112
+ BytesIO(downloaded.content_bytes),
113
+ media_type=downloaded.record.mime_type,
114
+ headers={
115
+ "Content-Disposition": f"attachment; filename*=UTF-8''{quoted_filename}",
116
+ FILE_REVISION_HEADER: str(downloaded.record.content_revision),
117
+ },
118
+ )
119
+
120
+
121
+ @router.put("/{file_id}/content", response_model=FileContentUpdatedResponse)
122
+ @transactional_route
123
+ async def replace_file_content_endpoint(
124
+ request: Request,
125
+ file_id: str,
126
+ file: Annotated[UploadFile, File()],
127
+ expected_revision: Annotated[int, Form()],
128
+ ):
129
+ token = _require_bearer_token(request)
130
+ _require_known_file(file_id)
131
+ content_bytes = await read_attachment(file)
132
+ record = replace_file_content(
133
+ file_id=file_id,
134
+ content_bytes=content_bytes,
135
+ expected_revision=expected_revision,
136
+ token=token,
137
+ )
138
+ return FileContentUpdatedResponse(
139
+ file_id=record.id,
140
+ content_revision=record.content_revision,
141
+ size_bytes=record.size_bytes,
142
+ )
143
+
144
+
145
+ @router.put("/{file_id}/previews", response_model=FilePreviewsStoredResponse)
146
+ @transactional_route
147
+ async def store_file_previews_endpoint(
148
+ request: Request,
149
+ file_id: str,
150
+ light: Annotated[UploadFile, File()],
151
+ dark: Annotated[UploadFile, File()],
152
+ content_revision: Annotated[int, Form()],
153
+ ):
154
+ token = _require_bearer_token(request)
155
+ _require_known_file(file_id)
156
+ previews_by_variant = {
157
+ "light": await read_attachment(light),
158
+ "dark": await read_attachment(dark),
159
+ }
160
+ record = store_file_previews(
161
+ file_id=file_id,
162
+ previews_by_variant=previews_by_variant,
163
+ content_revision=content_revision,
164
+ token=token,
165
+ )
166
+ return FilePreviewsStoredResponse(file_id=record.id, content_revision=record.content_revision)
167
+
168
+
169
+ @router.get("/{file_id}/previews/{variant}")
170
+ def download_file_preview_endpoint(request: Request, file_id: str, variant: str):
171
+ token = _require_bearer_token(request)
172
+ _require_known_file(file_id)
173
+ if variant not in FILE_PREVIEW_VARIANTS:
174
+ raise HTTPException(status_code=404, detail=f"Unknown preview variant: {variant}")
175
+ preview = get_file_preview(file_id=file_id, variant=variant, token=token)
176
+ return Response(
177
+ content=preview.content_bytes,
178
+ media_type=preview.mime_type,
179
+ headers={FILE_REVISION_HEADER: str(preview.content_revision)},
180
+ )
181
+
182
+
183
+ # A diagram editing session: opened with the editor, finished by Done (one undo step), or discarded.
184
+ @router.post("/{file_id}/edit-sessions")
185
+ @transactional_route
186
+ def start_file_edit_session_endpoint(request: Request, file_id: str) -> dict[str, object]:
187
+ token = _require_bearer_token(request)
188
+ _require_known_file(file_id)
189
+ return CmdStartFileEditSession(file_id=file_id, token=token).execute()
190
+
191
+
192
+ @router.post("/{file_id}/edit-sessions/{session_id}/finish")
193
+ @transactional_route
194
+ def finish_file_edit_session_endpoint(
195
+ request: Request,
196
+ file_id: str,
197
+ session_id: str,
198
+ body: FinishFileEditSessionRequest,
199
+ ) -> dict[str, object]:
200
+ token = _require_bearer_token(request)
201
+ _require_known_file(file_id)
202
+ host_note_id = body["hostNoteId"]
203
+ if not note_store.has_note(host_note_id):
204
+ raise HTTPException(status_code=404, detail=f"Note not found: {host_note_id}")
205
+ return CmdFinishFileEditSession(
206
+ file_id=file_id,
207
+ session_id=session_id,
208
+ host_note_id=host_note_id,
209
+ content_revision=body["contentRevision"],
210
+ is_new_diagram=body["isNewDiagram"],
211
+ token=token,
212
+ client_id=body["clientId"],
213
+ undo_context=body["undoContext"],
214
+ viewport=body["viewport"],
215
+ ).execute()
216
+
217
+
218
+ @router.post("/{file_id}/edit-sessions/{session_id}/discard")
219
+ @transactional_route
220
+ def discard_file_edit_session_endpoint(
221
+ request: Request,
222
+ file_id: str,
223
+ session_id: str,
224
+ body: DiscardFileEditSessionRequest,
225
+ ) -> dict[str, object]:
226
+ token = _require_bearer_token(request)
227
+ _require_known_file(file_id)
228
+ return CmdDiscardFileEditSession(
229
+ file_id=file_id,
230
+ session_id=session_id,
231
+ content_revision=body["contentRevision"],
232
+ token=token,
233
+ ).execute()
234
+
235
+
236
+ @router.post("/trim-unused", response_model=TrimUnusedFilesResponse)
237
+ @transactional_route
238
+ def trim_unused_files_endpoint(request: Request):
239
+ _require_bearer_token(request)
240
+ result = trim_unused_files()
241
+ return TrimUnusedFilesResponse(
242
+ deleted_count=result.deleted_count,
243
+ deleted_file_ids=result.deleted_file_ids,
244
+ )
@@ -241,16 +241,16 @@ def view_diff(payload: ViewDiffRequest):
241
241
  search = payload["search"]
242
242
  tab_id = payload["tabId"]
243
243
  undo_context = payload["undoContext"]
244
- client_note_uuid_hashes = payload["clientNoteUuidHashes"]
245
- anchor_root_id = payload["visibleRootAnchorId"]
244
+ tab_view_empty = payload["tabViewEmpty"]
245
+ visible_top_root_id = payload["visibleTopRootId"]
246
+ visible_bottom_root_id = payload["visibleBottomRootId"]
246
247
  is_untagged_view = payload["isUntaggedView"]
247
248
  if not isinstance(is_untagged_view, bool):
248
249
  raise TypeError("isUntaggedView must be a boolean")
250
+ if not isinstance(tab_view_empty, bool):
251
+ raise TypeError("tabViewEmpty must be a boolean")
249
252
 
250
253
  sort_mode = _resolve_tab_sort_mode(tab_id)
251
- if not isinstance(client_note_uuid_hashes, dict):
252
- raise TypeError("clientNoteUuidHashes must be an object")
253
-
254
254
  normalized_search = search
255
255
  if isinstance(normalized_search, str) and normalized_search == "":
256
256
  normalized_search = None
@@ -278,164 +278,58 @@ def view_diff(payload: ViewDiffRequest):
278
278
  if not note_store.has_note(normalized_editing_note_id):
279
279
  normalized_editing_note_id = None
280
280
 
281
- # Known hashes plus a viewport anchor so the server can extend the window
282
- client_hashes = {
283
- k: v for k, v in client_note_uuid_hashes.items() if k
284
- }
285
- # Fallback: if client didn't provide an anchor, use the last known root from cached state
286
- cache_key = {
287
- "client_id": client_id,
288
- "tab_id": tab_id,
289
- "search": normalized_search,
290
- "sort_mode": sort_mode,
291
- "is_untagged_view": is_untagged_view,
292
- }
293
- cached_state = view_cache.get(**cache_key)
294
- if not anchor_root_id and cached_state and client_hashes:
295
- last_roots = list(cached_state.children_by_parent.get(None, []))
296
- if last_roots:
297
- anchor_root_id = last_roots[-1]
281
+ # The server keeps each tab's warm view (what that browser tab displays),
282
+ # so the browser sends only where it is. A tab with no warm view, or one
283
+ # whose browser shows nothing for it, gets its initial window.
284
+ warm_view = view_cache.get(client_id=client_id, tab_id=tab_id)
285
+ if tab_view_empty:
286
+ warm_view = None
287
+ client_known_note_ids: set[str] = set()
288
+ if warm_view is not None:
289
+ client_known_note_ids = set(warm_view.hash_by_id)
298
290
 
299
291
  state = build_view_state(
300
292
  editing_note_id=normalized_editing_note_id,
301
293
  search=normalized_search,
302
294
  sort_mode=sort_mode,
303
- client_known_note_ids=set(client_hashes.keys()),
304
- client_seen_root_ids=set(),
305
- anchor_root_id=anchor_root_id,
295
+ client_known_note_ids=client_known_note_ids,
296
+ visible_top_root_id=visible_top_root_id,
297
+ visible_bottom_root_id=visible_bottom_root_id,
306
298
  is_untagged_view=is_untagged_view,
307
299
  )
308
300
  update_uuid = get_current_sync_uuid()
309
301
  root_ids = list(state.children_by_parent.get(None, []))
310
- root_count_total = state.metadata["rootCountTotal"]
311
- search_root_count_total = state.metadata["searchRootCountTotal"]
312
-
313
- client_note_ids = set(client_hashes.keys())
314
- current_note_ids = set(state.hash_by_id.keys())
315
- if cached_state is None:
316
- cached_note_ids = set()
317
- else:
318
- cached_note_ids = set(cached_state.hash_by_id.keys())
319
- extra_client_ids = _unknown_client_note_ids(
320
- client_note_ids=client_note_ids,
321
- current_note_ids=current_note_ids,
322
- cached_note_ids=cached_note_ids,
323
- )
324
- force_full_snapshot = bool(extra_client_ids)
325
- if force_full_snapshot:
326
- logger.info(
327
- "notes.view forcing full snapshot (client has unknown ids): extra_count=%s",
328
- len(extra_client_ids),
329
- )
330
-
331
- client_has_state = bool(client_hashes)
332
-
333
- if not cached_state or force_full_snapshot:
334
- view_cache.set(state=state, **cache_key)
335
- if force_full_snapshot:
336
- filtered_notes = dict(state.payloads)
337
- else:
338
- filtered_notes = {
339
- note_id: data
340
- for note_id, data in state.payloads.items()
341
- if client_hashes.get(note_id) != data.get("hash")
342
- }
343
-
344
- # Optimization: when the server cache is cold but the client already has a
345
- # complete, matching hash map for the visible window, avoid resending the
346
- # full structure + note payloads. This is common when a new tab is created
347
- # by cloning the existing DOM.
348
- if client_has_state and not filtered_notes and not force_full_snapshot:
349
- response_snapshot = {
350
- "diffOps": [],
351
- "notes": {},
352
- "locks": state.locks,
353
- "rootIds": root_ids,
354
- "lockDiffs": {},
355
- "updateUUID": update_uuid,
356
- "version": VERSION,
357
- "currentClientId": client_id,
358
- "searchQuery": search,
359
- "sortMode": sort_mode,
360
- "isUntaggedView": is_untagged_view,
361
- "rootCountTotal": root_count_total,
362
- "searchRootCountTotal": search_root_count_total,
363
- "rootSortBuckets": state.metadata["rootSortBuckets"],
364
- "editingNoteId": normalized_editing_note_id,
365
- }
366
- return {"snapshot": response_snapshot, "updateUUID": update_uuid}
367
-
368
- response_snapshot = {
369
- "structure": state.structure,
370
- "notes": filtered_notes,
371
- "locks": state.locks,
372
- "rootIds": root_ids,
373
- "updateUUID": update_uuid,
374
- "version": VERSION,
375
- "currentClientId": client_id,
376
- "searchQuery": search,
377
- "sortMode": sort_mode,
378
- "isUntaggedView": is_untagged_view,
379
- "rootCountTotal": root_count_total,
380
- "searchRootCountTotal": search_root_count_total,
381
- "rootSortBuckets": state.metadata["rootSortBuckets"],
382
- "editingNoteId": normalized_editing_note_id,
383
- }
384
- return {"snapshot": response_snapshot, "updateUUID": update_uuid}
385
-
386
- if not client_has_state:
387
- view_cache.set(state=state, **cache_key)
388
- filtered_notes = {
389
- note_id: data
390
- for note_id, data in state.payloads.items()
391
- if client_hashes.get(note_id) != data.get("hash")
392
- }
393
- response_snapshot = {
394
- "structure": state.structure,
395
- "notes": filtered_notes,
396
- "locks": state.locks,
397
- "rootIds": root_ids,
398
- "updateUUID": update_uuid,
399
- "version": VERSION,
400
- "currentClientId": client_id,
401
- "searchQuery": search,
402
- "sortMode": sort_mode,
403
- "isUntaggedView": is_untagged_view,
404
- "rootCountTotal": root_count_total,
405
- "searchRootCountTotal": search_root_count_total,
406
- "rootSortBuckets": state.metadata["rootSortBuckets"],
407
- "editingNoteId": normalized_editing_note_id,
408
- }
409
- return {"snapshot": response_snapshot, "updateUUID": update_uuid}
410
-
411
- diff_ops = generate_diff_ops(cached_state, state)
412
- note_updates = {
413
- note_id: payload
414
- for note_id, payload in state.payloads.items()
415
- if cached_state.hash_by_id.get(note_id) != payload["hash"]
416
- }
417
-
418
- view_cache.set(state=state, **cache_key)
419
-
420
- lock_diff = _compute_lock_diff(cached_state.locks, state.locks)
421
-
302
+ view_cache.set(client_id=client_id, tab_id=tab_id, state=state)
422
303
  response_snapshot = {
423
- "diffOps": diff_ops,
424
- "notes": note_updates,
304
+ "notes": {},
425
305
  "locks": state.locks,
426
306
  "rootIds": root_ids,
427
- "lockDiffs": lock_diff,
428
307
  "updateUUID": update_uuid,
429
308
  "version": VERSION,
430
309
  "currentClientId": client_id,
431
310
  "searchQuery": search,
432
311
  "sortMode": sort_mode,
433
312
  "isUntaggedView": is_untagged_view,
434
- "rootCountTotal": root_count_total,
435
- "searchRootCountTotal": search_root_count_total,
313
+ "rootCountTotal": state.metadata["rootCountTotal"],
314
+ "searchRootCountTotal": state.metadata["searchRootCountTotal"],
436
315
  "rootSortBuckets": state.metadata["rootSortBuckets"],
316
+ "rootWindowStart": state.metadata["rootWindowStart"],
317
+ "rootBandMargin": state.metadata["rootBandMargin"],
318
+ "rootSortKeyBeforeWindow": state.metadata["rootSortKeyBeforeWindow"],
437
319
  "editingNoteId": normalized_editing_note_id,
438
320
  }
321
+ if warm_view is None:
322
+ response_snapshot["structure"] = state.structure
323
+ response_snapshot["notes"] = dict(state.payloads)
324
+ return {"snapshot": response_snapshot, "updateUUID": update_uuid}
325
+
326
+ response_snapshot["diffOps"] = generate_diff_ops(warm_view, state)
327
+ response_snapshot["notes"] = {
328
+ note_id: note_payload
329
+ for note_id, note_payload in state.payloads.items()
330
+ if note_id not in warm_view.hash_by_id or warm_view.hash_by_id[note_id] != note_payload["hash"]
331
+ }
332
+ response_snapshot["lockDiffs"] = _compute_lock_diff(warm_view.locks, state.locks)
439
333
  return {"snapshot": response_snapshot, "updateUUID": update_uuid}
440
334
 
441
335
 
@@ -501,6 +395,11 @@ def create_new_tab(payload: CreateNewTabRequest) -> Dict[str, object]:
501
395
  response = tab_state_store.create_tab(copy_from_tab_id=copy_from_tab_id)
502
396
  if capture.captured_exception is not None:
503
397
  raise HTTPException(status_code=400, detail="Invalid or stale tab state") from capture.captured_exception
398
+ # The browser clones the source tab's DOM into the new tab, so the new tab
399
+ # starts from the source tab's warm view.
400
+ view_cache.copy_tab(
401
+ client_id=payload["clientId"], source_tab_id=copy_from_tab_id, target_tab_id=response["newTabId"],
402
+ )
504
403
  return response
505
404
 
506
405
 
@@ -790,16 +689,6 @@ def _compute_lock_diff(previous: Dict[str, str], current: Dict[str, str]) -> Dic
790
689
  return diff
791
690
 
792
691
 
793
- def _unknown_client_note_ids(
794
- *,
795
- client_note_ids: set[str],
796
- current_note_ids: set[str],
797
- cached_note_ids: set[str],
798
- ) -> set[str]:
799
- stale_deleted_ids = client_note_ids - current_note_ids
800
- return stale_deleted_ids - cached_note_ids
801
-
802
-
803
692
  # Stub endpoints for the rest of the notes API (501 Not Implemented)
804
693
 
805
694
  def _not_impl(exc: Exception) -> None:
@@ -11,7 +11,6 @@ from app.api.transactions import transactional_route
11
11
  from app.services.search_index import search_index
12
12
  from app.services.note_store import store as note_store
13
13
  from app.services.sync import generate_new_uuid
14
- from app.services.view_cache import view_cache
15
14
  from app.services.tag_ontology import RegexAtom, TagAtom, TextAtom, parse_rules_text
16
15
  from app.services.ontology_rules_store import (
17
16
  build_direct_edge_rule_map,
@@ -155,7 +154,6 @@ def create_rule(request: Request, payload: RuleTextRequest) -> dict:
155
154
  rule_id, normalized = create_rule_line(text=text, token=token)
156
155
  if note_store.loaded:
157
156
  note_store.rebuild_search_index_tag_terms_for_notes(candidate_note_ids)
158
- view_cache.clear()
159
157
  update_uuid = generate_new_uuid()
160
158
  return {"id": rule_id, "text": normalized, "updateUUID": update_uuid}
161
159
  return {"id": rule_id, "text": normalized}
@@ -193,7 +191,6 @@ def update_rule(request: Request, rule_id: int, payload: RuleTextRequest) -> dic
193
191
  updated_id, normalized = update_rule_line(rule_id=rule_id, text=text, token=token)
194
192
  if note_store.loaded:
195
193
  note_store.rebuild_search_index_tag_terms_for_notes(candidate_note_ids)
196
- view_cache.clear()
197
194
  update_uuid = generate_new_uuid()
198
195
  return {"id": updated_id, "text": normalized, "updateUUID": update_uuid}
199
196
  return {"id": updated_id, "text": normalized}
@@ -224,7 +221,6 @@ def delete_rule(rule_id: int) -> dict:
224
221
  delete_rule_line(rule_id=rule_id)
225
222
  if note_store.loaded:
226
223
  note_store.rebuild_search_index_tag_terms_for_notes(candidate_note_ids)
227
- view_cache.clear()
228
224
  update_uuid = generate_new_uuid()
229
225
  return {"ok": True, "updateUUID": update_uuid}
230
226
  return {"ok": True}