MetaList 0.19.0__tar.gz → 0.20.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.
- {metalist-0.19.0/metalist.egg-info → metalist-0.20.0}/PKG-INFO +12 -1
- {metalist-0.19.0 → metalist-0.20.0}/README.md +11 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/note_requests.py +30 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/routes/ai.py +275 -21
- {metalist-0.19.0 → metalist-0.20.0}/app/api/routes/notes.py +127 -1
- {metalist-0.19.0 → metalist-0.20.0}/app/exception_boundaries.py +1 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/presentation/render/note_renderer.py +1 -1
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/cloud_privacy.py +52 -19
- metalist-0.20.0/app/services/agent/export_title_suggestions.py +187 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/failure_explanations.py +1 -0
- metalist-0.20.0/app/services/agent/note_tag_suggestions.py +318 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/scope.py +4 -2
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-ai.md +32 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-data.md +9 -2
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-formatting.md +5 -4
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-menus.md +2 -1
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-notes.md +3 -2
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-privacy.md +6 -4
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-releases.md +11 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-tags.md +5 -3
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/structured_inference.py +2 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/tagging_run.py +43 -25
- {metalist-0.19.0 → metalist-0.20.0}/app/services/ai_chat_rendering.py +2 -2
- metalist-0.20.0/app/services/ai_setup.py +84 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/content_formatting.py +40 -20
- {metalist-0.19.0 → metalist-0.20.0}/app/services/diagnostics.py +7 -1
- {metalist-0.19.0 → metalist-0.20.0}/app/services/embedded_references.py +51 -49
- metalist-0.20.0/app/services/html_export.py +652 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/note_fullscreen.py +1 -1
- {metalist-0.19.0 → metalist-0.20.0}/app/services/note_store.py +21 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/search_index.py +36 -14
- {metalist-0.19.0 → metalist-0.20.0}/app/services/snapshot.py +23 -20
- {metalist-0.19.0 → metalist-0.20.0}/app/services/undo_state.py +88 -1
- {metalist-0.19.0 → metalist-0.20.0}/app/services/windows_process_control.py +6 -2
- {metalist-0.19.0 → metalist-0.20.0}/app/startup_sanity_config.py +2 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/config/agent-menu-actions.json +6 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/config/command_palette_tags.json +4 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/css/main.css +260 -33
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-chat-api.js +157 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/api-client.js +127 -14
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/command-palette/command-palette-controller.js +12 -65
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/command-palette/endpoint-registry.js +7 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/config.js +8 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/context-menu/context-menu-registry.js +72 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/context-menu/context-menu-service.js +24 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/context-menu/input-caret-service.js +15 -3
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/error-handler.js +3 -1
- metalist-0.20.0/app/static/js/modules/modals/export-options-modal.js +314 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/content-actions.js +3 -1
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/note-actions.js +2 -1
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/ui-actions.js +15 -1
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/context-menu-events.js +402 -260
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/mouse-events.js +65 -4
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/command-gate-service.js +12 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/differential-view-service.js +10 -0
- metalist-0.20.0/app/static/js/modules/mode-manager/services/export-service.js +64 -0
- metalist-0.20.0/app/static/js/modules/mode-manager/services/export-title-service.js +59 -0
- metalist-0.20.0/app/static/js/modules/mode-manager/services/print-service.js +82 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tag-bar-service.js +34 -1
- metalist-0.20.0/app/static/js/modules/mode-manager/services/tag-proposal-service.js +359 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/viewport-hold-service.js +72 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/prefetch_link_titles.py +1 -1
- metalist-0.20.0/app/usecases/tag_proposals.py +390 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/version.py +1 -1
- {metalist-0.19.0 → metalist-0.20.0}/main.py +24 -1
- {metalist-0.19.0 → metalist-0.20.0/metalist.egg-info}/PKG-INFO +12 -1
- {metalist-0.19.0 → metalist-0.20.0}/metalist.egg-info/SOURCES.txt +7 -0
- metalist-0.19.0/app/services/html_export.py +0 -327
- metalist-0.19.0/app/static/js/modules/mode-manager/services/tag-proposal-service.js +0 -141
- metalist-0.19.0/app/usecases/tag_proposals.py +0 -173
- {metalist-0.19.0 → metalist-0.20.0}/MANIFEST.in +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/deps.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/dev.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/middleware/auth.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/request_auth.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/routes/app_updates.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/routes/auth.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/routes/backups.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/routes/files.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/routes/ontology.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/routes/reminders.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/routes/remote_images.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/routes/test.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/transactions.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/api/upload_limits.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/config.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/data_directory.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/backup_settings_sql.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/editable_files_migration.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/engine.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/file_schema.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/file_session.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/files_sql.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/link_titles_sql.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/live_recovery.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/migrations.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/notes_sql.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/ontology_rules_sql.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/reminders_sql.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/retire_sounds.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/schema.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/search_history_sql.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/session.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/settings_sql.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/tab_state_sql.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/db/version.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/encryption_audit.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/https_proxy.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/main.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/models/commands.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/models/database.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/models/entities.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/models/enums.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/models/linked_list.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/models/list_operations.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/models/list_traversal.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/models/note_crud.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/models/utils.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/network_settings.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/presentation/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/presentation/login_page.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/presentation/render/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/presentation/templates.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/authenticated_logging.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/encryption.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/http_error_diagnostics.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/http_headers.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/note_html.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/request_boundary.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/request_hosts.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/sensitive_cache.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/sensitive_logging.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/shell_execution.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/security/validation_errors.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/server_runtime.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/action_log.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/agent_loop.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/agent_tools.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/citation_tokens.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/context.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/evidence_serialization.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/execution_errors.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/help_catalog.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/history.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/inference.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/investigation.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/judging.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/menu_actions.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/model_policy.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/note_aliases.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/openai_cost_tracking.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/openai_inference.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/prompt_settings.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/prompts/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/prompts/agent.md +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/prompts/final-response.md +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/prompts/system.md +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/release_notes.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/retrieval_settings.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/runtime.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skill_settings.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-dictation.md +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-references.md +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-reminders.md +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/help-search.md +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/staged-summary.md +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/tag-proposals.md +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/skills/web-browsing.md +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/staged_summary.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/strict_structured_outputs.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/summary_batch_store.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/tagging.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/token_estimation.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/tool_calling.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/trace.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/web_actions.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/web_capabilities.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/web_evidence.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/web_fetch.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/agent/web_settings.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/ai_chat.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/ai_chat_stream.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/app_update_worker.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/app_updates.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/auth.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/auth_cache_state.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/auth_service.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/backlink_index.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/backlinks.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/backup_service.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/backup_settings_service.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/bulk_operation.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/chess_rendering.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/client_state_service.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/code_rendering.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/content_cache.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/dictation_cleanup.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/diff_rendering.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/encryption.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/exception_capture.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/file_edit_sessions.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/file_registry.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/file_storage.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/file_text_preview.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/footnote_layout.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/footnote_rendering.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/hierarchy.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/html_unformatting.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/hydration_state.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/image_size_formatting.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/inline_image_dimensions.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/inline_image_occurrences.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/input_errors.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/integrity.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/latex_rendering.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/link_title_retry.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/link_titles.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/login_rate_limit.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/maintenance_mode.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/markdown_rendering.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/mermaid_note_rendering.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/namespace_deletion_jobs.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/namespace_deletion_worker.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/namespace_rename_jobs.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/namespace_rename_worker.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/namespace_runtime_guard.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/namespace_switcher.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/note_image_tags.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/note_ordering.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/ontology_rules_store.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/openai_credentials.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/password_note_fields.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/public_http.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/reference_presentation.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/reminders.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/remote_image_proxy.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/request_recovery.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/resource_limits.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/root_sorting.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/runtime_generation.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/runtime_hardening.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/runtime_lock.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/search_history.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/search_history_storage.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/search_query.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/search_text.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/selected_text_tag.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/self_update.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/session_timeout_service.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/shell_session_service.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/store.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/structured_note_renderers.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/sync.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/sync_state.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/tab_state.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/tag_case.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/tag_details.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/tag_ontology.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/tag_rename.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/tag_suggestion_reasons.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/tag_suggestions.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/tag_term_matching.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/test_reset.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/tokens.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/update_backups.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/update_installer.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/update_preflight.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/view_cache.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/view_diff.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/services/view_state.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/startup_console.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/startup_environment.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/startup_js_sanity.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/startup_sanity.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/css/floating-notes.css +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/css/forms.css +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/img/favicon.ico +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/locked.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/main.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/activity-tracker.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/agent-menu-actions.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/agent-retrieval-settings.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/agent-web-settings.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-agent-debug-view.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-chat-message-placement.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-chat-panel-controller.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-chat-panel-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-thinking-level-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/bulk-elapsed-clock.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/bulk-proposal-ui.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/bulk-scope-question.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/cloud-privacy-policy.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/confirmation-card.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/confirmation-question.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/proposal-menu.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/summary-batch-preview.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/api-error-diagnostics.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/api-failure-classification-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/app-update-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/application-state.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/async-result.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/auth.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/client-state-api.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/client-state-migration.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/command-palette/activation-auth-policy.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/command-palette/backup-command-availability.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/command-palette/note-layout-preferences.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/command-palette/preferences-store.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/command-palette/tag-config-loader.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/command-palette/usage-store.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/connectivity-monitor.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/context-menu/context-menu-target-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/disconnect-editing-state-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/dom-utils-fixed.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/dom-utils.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/editor-commands.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/editor-selection.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/editor-toolbar.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/elapsed-time.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/error-overlay.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-actions.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-autosave.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-editor-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-editor-state.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-loader.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-preview-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-scene.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/expected-errors.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/location-flags.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/login-namespace-picker.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/maintenance.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/ai-agent-settings-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/backup-restore-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/backup-result-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/backup-settings-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/base-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/confirmation-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/delete-namespace-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/delete-namespace-validation.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/dictation-settings-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/help-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/link-title-retry-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/namespace-modals.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/namespace-ports-profile.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/namespace-rename-validation.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/note-layout-appearance-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/ontology-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/password-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/prioritize-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/random-password-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/reminder-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/search-suggestion-statistics-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/session-timeout-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/tag-suggestion-settings-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/version-info-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/modals/visual-cues-modal.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/history-actions.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/selection-actions.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/focus-events.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/input-events.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/keyboard-events.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/search-events.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/mode-context.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/mode-manager-controller.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/add-style-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/backlinks-navigation-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/deselect-selection-state-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/dictation-note-paste.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/dictation-paste-padding.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/dictation-paste-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/dropped-file-preview-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/file-drop-policy-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/floating-note-dom.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/floating-note-layout.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/floating-note-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/footnote-navigation-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/history-selection-policy-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/history-shortcut-policy-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/link-title-prefetch-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/mermaid-render-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-delete-shortcut-policy-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-drag-indicator-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-fullscreen-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-timestamp-hover-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/paste-format-detection-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/paste-format-guess-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/paste-format-offer-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/polling-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/reference-source-navigation-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/remote-image-proxy-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/remove-formatting-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/root-note-creation-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/root-sort-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-input-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-suggestion-statistics-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-suggestion-window-labels-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-suggestion-windows-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-syntax-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/selected-text-tag-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tab-drag-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tab-state-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tag-input-selection-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/untagged-view-indicator-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/note-html-sanitizer.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/password-generator.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/password-operation-response.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/password-policy.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/password-strength.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/reminder-store.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/reminder-surface-service.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/selection-markers.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/session-auth.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/tag-token.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/modules/uuid.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/namespace-deleted.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/namespace-renamed.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/dompurify-3.4.16.LICENSE.txt +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/dompurify-3.4.16.min.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/excalidraw.css +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-Bold.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-Medium.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-Regular.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-SemiBold.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Cascadia/CascadiaCode-Regular.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Lilita/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Lilita/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Virgil/Virgil-Regular.woff2 +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1.LICENSE.txt +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1.min.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/markdown-it-14.3.2.LICENSE.txt +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/markdown-it-14.3.2.min.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/mermaid-11.16.1.LICENSE.txt +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/mermaid-11.16.1.min.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/zxcvbn-4.4.2.LICENSE.txt +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/js/vendor/zxcvbn-4.4.2.js +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/static/note-html-policy.json +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/templates/_logo_mark.html +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/templates/base.html +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/templates/index.html +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/templates/locked.html +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/templates/maintenance.html +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/templates/namespace_deleted.html +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/templates/namespace_renamed.html +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/templates/notes_list.html +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/upload_limits.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/add_selected_text_tag.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/alphabetize_root_notes.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/base.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/bulk_tag_proposals.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/collapse.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/copy_note.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/create_child.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/create_file_preview_child.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/create_note.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/create_sibling.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/delete_subtree.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/delete_tag.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/expand.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/file_edit_session.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/indent.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/lock.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/move.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/move_to_top.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/outdent.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/paste_child.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/paste_sibling.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/prioritize.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/redo.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/rename_tag.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/reset_updated_at.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/resize_image.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/run_shell.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/search_comment_autofill.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/search_context_tags.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/set_collapse_bulk.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/set_collapse_in_context.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/split_note.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/toggle_reference_mode.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/toggle_todo_done.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/undo.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/unformat_content.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/update_content.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/usecases/view.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/utils/__init__.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/utils/encryption.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/app/utils/text_utils.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/convert-from-legacy.py +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/metalist.egg-info/dependency_links.txt +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/metalist.egg-info/entry_points.txt +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/metalist.egg-info/requires.txt +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/metalist.egg-info/top_level.txt +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/pyproject.toml +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/scripts/generate-lan-cert.sh +0 -0
- {metalist-0.19.0 → metalist-0.20.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metalist
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.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
|
|
@@ -51,6 +51,17 @@ A minimalist single-user note-taking app focused on server-side rendering (SSR),
|
|
|
51
51
|
- Multi-tab search contexts with server-persisted scroll/search state (survives browser restarts)
|
|
52
52
|
- Manual namespace backups/restores to a user-selected backup folder with retention controls
|
|
53
53
|
|
|
54
|
+
## Changes in 0.20.0
|
|
55
|
+
|
|
56
|
+
- **Suggest Tags with AI** for the note being edited: right-click the note, or its tag bar past the tags, and choose 🤖 Suggest Tags with AI (for a note with children: This Note, or This Note and Its Children). The proposals appear under the tag bar for `+`/`−`, tags not yet in use marked **new** and grouped on the right; adding them can be undone. It shows only when an AI is set up and may see the note.
|
|
57
|
+
- The robot count (🤖 N) next to a note: click it to open every collapsed note on the way to its proposals and edit the highest one that has some; right-click it to Accept All or Reject All, one undo step.
|
|
58
|
+
- **Print**: the note menu's Print ▸ Note / View, the background menu's Print View, or the palette's Print view, through the browser's print dialog (Save as PDF included). Always light, fully expanded, without arrows, using the page's full width with its own margins, so the browser prints no header or footer.
|
|
59
|
+
- Export as HTML and Print open one dialog: a title with a smart default (the note's first line, or MetaList with the view's search), which heads the page unless the first note already says it and names an exported file; 🤖 Suggest with AI for the title, asked only when you press it, which never sees private notes, usernames, passwords or the search; and, when some would show, an Include Unredacted Passwords / Usernames checkbox (unchecked: each shows `••••••••`, whatever its length).
|
|
60
|
+
- Exported HTML files show the view as it is shown (its search, sort order, Untagged view; notes a search leaves out are absent), start fully expanded with MetaList's collapse arrows working in the file, and copy usernames, included passwords and `@copyable` values on click. Collapsing no longer shifts the page sideways.
|
|
61
|
+
- `@username` notes are private like `@password`: never sent to the AI. Username, password and email rows show just their icon, named on hover, and the value.
|
|
62
|
+
- Text never runs out of its note: a word too long for the line (a token, a hash, a long URL) breaks at the note's edge, and code, `@json`, `@diff`, CSV and Markdown code blocks wrap. Images shrink to fit their note or embedded note, and `@size` scales them only up to that width.
|
|
63
|
+
- The light theme's header bar is black again, with white icons and count, and the AI chat header now lines up with the search bar.
|
|
64
|
+
|
|
54
65
|
## Changes in 0.19.0
|
|
55
66
|
|
|
56
67
|
- Paste formats: pasting a fenced code block that names its language tags the note `@markdown`, and `@markdown` now colours that code like `@code=<language>` (with its label). Code copied from VS Code is tagged `@code=<its language>`, and cells copied from Excel, Google Sheets or Numbers become CSV text tagged `@csv`.
|
|
@@ -12,6 +12,17 @@ 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.20.0
|
|
16
|
+
|
|
17
|
+
- **Suggest Tags with AI** for the note being edited: right-click the note, or its tag bar past the tags, and choose 🤖 Suggest Tags with AI (for a note with children: This Note, or This Note and Its Children). The proposals appear under the tag bar for `+`/`−`, tags not yet in use marked **new** and grouped on the right; adding them can be undone. It shows only when an AI is set up and may see the note.
|
|
18
|
+
- The robot count (🤖 N) next to a note: click it to open every collapsed note on the way to its proposals and edit the highest one that has some; right-click it to Accept All or Reject All, one undo step.
|
|
19
|
+
- **Print**: the note menu's Print ▸ Note / View, the background menu's Print View, or the palette's Print view, through the browser's print dialog (Save as PDF included). Always light, fully expanded, without arrows, using the page's full width with its own margins, so the browser prints no header or footer.
|
|
20
|
+
- Export as HTML and Print open one dialog: a title with a smart default (the note's first line, or MetaList with the view's search), which heads the page unless the first note already says it and names an exported file; 🤖 Suggest with AI for the title, asked only when you press it, which never sees private notes, usernames, passwords or the search; and, when some would show, an Include Unredacted Passwords / Usernames checkbox (unchecked: each shows `••••••••`, whatever its length).
|
|
21
|
+
- Exported HTML files show the view as it is shown (its search, sort order, Untagged view; notes a search leaves out are absent), start fully expanded with MetaList's collapse arrows working in the file, and copy usernames, included passwords and `@copyable` values on click. Collapsing no longer shifts the page sideways.
|
|
22
|
+
- `@username` notes are private like `@password`: never sent to the AI. Username, password and email rows show just their icon, named on hover, and the value.
|
|
23
|
+
- Text never runs out of its note: a word too long for the line (a token, a hash, a long URL) breaks at the note's edge, and code, `@json`, `@diff`, CSV and Markdown code blocks wrap. Images shrink to fit their note or embedded note, and `@size` scales them only up to that width.
|
|
24
|
+
- The light theme's header bar is black again, with white icons and count, and the AI chat header now lines up with the search bar.
|
|
25
|
+
|
|
15
26
|
## Changes in 0.19.0
|
|
16
27
|
|
|
17
28
|
- Paste formats: pasting a fenced code block that names its language tags the note `@markdown`, and `@markdown` now colours that code like `@code=<language>` (with its label). Code copied from VS Code is tagged `@code=<its language>`, and cells copied from Excel, Google Sheets or Numbers become CSV text tagged `@csv`.
|
|
@@ -203,6 +203,36 @@ class AcceptTagProposalRequest(TypedDict):
|
|
|
203
203
|
undoContext: UndoContext
|
|
204
204
|
viewport: Viewport
|
|
205
205
|
|
|
206
|
+
@with_config(ConfigDict(strict=True))
|
|
207
|
+
class AddTagProposalsRequest(TypedDict):
|
|
208
|
+
# note id -> the AI's tags for it: the edited note and maybe notes below it.
|
|
209
|
+
proposals: Annotated[dict[Identifier, Annotated[list[Identifier], Field(min_length=1, max_length=12)]],
|
|
210
|
+
Field(min_length=1, max_length=20_000)]
|
|
211
|
+
clientId: Identifier
|
|
212
|
+
undoContext: UndoContext
|
|
213
|
+
viewport: Viewport
|
|
214
|
+
|
|
215
|
+
# The view a robot count (🤖 N) was shown in: its tab, search ('' for none) and kind.
|
|
216
|
+
@with_config(ConfigDict(strict=True))
|
|
217
|
+
class OpenToTagProposalsRequest(TypedDict):
|
|
218
|
+
search: Text
|
|
219
|
+
tabId: Identifier
|
|
220
|
+
isUntaggedView: bool
|
|
221
|
+
clientId: Identifier
|
|
222
|
+
undoContext: UndoContext
|
|
223
|
+
viewport: Viewport
|
|
224
|
+
|
|
225
|
+
@with_config(ConfigDict(strict=True))
|
|
226
|
+
class ResolveRobotProposalsRequest(TypedDict):
|
|
227
|
+
action: Literal["accept", "reject"]
|
|
228
|
+
includesHiddenChildren: bool
|
|
229
|
+
search: Text
|
|
230
|
+
tabId: Identifier
|
|
231
|
+
isUntaggedView: bool
|
|
232
|
+
clientId: Identifier
|
|
233
|
+
undoContext: UndoContext
|
|
234
|
+
viewport: Viewport
|
|
235
|
+
|
|
206
236
|
@with_config(ConfigDict(strict=True))
|
|
207
237
|
class RejectTagProposalRequest(TypedDict):
|
|
208
238
|
proposal: Identifier
|
|
@@ -18,6 +18,10 @@ from app.api.transactions import transactional_route
|
|
|
18
18
|
from app.models.utils import note_data_to_html
|
|
19
19
|
from app.models.utils import render_note_data_read_only
|
|
20
20
|
from app.services.ai_chat_stream import ChatTurnStream
|
|
21
|
+
from app.services.ai_setup import AiConnection
|
|
22
|
+
from app.services.ai_setup import AiNotSetUp
|
|
23
|
+
from app.services.ai_setup import AiProvider
|
|
24
|
+
from app.services.ai_setup import connect_to_ai
|
|
21
25
|
from app.services.ai_chat import ai_chat_store
|
|
22
26
|
from app.services.ai_chat_rendering import find_note_citation_ids
|
|
23
27
|
from app.services.ai_chat_rendering import find_web_citation_ids
|
|
@@ -27,9 +31,7 @@ from app.services.agent.cloud_privacy import cloud_privacy_evaluator
|
|
|
27
31
|
from app.services.agent.cloud_privacy import resolve_cloud_privacy_boundary
|
|
28
32
|
from app.services.agent.inference import InferenceAdapter
|
|
29
33
|
from app.services.agent.model_policy import SingleModelPolicy
|
|
30
|
-
from app.services.agent.openai_inference import OPENAI_API_BASE_URL
|
|
31
34
|
from app.services.agent.openai_inference import OPENAI_MODELS
|
|
32
|
-
from app.services.agent.openai_inference import OpenAIInferenceAdapter
|
|
33
35
|
from app.services.agent.openai_inference import validate_openai_model
|
|
34
36
|
from app.services.agent.openai_cost_tracking import OpenAICostSnapshot
|
|
35
37
|
from app.services.agent.openai_cost_tracking import openai_cost_tracker
|
|
@@ -64,6 +66,19 @@ from app.usecases.bulk_tag_proposals import apply_bulk_proposals, prepare_propos
|
|
|
64
66
|
from app.services.bulk_operation import bulk_operation_guard
|
|
65
67
|
from app.services.tab_state import tab_state_store
|
|
66
68
|
from app.services.tokens import token_service
|
|
69
|
+
from app.services.agent.inference import InferenceProviderError
|
|
70
|
+
from app.services.agent.note_tag_suggestions import build_note_tag_request
|
|
71
|
+
from app.services.agent.export_title_suggestions import MAX_PREVIOUS_TITLES
|
|
72
|
+
from app.services.agent.export_title_suggestions import ExportTitleRequest
|
|
73
|
+
from app.services.agent.export_title_suggestions import build_export_title_request
|
|
74
|
+
from app.services.agent.export_title_suggestions import suggest_export_title
|
|
75
|
+
from app.services.agent.note_tag_suggestions import NoteTagRequest
|
|
76
|
+
from app.services.agent.note_tag_suggestions import suggest_note_tags
|
|
77
|
+
from app.services.snapshot import resolve_view_scope_membership
|
|
78
|
+
from app.services.html_export import resolve_export_scope
|
|
79
|
+
from app.services.ai_setup import describe_ai_setup
|
|
80
|
+
from app.services.input_errors import InputRejected
|
|
81
|
+
from app.utils.text_utils import strip_html
|
|
67
82
|
|
|
68
83
|
|
|
69
84
|
router = APIRouter(prefix="/ai", tags=["ai"])
|
|
@@ -71,6 +86,15 @@ router = APIRouter(prefix="/ai", tags=["ai"])
|
|
|
71
86
|
agent_context_builder = AgentContextBuilder()
|
|
72
87
|
|
|
73
88
|
|
|
89
|
+
def _require_ai_connection(*, provider: AiProvider, token: str, session_key: str) -> AiConnection:
|
|
90
|
+
# lint: allow-PY001 rationale="the user removed or never added the AI's key: an expected 409, not a defect"
|
|
91
|
+
try:
|
|
92
|
+
return connect_to_ai(provider=provider, token=token, session_key=session_key)
|
|
93
|
+
# lint: allow-PY001 rationale="the user removed or never added the AI's key: an expected 409, not a defect"
|
|
94
|
+
except AiNotSetUp as exc:
|
|
95
|
+
raise HTTPException(status_code=409, detail="The AI is not set up: add its API key in AI settings") from exc
|
|
96
|
+
|
|
97
|
+
|
|
74
98
|
def _agent_runtime(*, inference: InferenceAdapter) -> AgentRuntime:
|
|
75
99
|
return AgentRuntime(
|
|
76
100
|
context_builder=agent_context_builder,
|
|
@@ -163,6 +187,76 @@ class AiChatRequest(BaseModel):
|
|
|
163
187
|
return self
|
|
164
188
|
|
|
165
189
|
|
|
190
|
+
class NoteTagSuggestionAvailabilityRequest(BaseModel):
|
|
191
|
+
model_config = ConfigDict(extra="forbid")
|
|
192
|
+
|
|
193
|
+
provider: AiProvider
|
|
194
|
+
# The note as it is being edited, unsaved changes included.
|
|
195
|
+
tags: str = Field(..., max_length=1024 * 1024)
|
|
196
|
+
content_html: str = Field(..., max_length=16 * 1024 * 1024)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class NoteTagSuggestionAvailabilityResponse(BaseModel):
|
|
200
|
+
is_available: bool
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class NoteTagSuggestionRequest(BaseModel):
|
|
204
|
+
model_config = ConfigDict(extra="forbid")
|
|
205
|
+
|
|
206
|
+
provider: AiProvider
|
|
207
|
+
model: str
|
|
208
|
+
thinking_level: Literal["off", "low", "medium", "high"]
|
|
209
|
+
# The note alone, or it and the notes below it the view shows.
|
|
210
|
+
include_children: bool
|
|
211
|
+
# The view the note is in ('' for no search).
|
|
212
|
+
search: str = Field(..., max_length=1024 * 1024)
|
|
213
|
+
tab_id: str = Field(..., min_length=1, max_length=256)
|
|
214
|
+
is_untagged_view: bool
|
|
215
|
+
|
|
216
|
+
@model_validator(mode="after")
|
|
217
|
+
def validate_model_for_provider(self) -> Self:
|
|
218
|
+
assert self.provider == "openai"
|
|
219
|
+
validate_openai_model(self.model)
|
|
220
|
+
return self
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class ExportTitleScope(BaseModel):
|
|
224
|
+
model_config = ConfigDict(extra="forbid")
|
|
225
|
+
|
|
226
|
+
provider: AiProvider
|
|
227
|
+
# The view exported or printed, or the one its note is in ('' for no search).
|
|
228
|
+
search: str = Field(..., max_length=1024 * 1024)
|
|
229
|
+
tab_id: str = Field(..., min_length=1, max_length=256)
|
|
230
|
+
is_untagged_view: bool
|
|
231
|
+
# A note with its children ("note"), or the view as it is shown ("view", note_id '').
|
|
232
|
+
exported: Literal["note", "view"]
|
|
233
|
+
note_id: str = Field(..., max_length=256)
|
|
234
|
+
|
|
235
|
+
@model_validator(mode="after")
|
|
236
|
+
def validate_exported_note(self) -> Self:
|
|
237
|
+
if (self.exported == "note") != (self.note_id != ""):
|
|
238
|
+
raise ValueError("A note's export names the note; the view's export names none")
|
|
239
|
+
return self
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class ExportTitleAvailabilityResponse(BaseModel):
|
|
243
|
+
# "unavailable": no AI is set up (no button); then as ExportTitleAvailability.
|
|
244
|
+
availability: Literal["unavailable", "nothing", "private", "partial", "ready"]
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class ExportTitleSuggestionRequest(ExportTitleScope):
|
|
248
|
+
model: str
|
|
249
|
+
thinking_level: Literal["off", "low", "medium", "high"]
|
|
250
|
+
# Titles the AI gave in this dialog: it gives another.
|
|
251
|
+
previous_titles: list[str] = Field(..., max_length=MAX_PREVIOUS_TITLES)
|
|
252
|
+
|
|
253
|
+
@model_validator(mode="after")
|
|
254
|
+
def validate_model_for_provider(self) -> Self:
|
|
255
|
+
assert self.provider == "openai"
|
|
256
|
+
validate_openai_model(self.model)
|
|
257
|
+
return self
|
|
258
|
+
|
|
259
|
+
|
|
166
260
|
class AiChatActivity(BaseModel):
|
|
167
261
|
sequence: int = Field(..., ge=1)
|
|
168
262
|
action: str
|
|
@@ -330,6 +424,178 @@ def delete_openai_credential(
|
|
|
330
424
|
)
|
|
331
425
|
|
|
332
426
|
|
|
427
|
+
@router.post(
|
|
428
|
+
"/notes/{note_id}/tag-suggestions/availability",
|
|
429
|
+
response_model=NoteTagSuggestionAvailabilityResponse,
|
|
430
|
+
)
|
|
431
|
+
@transactional_route
|
|
432
|
+
def note_tag_suggestion_availability(
|
|
433
|
+
note_id: str,
|
|
434
|
+
payload: NoteTagSuggestionAvailabilityRequest,
|
|
435
|
+
response: Response,
|
|
436
|
+
token: Annotated[str, Depends(require_request_auth_token)],
|
|
437
|
+
) -> NoteTagSuggestionAvailabilityResponse:
|
|
438
|
+
"""Whether "Suggest Tags with AI" applies: an AI is set up and may see the note as edited."""
|
|
439
|
+
response.headers["Cache-Control"] = "no-store"
|
|
440
|
+
if not note_store.has_note(note_id):
|
|
441
|
+
raise HTTPException(status_code=404, detail=f"Note not found: {note_id}")
|
|
442
|
+
session_key = token_service.get_session_key(token)
|
|
443
|
+
if not describe_ai_setup(token=token, session_key=session_key).is_ready_for(payload.provider):
|
|
444
|
+
return NoteTagSuggestionAvailabilityResponse(is_available=False)
|
|
445
|
+
boundary = resolve_cloud_privacy_boundary(
|
|
446
|
+
preferences=load_client_preferences(token=token),
|
|
447
|
+
provider=payload.provider,
|
|
448
|
+
)
|
|
449
|
+
draft_effective_tags = note_store.draft_effective_tag_terms(
|
|
450
|
+
note_id=note_id, tags=payload.tags, plaintext=strip_html(payload.content_html),
|
|
451
|
+
)
|
|
452
|
+
reason = cloud_privacy_evaluator.draft_exclusion_reason(
|
|
453
|
+
note_id=note_id, draft_effective_tags=draft_effective_tags,
|
|
454
|
+
draft_content_html=payload.content_html, boundary=boundary,
|
|
455
|
+
)
|
|
456
|
+
return NoteTagSuggestionAvailabilityResponse(is_available=reason == "")
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
@router.post("/notes/{note_id}/tag-suggestions")
|
|
460
|
+
@transactional_route
|
|
461
|
+
def suggest_tags_for_note_with_ai(
|
|
462
|
+
note_id: str,
|
|
463
|
+
payload: NoteTagSuggestionRequest,
|
|
464
|
+
token: Annotated[str, Depends(require_request_auth_token)],
|
|
465
|
+
) -> StreamingResponse:
|
|
466
|
+
"""Ask the AI for the saved note's tags (and its children's): streams {"type": "progress"}
|
|
467
|
+
lines, then one {"type": "proposals"} or {"type": "failed"} line.
|
|
468
|
+
|
|
469
|
+
Nothing is written: the client adds what it gets back as proposals
|
|
470
|
+
(POST /notes/{id}/tag-proposals/add), one undo step.
|
|
471
|
+
"""
|
|
472
|
+
if not note_store.has_note(note_id):
|
|
473
|
+
raise HTTPException(status_code=404, detail=f"Note not found: {note_id}")
|
|
474
|
+
session_key = token_service.get_session_key(token)
|
|
475
|
+
connection = _require_ai_connection(provider=payload.provider, token=token, session_key=session_key)
|
|
476
|
+
preferences = load_client_preferences(token=token)
|
|
477
|
+
boundary = resolve_cloud_privacy_boundary(preferences=preferences, provider=payload.provider)
|
|
478
|
+
if cloud_privacy_evaluator.exclusion_reason(note_id=note_id, boundary=boundary) != "":
|
|
479
|
+
raise InputRejected(
|
|
480
|
+
"Your AI privacy settings hide this note from the AI, so it can't suggest tags for it"
|
|
481
|
+
)
|
|
482
|
+
scope = resolve_view_scope_membership(
|
|
483
|
+
search=payload.search,
|
|
484
|
+
sort_mode=tab_state_store.get_sort_mode(tab_id=payload.tab_id),
|
|
485
|
+
is_untagged_view=payload.is_untagged_view,
|
|
486
|
+
)
|
|
487
|
+
if note_id not in scope.allowed_note_ids:
|
|
488
|
+
raise HTTPException(status_code=409, detail="The note is not in this view any more")
|
|
489
|
+
request = build_note_tag_request(
|
|
490
|
+
note_id=note_id, include_children=payload.include_children, boundary=boundary,
|
|
491
|
+
tag_skill=DEFAULT_AGENT_SKILLS.for_action("tag_proposals"), scope_note_ids=scope.allowed_note_ids,
|
|
492
|
+
batch_tokens=resolve_tagging_batch_tokens(preferences, payload.provider),
|
|
493
|
+
)
|
|
494
|
+
events = _note_tag_suggestion_events(
|
|
495
|
+
request=request, connection=connection, payload=payload,
|
|
496
|
+
web_settings=resolve_agent_web_settings(preferences=preferences), session_key=session_key,
|
|
497
|
+
)
|
|
498
|
+
return StreamingResponse(
|
|
499
|
+
events,
|
|
500
|
+
media_type="application/x-ndjson",
|
|
501
|
+
headers={"X-Accel-Buffering": "no", "Cache-Control": "no-store", "Content-Encoding": "identity"},
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
async def _note_tag_suggestion_events(
|
|
506
|
+
*, request: NoteTagRequest, connection: AiConnection, payload: NoteTagSuggestionRequest,
|
|
507
|
+
web_settings: AgentWebSettings, session_key: str,
|
|
508
|
+
) -> AsyncIterator[str]:
|
|
509
|
+
events = suggest_note_tags(
|
|
510
|
+
request=request, inference=connection.inference, base_url=connection.base_url,
|
|
511
|
+
model=payload.model, thinking_level=payload.thinking_level,
|
|
512
|
+
web_settings=web_settings, session_key=session_key,
|
|
513
|
+
)
|
|
514
|
+
# lint: allow-PY001 rationale="the AI provider failed or kept answering unusably: the client shows why"
|
|
515
|
+
try:
|
|
516
|
+
async for event in events:
|
|
517
|
+
yield json.dumps(event) + "\n"
|
|
518
|
+
# lint: allow-PY001 rationale="the AI provider failed or kept answering unusably: the client shows why"
|
|
519
|
+
except InferenceProviderError as exc:
|
|
520
|
+
yield json.dumps({"type": "failed", "message": str(exc)}) + "\n"
|
|
521
|
+
finally:
|
|
522
|
+
await events.aclose()
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def _export_title_request(scope: ExportTitleScope, token: str) -> ExportTitleRequest:
|
|
526
|
+
"""What the AI may read of the export or print: the notes the AI privacy settings let it see."""
|
|
527
|
+
note_id = None
|
|
528
|
+
if scope.exported == "note":
|
|
529
|
+
note_id = scope.note_id
|
|
530
|
+
if not note_store.has_note(note_id):
|
|
531
|
+
raise HTTPException(status_code=404, detail=f"Note not found: {note_id}")
|
|
532
|
+
search = None
|
|
533
|
+
if scope.search != "":
|
|
534
|
+
search = scope.search
|
|
535
|
+
root_ids, allowed_note_ids = resolve_export_scope(
|
|
536
|
+
search=search,
|
|
537
|
+
sort_mode=tab_state_store.get_sort_mode(tab_id=scope.tab_id),
|
|
538
|
+
is_untagged_view=scope.is_untagged_view, root_note_id=note_id,
|
|
539
|
+
)
|
|
540
|
+
boundary = resolve_cloud_privacy_boundary(preferences=load_client_preferences(token=token), provider=scope.provider)
|
|
541
|
+
return build_export_title_request(root_ids=root_ids, allowed_note_ids=allowed_note_ids, boundary=boundary)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
@router.post("/export-title/availability", response_model=ExportTitleAvailabilityResponse)
|
|
545
|
+
@transactional_route
|
|
546
|
+
def export_title_availability(
|
|
547
|
+
payload: ExportTitleScope,
|
|
548
|
+
response: Response,
|
|
549
|
+
token: Annotated[str, Depends(require_request_auth_token)],
|
|
550
|
+
) -> ExportTitleAvailabilityResponse:
|
|
551
|
+
"""Whether the Export and Print dialog's "Suggest with AI" can ask, and what it says about private notes."""
|
|
552
|
+
response.headers["Cache-Control"] = "no-store"
|
|
553
|
+
session_key = token_service.get_session_key(token)
|
|
554
|
+
if not describe_ai_setup(token=token, session_key=session_key).is_ready_for(payload.provider):
|
|
555
|
+
return ExportTitleAvailabilityResponse(availability="unavailable")
|
|
556
|
+
return ExportTitleAvailabilityResponse(availability=_export_title_request(payload, token).availability)
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
@router.post("/export-title")
|
|
560
|
+
@transactional_route
|
|
561
|
+
def suggest_export_title_with_ai(
|
|
562
|
+
payload: ExportTitleSuggestionRequest,
|
|
563
|
+
token: Annotated[str, Depends(require_request_auth_token)],
|
|
564
|
+
) -> StreamingResponse:
|
|
565
|
+
"""Ask the AI for an export's or print's title: one {"type": "title"} or {"type": "failed"} line.
|
|
566
|
+
Streamed, so Stop (the client going away) ends the request."""
|
|
567
|
+
session_key = token_service.get_session_key(token)
|
|
568
|
+
connection = _require_ai_connection(provider=payload.provider, token=token, session_key=session_key)
|
|
569
|
+
request = _export_title_request(payload, token)
|
|
570
|
+
if not request.notes:
|
|
571
|
+
raise InputRejected("These notes are private, so the AI can't suggest a title")
|
|
572
|
+
web_mode = resolve_agent_web_settings(preferences=load_client_preferences(token=token)).mode
|
|
573
|
+
return StreamingResponse(
|
|
574
|
+
_export_title_events(
|
|
575
|
+
request=request, payload=payload, connection=connection, web_mode=web_mode, session_key=session_key,
|
|
576
|
+
),
|
|
577
|
+
media_type="application/x-ndjson",
|
|
578
|
+
headers={"X-Accel-Buffering": "no", "Cache-Control": "no-store", "Content-Encoding": "identity"},
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
async def _export_title_events(
|
|
583
|
+
*, request: ExportTitleRequest, payload: ExportTitleSuggestionRequest, connection: AiConnection,
|
|
584
|
+
web_mode: str, session_key: str,
|
|
585
|
+
) -> AsyncIterator[str]:
|
|
586
|
+
# lint: allow-PY001 rationale="the AI provider failed or answered without a title: the dialog shows why"
|
|
587
|
+
try:
|
|
588
|
+
title = await suggest_export_title(
|
|
589
|
+
request=request, previous_titles=tuple(payload.previous_titles), inference=connection.inference,
|
|
590
|
+
base_url=connection.base_url, model=payload.model, thinking_level=payload.thinking_level,
|
|
591
|
+
web_mode=web_mode, session_key=session_key,
|
|
592
|
+
)
|
|
593
|
+
yield json.dumps({"type": "title", "title": title}) + "\n"
|
|
594
|
+
# lint: allow-PY001 rationale="the AI provider failed or answered without a title: the dialog shows why"
|
|
595
|
+
except InferenceProviderError as exc:
|
|
596
|
+
yield json.dumps({"type": "failed", "message": str(exc)}) + "\n"
|
|
597
|
+
|
|
598
|
+
|
|
333
599
|
def _openai_cost_response(snapshot: OpenAICostSnapshot) -> OpenAICostResponse:
|
|
334
600
|
return OpenAICostResponse(
|
|
335
601
|
estimated_cost_usd=float(snapshot.estimated_cost_usd),
|
|
@@ -576,19 +842,7 @@ def stream_ai_chat(
|
|
|
576
842
|
token: Annotated[str, Depends(require_request_auth_token)],
|
|
577
843
|
) -> StreamingResponse:
|
|
578
844
|
session_key = token_service.get_session_key(token)
|
|
579
|
-
|
|
580
|
-
token=token,
|
|
581
|
-
session_key=session_key,
|
|
582
|
-
)
|
|
583
|
-
if not credential_status.configured:
|
|
584
|
-
raise HTTPException(
|
|
585
|
-
status_code=409,
|
|
586
|
-
detail="OpenAI API key is not configured",
|
|
587
|
-
)
|
|
588
|
-
openai_api_key = openai_credential_store.resolve(
|
|
589
|
-
token=token,
|
|
590
|
-
session_key=session_key,
|
|
591
|
-
)
|
|
845
|
+
connection = _require_ai_connection(provider=payload.provider, token=token, session_key=session_key)
|
|
592
846
|
preferences = load_client_preferences(token=token)
|
|
593
847
|
prompts = DEFAULT_AGENT_PROMPTS
|
|
594
848
|
skills = DEFAULT_AGENT_SKILLS
|
|
@@ -655,7 +909,7 @@ def stream_ai_chat(
|
|
|
655
909
|
turn_stream = ChatTurnStream(store=ai_chat_store, notes=note_store,
|
|
656
910
|
web_evidence_store=web_evidence_store, session_key=session_key,
|
|
657
911
|
turn_id=turn_id, initial_input_tokens=initial_approx_input_tokens)
|
|
658
|
-
events = _stream_runtime_events(payload=payload,
|
|
912
|
+
events = _stream_runtime_events(payload=payload, connection=connection,
|
|
659
913
|
session_key=session_key, provider_messages=provider_messages, prompts=prompts, skills=skills,
|
|
660
914
|
retrieval_settings=retrieval_settings, frozen_scope=frozen_scope, tagging_run=tagging_run,
|
|
661
915
|
web_settings=web_settings, earlier_actions=earlier_actions,
|
|
@@ -673,16 +927,16 @@ def stream_ai_chat(
|
|
|
673
927
|
|
|
674
928
|
|
|
675
929
|
async def _stream_runtime_events(
|
|
676
|
-
*, payload: AiChatRequest,
|
|
930
|
+
*, payload: AiChatRequest, connection: AiConnection, session_key: str,
|
|
677
931
|
provider_messages: list[dict[str, str]], prompts: AgentPromptSet, skills: AgentSkillSet,
|
|
678
932
|
retrieval_settings: AgentRetrievalSettings, frozen_scope: ScopedSearchSnapshot,
|
|
679
933
|
tagging_run: TaggingRun, initial_input_tokens: int, web_settings: AgentWebSettings,
|
|
680
934
|
earlier_actions: list[dict[str, object]],
|
|
681
935
|
) -> AsyncIterator[dict[str, object]]:
|
|
682
|
-
action, start_label = 'provider_runtime', 'Connecting to
|
|
683
|
-
ready_label =
|
|
684
|
-
base_url =
|
|
685
|
-
runtime = _agent_runtime(inference=
|
|
936
|
+
action, start_label = 'provider_runtime', f'Connecting to {connection.label} API'
|
|
937
|
+
ready_label = connection.ready_label
|
|
938
|
+
base_url = connection.base_url
|
|
939
|
+
runtime = _agent_runtime(inference=connection.inference)
|
|
686
940
|
yield dict(type='action_status', action=action, status='started', label=start_label,
|
|
687
941
|
approx_input_tokens=initial_input_tokens, output_tokens_received=0, duration_ms=0.0)
|
|
688
942
|
yield dict(type='action_status', action=action, status='completed', label=ready_label,
|
|
@@ -31,7 +31,10 @@ from app.api.note_requests import (
|
|
|
31
31
|
SaveNoteRequest,
|
|
32
32
|
AddSelectedTextTagRequest,
|
|
33
33
|
AcceptTagProposalRequest,
|
|
34
|
+
AddTagProposalsRequest,
|
|
35
|
+
OpenToTagProposalsRequest,
|
|
34
36
|
RejectTagProposalRequest,
|
|
37
|
+
ResolveRobotProposalsRequest,
|
|
35
38
|
SplitNoteRequest,
|
|
36
39
|
ToggleTodoDoneRequest,
|
|
37
40
|
UnformatNoteContentRequest,
|
|
@@ -60,6 +63,7 @@ from app.api.transactions import transactional_route
|
|
|
60
63
|
from app.services.sync import touch_client
|
|
61
64
|
from app.services.snapshot import build_view_state
|
|
62
65
|
from app.services.snapshot import resolve_search_scope
|
|
66
|
+
from app.services.snapshot import resolve_view_scope_membership
|
|
63
67
|
from app.services.note_store import store as note_store
|
|
64
68
|
from app.services.exception_capture import CapturedExceptionContext
|
|
65
69
|
from app.usecases.create_note import CmdCreateNote
|
|
@@ -78,7 +82,10 @@ from app.services.link_title_retry import (
|
|
|
78
82
|
from app.usecases.add_selected_text_tag import CmdAddSelectedTextTag
|
|
79
83
|
from app.usecases.tag_proposals import (
|
|
80
84
|
CmdAcceptTagProposal,
|
|
85
|
+
CmdAddTagProposals,
|
|
86
|
+
CmdOpenToTagProposals,
|
|
81
87
|
CmdRejectTagProposal,
|
|
88
|
+
CmdResolveRobotProposals,
|
|
82
89
|
)
|
|
83
90
|
from app.usecases.delete_subtree import CmdDeleteSubtree
|
|
84
91
|
from app.usecases.move import CmdMove
|
|
@@ -132,7 +139,10 @@ from app.services.search_history import (
|
|
|
132
139
|
from app.services.root_sorting import is_root_reorder_locked
|
|
133
140
|
from app.services.root_sorting import normalize_sort_mode
|
|
134
141
|
from app.services.search_query import parse_search_query
|
|
142
|
+
from app.services.html_export import EXPORT_PURPOSES
|
|
135
143
|
from app.services.html_export import build_notes_export_document
|
|
144
|
+
from app.services.html_export import default_export_title
|
|
145
|
+
from app.services.html_export import exported_credential_counts
|
|
136
146
|
from app.services.html_export import build_notes_export_filename
|
|
137
147
|
from app.services.note_fullscreen import build_note_fullscreen_markup
|
|
138
148
|
from app.services.link_titles import link_title_store
|
|
@@ -682,6 +692,35 @@ def search_tag_details(request: Request, payload: SearchTagDetailsRequest) -> Di
|
|
|
682
692
|
return {"lines": list(lines)}
|
|
683
693
|
|
|
684
694
|
|
|
695
|
+
@router.get("/notes/export-summary")
|
|
696
|
+
def export_summary(request: Request) -> Dict[str, object]:
|
|
697
|
+
"""What an export or print of a note (with its children) or of the view holds,
|
|
698
|
+
for its dialog: the title it starts with, and how many passwords and usernames it shows."""
|
|
699
|
+
_require_bearer_token(request)
|
|
700
|
+
search_query = request.query_params.get("search_query")
|
|
701
|
+
tab_id = request.query_params.get("tab_id")
|
|
702
|
+
is_untagged_view = request.query_params.get("is_untagged_view")
|
|
703
|
+
if search_query is None or tab_id is None or tab_id == "" or is_untagged_view not in {"true", "false"}:
|
|
704
|
+
raise HTTPException(status_code=400, detail="search_query, tab_id and is_untagged_view are required")
|
|
705
|
+
note_id = request.query_params.get("note_id")
|
|
706
|
+
if note_id is not None:
|
|
707
|
+
_require_note_present(note_id, context="notes.export-summary")
|
|
708
|
+
normalized_search = search_query
|
|
709
|
+
if normalized_search == "":
|
|
710
|
+
normalized_search = None
|
|
711
|
+
counts = exported_credential_counts(
|
|
712
|
+
search=normalized_search, sort_mode=_resolve_tab_sort_mode(tab_id),
|
|
713
|
+
is_untagged_view=is_untagged_view == "true", root_note_id=note_id,
|
|
714
|
+
)
|
|
715
|
+
return {
|
|
716
|
+
"title": default_export_title(
|
|
717
|
+
search=search_query, is_untagged_view=is_untagged_view == "true", root_note_id=note_id,
|
|
718
|
+
),
|
|
719
|
+
"password_count": counts.passwords,
|
|
720
|
+
"username_count": counts.usernames,
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
|
|
685
724
|
@router.get("/notes/export-html")
|
|
686
725
|
def export_notes_html(request: Request) -> Response:
|
|
687
726
|
token = _require_bearer_token(request)
|
|
@@ -709,14 +748,40 @@ def export_notes_html(request: Request) -> Response:
|
|
|
709
748
|
normalized_theme = theme.strip().lower()
|
|
710
749
|
if normalized_theme not in {"dark", "light"}:
|
|
711
750
|
raise HTTPException(status_code=400, detail="theme must be 'light' or 'dark'")
|
|
751
|
+
# The view as the tab shows it: its sort order and kind (All or Untagged notes).
|
|
752
|
+
tab_id = request.query_params.get("tab_id")
|
|
753
|
+
if tab_id is None or tab_id == "":
|
|
754
|
+
raise HTTPException(status_code=400, detail="tab_id query parameter is required")
|
|
755
|
+
is_untagged_view = request.query_params.get("is_untagged_view")
|
|
756
|
+
if is_untagged_view not in {"true", "false"}:
|
|
757
|
+
raise HTTPException(status_code=400, detail="is_untagged_view query parameter must be 'true' or 'false'")
|
|
758
|
+
|
|
759
|
+
# Both carry the user's title; a download collapses and expands, a print is always light.
|
|
760
|
+
purpose = request.query_params.get("purpose")
|
|
761
|
+
if purpose not in EXPORT_PURPOSES:
|
|
762
|
+
raise HTTPException(status_code=400, detail="purpose query parameter must be 'download' or 'print'")
|
|
763
|
+
title = request.query_params.get("title")
|
|
764
|
+
if title is None:
|
|
765
|
+
raise HTTPException(status_code=400, detail="title query parameter is required")
|
|
766
|
+
if purpose == "print":
|
|
767
|
+
normalized_theme = "light"
|
|
768
|
+
# Passwords and usernames show only when the user chose to include them in this export.
|
|
769
|
+
include_credentials = request.query_params.get("include_credentials")
|
|
770
|
+
if include_credentials not in {"true", "false"}:
|
|
771
|
+
raise HTTPException(status_code=400, detail="include_credentials query parameter must be 'true' or 'false'")
|
|
712
772
|
|
|
713
773
|
document = build_notes_export_document(
|
|
714
774
|
search=normalized_search,
|
|
775
|
+
sort_mode=_resolve_tab_sort_mode(tab_id),
|
|
776
|
+
is_untagged_view=is_untagged_view == "true",
|
|
715
777
|
theme=normalized_theme,
|
|
716
778
|
token=token,
|
|
717
779
|
root_note_id=note_id,
|
|
780
|
+
purpose=purpose,
|
|
781
|
+
title=title,
|
|
782
|
+
include_credentials=include_credentials == "true",
|
|
718
783
|
)
|
|
719
|
-
filename = build_notes_export_filename()
|
|
784
|
+
filename = build_notes_export_filename(title)
|
|
720
785
|
quoted_filename = urllib.parse.quote(filename)
|
|
721
786
|
return Response(
|
|
722
787
|
content=document,
|
|
@@ -967,6 +1032,67 @@ def accept_tag_proposal(request: Request, note_id: str, body: AcceptTagProposalR
|
|
|
967
1032
|
)
|
|
968
1033
|
|
|
969
1034
|
|
|
1035
|
+
@router.post("/notes/{note_id}/tag-proposals/add")
|
|
1036
|
+
@transactional_route
|
|
1037
|
+
def add_tag_proposals(request: Request, note_id: str, body: AddTagProposalsRequest):
|
|
1038
|
+
token = _require_bearer_token(request)
|
|
1039
|
+
viewport = _require_viewport(body)
|
|
1040
|
+
_require_note_present(note_id, context="notes.tag-proposals.add")
|
|
1041
|
+
return _execute_tag_proposal_mutation(
|
|
1042
|
+
command=CmdAddTagProposals(
|
|
1043
|
+
note_id=note_id,
|
|
1044
|
+
proposals={target_id: tuple(tags) for target_id, tags in body["proposals"].items()},
|
|
1045
|
+
token=token,
|
|
1046
|
+
client_id=body["clientId"],
|
|
1047
|
+
undo_context=body["undoContext"],
|
|
1048
|
+
viewport=viewport,
|
|
1049
|
+
)
|
|
1050
|
+
)
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
def _robot_scope_note_ids(body: dict) -> frozenset[str]:
|
|
1054
|
+
"""The notes the view showing a robot count lets it count (as /notes/view resolves them)."""
|
|
1055
|
+
return resolve_view_scope_membership(
|
|
1056
|
+
search=body["search"],
|
|
1057
|
+
sort_mode=_resolve_tab_sort_mode(body["tabId"]),
|
|
1058
|
+
is_untagged_view=body["isUntaggedView"],
|
|
1059
|
+
).allowed_note_ids
|
|
1060
|
+
|
|
1061
|
+
|
|
1062
|
+
@router.post("/notes/{note_id}/tag-proposals/open")
|
|
1063
|
+
@transactional_route
|
|
1064
|
+
def open_to_tag_proposals(note_id: str, body: OpenToTagProposalsRequest):
|
|
1065
|
+
viewport = _require_viewport(body)
|
|
1066
|
+
_require_note_present(note_id, context="notes.tag-proposals.open")
|
|
1067
|
+
return CmdOpenToTagProposals(
|
|
1068
|
+
note_id=note_id,
|
|
1069
|
+
scope_note_ids=_robot_scope_note_ids(body),
|
|
1070
|
+
client_id=body["clientId"],
|
|
1071
|
+
undo_context=body["undoContext"],
|
|
1072
|
+
viewport=viewport,
|
|
1073
|
+
).execute()
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
@router.post("/notes/{note_id}/tag-proposals/resolve-all")
|
|
1077
|
+
@transactional_route
|
|
1078
|
+
def resolve_robot_tag_proposals(request: Request, note_id: str, body: ResolveRobotProposalsRequest):
|
|
1079
|
+
token = _require_bearer_token(request)
|
|
1080
|
+
viewport = _require_viewport(body)
|
|
1081
|
+
_require_note_present(note_id, context="notes.tag-proposals.resolve-all")
|
|
1082
|
+
return _execute_tag_proposal_mutation(
|
|
1083
|
+
command=CmdResolveRobotProposals(
|
|
1084
|
+
note_id=note_id,
|
|
1085
|
+
action=body["action"],
|
|
1086
|
+
includes_hidden_children=body["includesHiddenChildren"],
|
|
1087
|
+
scope_note_ids=_robot_scope_note_ids(body),
|
|
1088
|
+
token=token,
|
|
1089
|
+
client_id=body["clientId"],
|
|
1090
|
+
undo_context=body["undoContext"],
|
|
1091
|
+
viewport=viewport,
|
|
1092
|
+
)
|
|
1093
|
+
)
|
|
1094
|
+
|
|
1095
|
+
|
|
970
1096
|
@router.post("/notes/{note_id}/tag-proposals/reject")
|
|
971
1097
|
@transactional_route
|
|
972
1098
|
def reject_tag_proposal(request: Request, note_id: str, body: RejectTagProposalRequest):
|
|
@@ -171,6 +171,7 @@ CAPTURE_BOUNDARIES = {
|
|
|
171
171
|
# Handle expected external I/O, provider, image or LaTeX parsing failure.
|
|
172
172
|
'convert-from-legacy.py:_legacy_rule_is_currently_valid:parse_capture': ('OntologyParseError',),
|
|
173
173
|
# Handle expected external I/O, provider, image or LaTeX parsing failure.
|
|
174
|
+
'main.py:_bind_https_proxy_server:bind_capture': ('OSError',),
|
|
174
175
|
'main.py:_is_process_running:kill_capture': ('ProcessLookupError', 'PermissionError'),
|
|
175
176
|
# Handle expected external I/O, provider, image or LaTeX parsing failure.
|
|
176
177
|
'main.py:_send_signal_if_running:signal_capture': ('ProcessLookupError',),
|