MetaList 0.6.3__tar.gz → 0.7.1__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.6.3 → metalist-0.7.1}/PKG-INFO +13 -1
- {metalist-0.6.3 → metalist-0.7.1}/README.md +12 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/exception_boundaries.py +2 -2
- metalist-0.7.1/app/network_settings.py +61 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/security/request_boundary.py +1 -74
- metalist-0.7.1/app/security/request_hosts.py +83 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/note_store.py +117 -45
- {metalist-0.6.3 → metalist-0.7.1}/app/static/css/main.css +5 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/version-info-modal.js +5 -1
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/tab-state-service.js +24 -3
- {metalist-0.6.3 → metalist-0.7.1}/app/version.py +1 -1
- {metalist-0.6.3 → metalist-0.7.1}/main.py +9 -0
- {metalist-0.6.3 → metalist-0.7.1}/metalist.egg-info/PKG-INFO +13 -1
- {metalist-0.6.3 → metalist-0.7.1}/metalist.egg-info/SOURCES.txt +2 -0
- {metalist-0.6.3 → metalist-0.7.1}/MANIFEST.in +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/deps.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/dev.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/middleware/auth.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/note_requests.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/request_auth.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/routes/ai.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/routes/app_updates.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/routes/auth.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/routes/backups.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/routes/files.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/routes/notes.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/routes/ontology.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/routes/reminders.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/routes/remote_images.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/routes/test.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/transactions.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/api/upload_limits.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/config.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/data_directory.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/backup_settings_sql.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/engine.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/file_schema.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/file_session.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/files_sql.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/link_titles_sql.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/live_recovery.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/migrations.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/notes_sql.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/ontology_rules_sql.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/reminders_sql.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/retire_sounds.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/schema.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/search_history_sql.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/session.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/settings_sql.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/tab_state_sql.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/db/version.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/encryption_audit.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/https_proxy.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/main.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/models/commands.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/models/database.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/models/entities.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/models/enums.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/models/linked_list.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/models/list_operations.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/models/list_traversal.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/models/note_crud.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/models/utils.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/presentation/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/presentation/render/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/presentation/render/note_renderer.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/presentation/templates.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/security/authenticated_logging.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/security/encryption.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/security/http_headers.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/security/note_html.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/security/sensitive_cache.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/security/sensitive_logging.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/security/shell_execution.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/security/validation_errors.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/server_runtime.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/actions.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/cloud_privacy.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/context.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/evidence_serialization.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/inference.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/investigation.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/model_policy.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/ollama_inference.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/openai_cost_tracking.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/openai_inference.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/permissions.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/prompt_settings.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/prompts/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/prompts/final-response.md +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/prompts/system.md +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/prompts/tool-result.md +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/retrieval_settings.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/runtime.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/scope.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/skill_settings.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/skills/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/skills/scoped-investigation.md +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/tagging.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/tagging_run.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/token_estimation.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/tools.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/agent/trace.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/ai_chat.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/ai_chat_rendering.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/ai_chat_stream.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/app_update_worker.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/app_updates.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/auth.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/auth_cache_state.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/auth_service.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/backlink_index.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/backlinks.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/backup_service.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/backup_settings_service.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/bulk_operation.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/client_state_service.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/content_cache.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/content_formatting.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/diagnostics.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/embedded_references.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/encryption.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/exception_capture.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/file_registry.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/file_storage.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/footnote_layout.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/footnote_rendering.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/hierarchy.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/html_export.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/html_unformatting.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/hydration_state.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/image_size_formatting.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/inline_image_occurrences.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/input_errors.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/integrity.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/latex_rendering.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/link_titles.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/login_rate_limit.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/maintenance_mode.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/managed_ollama_runtime.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/markdown_rendering.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/namespace_deletion_jobs.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/namespace_deletion_worker.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/namespace_rename_jobs.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/namespace_rename_worker.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/namespace_runtime_guard.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/namespace_switcher.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/note_fullscreen.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/note_image_tags.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/note_ordering.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/ollama_provider.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/ontology_rules_store.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/openai_credentials.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/password_note_fields.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/reference_presentation.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/reminders.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/remote_image_proxy.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/request_recovery.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/resource_limits.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/root_sorting.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/runtime_generation.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/runtime_hardening.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/runtime_lock.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/search_history.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/search_history_storage.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/search_index.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/search_query.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/search_text.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/selected_text_tag.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/self_update.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/session_timeout_service.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/shell_session_service.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/snapshot.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/store.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/structured_note_renderers.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/sync.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/sync_state.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/tab_state.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/tag_case.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/tag_ontology.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/tag_rename.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/tag_suggestions.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/tag_term_matching.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/test_reset.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/tokens.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/undo_state.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/update_backups.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/update_preflight.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/view_cache.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/view_diff.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/view_state.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/services/windows_process_control.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/startup_environment.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/startup_js_sanity.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/startup_sanity.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/startup_sanity_config.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/config/command_palette_tags.json +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/css/floating-notes.css +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/css/forms.css +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/img/favicon.ico +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/locked.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/main.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/activity-tracker.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/agent-prompt-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/agent-retrieval-settings.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/ai-agent-debug-view.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/ai-chat-api.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/ai-chat-panel-controller.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/ai-chat-panel-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/ai-thinking-level-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/bulk-elapsed-clock.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/bulk-proposal-ui.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/cloud-privacy-policy.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/ai-chat/proposal-menu.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/api-client.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/api-failure-classification-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/app-update-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/application-state.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/async-result.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/auth.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/client-state-api.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/client-state-migration.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/command-palette/activation-auth-policy.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/command-palette/backup-command-availability.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/command-palette/command-palette-controller.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/command-palette/endpoint-registry.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/command-palette/note-layout-preferences.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/command-palette/preferences-store.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/command-palette/tag-config-loader.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/command-palette/usage-store.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/config.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/connectivity-monitor.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/context-menu/context-menu-registry.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/context-menu/context-menu-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/context-menu/context-menu-target-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/context-menu/input-caret-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/disconnect-editing-state-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/dom-utils-fixed.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/dom-utils.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/editor-commands.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/editor-selection.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/editor-toolbar.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/elapsed-time.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/error-handler.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/error-overlay.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/expected-errors.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/location-flags.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/login-namespace-picker.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/maintenance.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/agent-prompt-editor-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/ai-agent-settings-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/backup-restore-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/backup-result-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/backup-settings-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/base-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/confirmation-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/delete-namespace-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/delete-namespace-validation.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/help-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/namespace-modals.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/namespace-ports-profile.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/namespace-rename-validation.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/note-layout-appearance-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/ontology-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/password-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/prioritize-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/random-password-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/reminder-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/search-suggestion-statistics-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/modals/session-timeout-modal.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/actions/content-actions.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/actions/history-actions.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/actions/note-actions.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/actions/selection-actions.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/actions/ui-actions.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/events/context-menu-events.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/events/focus-events.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/events/input-events.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/events/keyboard-events.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/events/mouse-events.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/events/search-events.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/mode-context.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/mode-manager-controller.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/add-style-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/backlinks-navigation-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/command-gate-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/deselect-selection-state-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/differential-view-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/file-drop-policy-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/floating-note-dom.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/floating-note-layout.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/floating-note-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/footnote-navigation-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/history-selection-policy-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/history-shortcut-policy-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/link-title-prefetch-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/mermaid-render-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/note-delete-shortcut-policy-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/note-fullscreen-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/note-timestamp-hover-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/polling-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/reference-source-navigation-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/remote-image-proxy-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/remove-formatting-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/root-sort-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/search-input-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/search-suggestion-statistics-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/search-suggestion-window-labels-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/search-suggestion-windows-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/search-syntax-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/selected-text-tag-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/tab-drag-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/tag-bar-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/tag-input-selection-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/tag-proposal-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/untagged-view-indicator-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/note-html-sanitizer.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/password-generator.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/password-operation-response.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/password-policy.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/password-strength.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/reminder-store.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/reminder-surface-service.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/selection-markers.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/session-auth.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/tag-token.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/modules/uuid.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/namespace-deleted.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/namespace-renamed.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/vendor/dompurify-3.4.13.LICENSE.txt +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/vendor/dompurify-3.4.13.min.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/vendor/markdown-it-14.3.2.LICENSE.txt +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/vendor/markdown-it-14.3.2.min.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/vendor/mermaid-11.16.1.LICENSE.txt +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/vendor/mermaid-11.16.1.min.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/vendor/zxcvbn-4.4.2.LICENSE.txt +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/js/vendor/zxcvbn-4.4.2.js +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/note-html-policy.json +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/static/video/login-startup.mp4 +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/templates/base.html +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/templates/index.html +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/templates/locked.html +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/templates/maintenance.html +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/templates/namespace_deleted.html +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/templates/namespace_renamed.html +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/templates/notes_list.html +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/upload_limits.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/add_selected_text_tag.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/alphabetize_root_notes.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/base.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/bulk_tag_proposals.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/collapse.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/copy_note.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/create_child.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/create_note.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/create_sibling.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/delete_subtree.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/delete_tag.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/expand.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/indent.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/lock.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/move.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/move_to_top.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/outdent.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/paste_child.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/paste_sibling.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/prefetch_link_titles.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/prioritize.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/redo.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/rename_tag.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/reset_updated_at.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/resize_image.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/run_shell.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/search_comment_autofill.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/search_context_tags.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/set_collapse_bulk.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/set_collapse_in_context.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/split_note.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/tag_proposals.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/toggle_reference_mode.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/toggle_todo_done.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/undo.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/unformat_content.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/update_content.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/usecases/view.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/utils/__init__.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/utils/encryption.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/app/utils/text_utils.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/convert-from-legacy.py +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/metalist.egg-info/dependency_links.txt +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/metalist.egg-info/entry_points.txt +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/metalist.egg-info/requires.txt +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/metalist.egg-info/top_level.txt +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/pyproject.toml +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/scripts/generate-lan-cert.sh +0 -0
- {metalist-0.6.3 → metalist-0.7.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metalist
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
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
|
|
@@ -213,6 +213,18 @@ METALIST_HOST=192.168.1.20 metalist
|
|
|
213
213
|
```
|
|
214
214
|
On a fresh machine, that first launch also creates the default TLS cert pair automatically. Then open either `http://192.168.1.20:8000` or `https://192.168.1.20:8443` from the other machine.
|
|
215
215
|
|
|
216
|
+
Starting with 0.7.1, an explicit `METALIST_HOST` or `METALIST_ALLOWED_HOSTS`
|
|
217
|
+
configuration is saved in `~/MetaList/network-settings.json` (under
|
|
218
|
+
`METALIST_DATA_DIRECTORY` when overridden). Subsequent source launches, plain
|
|
219
|
+
`metalist` launches, and updates reuse it. Environment values override and update
|
|
220
|
+
the corresponding saved settings. This file stores only the bind address and
|
|
221
|
+
allowed hosts; namespace ports remain in their launch profiles, and shell access
|
|
222
|
+
still requires `--enable-shell` on each launch. Invalid settings stop startup
|
|
223
|
+
with an error. Fresh installations continue to bind to localhost by default.
|
|
224
|
+
To switch back to local-only access, run `METALIST_HOST=127.0.0.1 metalist` once.
|
|
225
|
+
Older installed versions do not read this file; keep supplying their LAN settings
|
|
226
|
+
until upgrading to 0.7.1 or later.
|
|
227
|
+
|
|
216
228
|
To keep `@shell` available on the host laptop while allowing another laptop to
|
|
217
229
|
use normal MetaList features, bind both interfaces and explicitly allow the LAN
|
|
218
230
|
address:
|
|
@@ -177,6 +177,18 @@ METALIST_HOST=192.168.1.20 metalist
|
|
|
177
177
|
```
|
|
178
178
|
On a fresh machine, that first launch also creates the default TLS cert pair automatically. Then open either `http://192.168.1.20:8000` or `https://192.168.1.20:8443` from the other machine.
|
|
179
179
|
|
|
180
|
+
Starting with 0.7.1, an explicit `METALIST_HOST` or `METALIST_ALLOWED_HOSTS`
|
|
181
|
+
configuration is saved in `~/MetaList/network-settings.json` (under
|
|
182
|
+
`METALIST_DATA_DIRECTORY` when overridden). Subsequent source launches, plain
|
|
183
|
+
`metalist` launches, and updates reuse it. Environment values override and update
|
|
184
|
+
the corresponding saved settings. This file stores only the bind address and
|
|
185
|
+
allowed hosts; namespace ports remain in their launch profiles, and shell access
|
|
186
|
+
still requires `--enable-shell` on each launch. Invalid settings stop startup
|
|
187
|
+
with an error. Fresh installations continue to bind to localhost by default.
|
|
188
|
+
To switch back to local-only access, run `METALIST_HOST=127.0.0.1 metalist` once.
|
|
189
|
+
Older installed versions do not read this file; keep supplying their LAN settings
|
|
190
|
+
until upgrading to 0.7.1 or later.
|
|
191
|
+
|
|
180
192
|
To keep `@shell` available on the host laptop while allowing another laptop to
|
|
181
193
|
use normal MetaList features, bind both interfaces and explicitly allow the LAN
|
|
182
194
|
address:
|
|
@@ -78,9 +78,9 @@ CAPTURE_BOUNDARIES = {
|
|
|
78
78
|
# Translate explicitly validated domain input/resource rejection.
|
|
79
79
|
'app/main.py:namespace_renamed_page:job_record_capture': ('NamespaceInputRejected', 'FileNotFoundError'),
|
|
80
80
|
# Parse external host, URL, IP or date input at the named boundary.
|
|
81
|
-
'app/security/
|
|
81
|
+
'app/security/request_hosts.py:_normalize_configured_hostname:normalize_capture': ('ValueError',),
|
|
82
82
|
# Parse external host, URL, IP or date input at the named boundary.
|
|
83
|
-
'app/security/
|
|
83
|
+
'app/security/request_hosts.py:_normalize_hostname:ip_capture': ('ValueError',),
|
|
84
84
|
# Parse external host, URL, IP or date input at the named boundary.
|
|
85
85
|
'app/security/request_boundary.py:_parse_host_header:parse_capture': ('ValueError',),
|
|
86
86
|
# Parse external host, URL, IP or date input at the named boundary.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Machine network preferences shared by source and installed entrypoints.
|
|
2
|
+
|
|
3
|
+
Only an explicit bind address/allowed-host configuration opts into persistence.
|
|
4
|
+
Namespace ports, shell capability, credentials, and backups are not stored here.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from collections.abc import MutableMapping
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from uuid import uuid4
|
|
13
|
+
|
|
14
|
+
from app.data_directory import resolve_data_directory
|
|
15
|
+
from app.security.request_hosts import resolve_allowed_request_hosts
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
_NETWORK_ENVIRONMENT_KEYS = frozenset({'METALIST_HOST', 'METALIST_ALLOWED_HOSTS'})
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _validate_network_environment(environment: object) -> dict[str, str]:
|
|
22
|
+
if not isinstance(environment, dict) or not environment.keys() <= _NETWORK_ENVIRONMENT_KEYS:
|
|
23
|
+
raise RuntimeError('Network settings must contain only bind-address and allowed-host preferences')
|
|
24
|
+
if any(not isinstance(value, str) for value in environment.values()):
|
|
25
|
+
raise RuntimeError('Network settings values must be strings')
|
|
26
|
+
resolve_allowed_request_hosts(environ=environment)
|
|
27
|
+
return dict(environment)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _read_network_settings(path: Path) -> dict[str, str]:
|
|
31
|
+
if not path.exists():
|
|
32
|
+
return {}
|
|
33
|
+
payload = json.loads(path.read_text(encoding='utf-8'))
|
|
34
|
+
if (not isinstance(payload, dict) or set(payload) != {'version', 'environment'}
|
|
35
|
+
or type(payload['version']) is not int or payload['version'] != 1):
|
|
36
|
+
raise RuntimeError(f'Invalid network settings file: {path}')
|
|
37
|
+
return _validate_network_environment(payload['environment'])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _write_network_settings(path: Path, environment: dict[str, str]) -> None:
|
|
41
|
+
assert path.name == 'network-settings.json'
|
|
42
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
temporary = path.with_name(f'.network-settings-{uuid4().hex}.tmp')
|
|
44
|
+
try:
|
|
45
|
+
with temporary.open('x', encoding='utf-8') as stream:
|
|
46
|
+
json.dump({'version': 1, 'environment': environment}, stream, indent=2)
|
|
47
|
+
stream.write('\n')
|
|
48
|
+
os.replace(temporary, path)
|
|
49
|
+
finally:
|
|
50
|
+
temporary.unlink(missing_ok=True)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def apply_network_settings(*, environ: MutableMapping[str, str], persist: bool) -> None:
|
|
54
|
+
path = resolve_data_directory(environ=environ) / 'network-settings.json'
|
|
55
|
+
saved = _read_network_settings(path)
|
|
56
|
+
configured = {key: environ[key] for key in _NETWORK_ENVIRONMENT_KEYS if key in environ}
|
|
57
|
+
effective = _validate_network_environment(saved | configured)
|
|
58
|
+
if persist and effective != saved:
|
|
59
|
+
_write_network_settings(path, effective)
|
|
60
|
+
print(f'[startup] Saved network settings: {path}', flush=True)
|
|
61
|
+
environ.update(effective)
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from collections.abc import Mapping
|
|
4
3
|
from dataclasses import dataclass
|
|
5
|
-
import ipaddress
|
|
6
|
-
import re
|
|
7
4
|
from urllib.parse import urlsplit
|
|
8
5
|
|
|
9
6
|
from fastapi import Request
|
|
@@ -12,14 +9,10 @@ from starlette.middleware.base import BaseHTTPMiddleware
|
|
|
12
9
|
|
|
13
10
|
from app.api.request_auth import AUTH_COOKIE_NAME
|
|
14
11
|
from app.services.exception_capture import CapturedExceptionContext
|
|
12
|
+
from app.security.request_hosts import _normalize_hostname, resolve_allowed_request_hosts
|
|
15
13
|
|
|
16
14
|
|
|
17
|
-
_LOOPBACK_REQUEST_HOSTS = frozenset({"127.0.0.1", "::1", "localhost"})
|
|
18
|
-
_WILDCARD_BIND_HOSTS = frozenset({"0.0.0.0", "::"})
|
|
19
15
|
_SAFE_METHODS = frozenset({"GET", "HEAD", "OPTIONS"})
|
|
20
|
-
_DNS_HOSTNAME_PATTERN = re.compile(
|
|
21
|
-
r"^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$"
|
|
22
|
-
)
|
|
23
16
|
|
|
24
17
|
|
|
25
18
|
@dataclass(frozen=True)
|
|
@@ -28,72 +21,6 @@ class RequestBoundaryRejection:
|
|
|
28
21
|
detail: str
|
|
29
22
|
|
|
30
23
|
|
|
31
|
-
def _normalize_hostname(*, hostname: str, context: str) -> str:
|
|
32
|
-
normalized = hostname.strip().casefold().rstrip(".")
|
|
33
|
-
if normalized == "":
|
|
34
|
-
raise ValueError(f"{context} hostname must not be empty")
|
|
35
|
-
|
|
36
|
-
ip_capture = CapturedExceptionContext(ValueError, boundary='app/security/request_boundary.py:_normalize_hostname:ip_capture')
|
|
37
|
-
parsed_ip: ipaddress._BaseAddress | None = None
|
|
38
|
-
with ip_capture:
|
|
39
|
-
parsed_ip = ipaddress.ip_address(normalized)
|
|
40
|
-
if ip_capture.captured_exception is None:
|
|
41
|
-
if parsed_ip is None:
|
|
42
|
-
raise RuntimeError("IP parser returned no address")
|
|
43
|
-
return parsed_ip.compressed.casefold()
|
|
44
|
-
|
|
45
|
-
if _DNS_HOSTNAME_PATTERN.fullmatch(normalized) is None:
|
|
46
|
-
raise ValueError(f"{context} contains an invalid hostname: {hostname!r}")
|
|
47
|
-
return normalized
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def _normalize_configured_hostname(*, raw_value: str) -> str:
|
|
51
|
-
value = raw_value.strip()
|
|
52
|
-
if value == "":
|
|
53
|
-
raise RuntimeError("METALIST_ALLOWED_HOSTS contains an empty entry")
|
|
54
|
-
if value == "*":
|
|
55
|
-
raise RuntimeError("METALIST_ALLOWED_HOSTS must not contain '*'")
|
|
56
|
-
if "://" in value or "/" in value or "@" in value:
|
|
57
|
-
raise RuntimeError(
|
|
58
|
-
"METALIST_ALLOWED_HOSTS entries must be hostnames without schemes, ports, or paths"
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
unbracketed_value = value
|
|
62
|
-
if value.startswith("[") and value.endswith("]"):
|
|
63
|
-
unbracketed_value = value[1:-1]
|
|
64
|
-
normalize_capture = CapturedExceptionContext(ValueError, boundary='app/security/request_boundary.py:_normalize_configured_hostname:normalize_capture')
|
|
65
|
-
normalized_hostname: str | None = None
|
|
66
|
-
with normalize_capture:
|
|
67
|
-
normalized_hostname = _normalize_hostname(
|
|
68
|
-
hostname=unbracketed_value,
|
|
69
|
-
context="METALIST_ALLOWED_HOSTS",
|
|
70
|
-
)
|
|
71
|
-
if normalize_capture.captured_exception is not None:
|
|
72
|
-
exc = normalize_capture.captured_exception
|
|
73
|
-
raise RuntimeError(str(exc)) from exc
|
|
74
|
-
if normalized_hostname is None:
|
|
75
|
-
raise RuntimeError("METALIST_ALLOWED_HOSTS parser returned no hostname")
|
|
76
|
-
return normalized_hostname
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def resolve_allowed_request_hosts(*, environ: Mapping[str, str]) -> frozenset[str]:
|
|
80
|
-
allowed_hosts = set(_LOOPBACK_REQUEST_HOSTS)
|
|
81
|
-
bind_host = environ.get("METALIST_HOST", "127.0.0.1").strip()
|
|
82
|
-
if bind_host == "":
|
|
83
|
-
raise RuntimeError("METALIST_HOST must not be empty")
|
|
84
|
-
normalized_bind_host = _normalize_configured_hostname(raw_value=bind_host)
|
|
85
|
-
if normalized_bind_host not in _WILDCARD_BIND_HOSTS:
|
|
86
|
-
allowed_hosts.add(normalized_bind_host)
|
|
87
|
-
|
|
88
|
-
configured_hosts = environ.get("METALIST_ALLOWED_HOSTS")
|
|
89
|
-
if configured_hosts is not None:
|
|
90
|
-
if configured_hosts.strip() == "":
|
|
91
|
-
raise RuntimeError("METALIST_ALLOWED_HOSTS must not be empty when set")
|
|
92
|
-
for raw_host in configured_hosts.split(","):
|
|
93
|
-
allowed_hosts.add(_normalize_configured_hostname(raw_value=raw_host))
|
|
94
|
-
return frozenset(allowed_hosts)
|
|
95
|
-
|
|
96
|
-
|
|
97
24
|
def _parse_host_header(host_header: str) -> tuple[str, int | None] | None:
|
|
98
25
|
raw_host = host_header.strip()
|
|
99
26
|
if raw_host == "":
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Host validation shared by early launch settings and HTTP request checks."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
import ipaddress
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from app.services.exception_capture import CapturedExceptionContext
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
_LOOPBACK_REQUEST_HOSTS = frozenset({"127.0.0.1", "::1", "localhost"})
|
|
12
|
+
_WILDCARD_BIND_HOSTS = frozenset({"0.0.0.0", "::"})
|
|
13
|
+
_DNS_HOSTNAME_PATTERN = re.compile(
|
|
14
|
+
r"^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _normalize_hostname(*, hostname: str, context: str) -> str:
|
|
19
|
+
normalized = hostname.strip().casefold().rstrip(".")
|
|
20
|
+
if normalized == "":
|
|
21
|
+
raise ValueError(f"{context} hostname must not be empty")
|
|
22
|
+
|
|
23
|
+
ip_capture = CapturedExceptionContext(ValueError, boundary='app/security/request_hosts.py:_normalize_hostname:ip_capture')
|
|
24
|
+
parsed_ip: ipaddress._BaseAddress | None = None
|
|
25
|
+
with ip_capture:
|
|
26
|
+
parsed_ip = ipaddress.ip_address(normalized)
|
|
27
|
+
if ip_capture.captured_exception is None:
|
|
28
|
+
if parsed_ip is None:
|
|
29
|
+
raise RuntimeError("IP parser returned no address")
|
|
30
|
+
return parsed_ip.compressed.casefold()
|
|
31
|
+
|
|
32
|
+
if _DNS_HOSTNAME_PATTERN.fullmatch(normalized) is None:
|
|
33
|
+
raise ValueError(f"{context} contains an invalid hostname: {hostname!r}")
|
|
34
|
+
return normalized
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _normalize_configured_hostname(*, raw_value: str) -> str:
|
|
38
|
+
value = raw_value.strip()
|
|
39
|
+
if value == "":
|
|
40
|
+
raise RuntimeError("METALIST_ALLOWED_HOSTS contains an empty entry")
|
|
41
|
+
if value == "*":
|
|
42
|
+
raise RuntimeError("METALIST_ALLOWED_HOSTS must not contain '*'")
|
|
43
|
+
if "://" in value or "/" in value or "@" in value:
|
|
44
|
+
raise RuntimeError(
|
|
45
|
+
"METALIST_ALLOWED_HOSTS entries must be hostnames without schemes, ports, or paths"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
unbracketed_value = value
|
|
49
|
+
if value.startswith("[") and value.endswith("]"):
|
|
50
|
+
unbracketed_value = value[1:-1]
|
|
51
|
+
normalize_capture = CapturedExceptionContext(ValueError, boundary='app/security/request_hosts.py:_normalize_configured_hostname:normalize_capture')
|
|
52
|
+
normalized_hostname: str | None = None
|
|
53
|
+
with normalize_capture:
|
|
54
|
+
normalized_hostname = _normalize_hostname(
|
|
55
|
+
hostname=unbracketed_value,
|
|
56
|
+
context="METALIST_ALLOWED_HOSTS",
|
|
57
|
+
)
|
|
58
|
+
if normalize_capture.captured_exception is not None:
|
|
59
|
+
exc = normalize_capture.captured_exception
|
|
60
|
+
raise RuntimeError(str(exc)) from exc
|
|
61
|
+
if normalized_hostname is None:
|
|
62
|
+
raise RuntimeError("METALIST_ALLOWED_HOSTS parser returned no hostname")
|
|
63
|
+
return normalized_hostname
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def resolve_allowed_request_hosts(*, environ: Mapping[str, str]) -> frozenset[str]:
|
|
67
|
+
allowed_hosts = set(_LOOPBACK_REQUEST_HOSTS)
|
|
68
|
+
bind_host = environ.get("METALIST_HOST", "127.0.0.1").strip()
|
|
69
|
+
if bind_host == "":
|
|
70
|
+
raise RuntimeError("METALIST_HOST must not be empty")
|
|
71
|
+
normalized_bind_host = _normalize_configured_hostname(raw_value=bind_host)
|
|
72
|
+
if normalized_bind_host not in _WILDCARD_BIND_HOSTS:
|
|
73
|
+
allowed_hosts.add(normalized_bind_host)
|
|
74
|
+
|
|
75
|
+
configured_hosts = environ.get("METALIST_ALLOWED_HOSTS")
|
|
76
|
+
if configured_hosts is not None:
|
|
77
|
+
if configured_hosts.strip() == "":
|
|
78
|
+
raise RuntimeError("METALIST_ALLOWED_HOSTS must not be empty when set")
|
|
79
|
+
for raw_host in configured_hosts.split(","):
|
|
80
|
+
allowed_hosts.add(_normalize_configured_hostname(raw_value=raw_host))
|
|
81
|
+
return frozenset(allowed_hosts)
|
|
82
|
+
|
|
83
|
+
|
|
@@ -113,6 +113,8 @@ class NoteStore:
|
|
|
113
113
|
self._ordering = NoteOrdering()
|
|
114
114
|
self._effective_non_meta_tag_terms: Dict[str, FrozenSet[str]] = {}
|
|
115
115
|
self._effective_proposed_non_meta_tag_terms: Dict[str, FrozenSet[str]] = {}
|
|
116
|
+
self._subtree_non_meta_tag_terms: Dict[str, FrozenSet[str]] = {}
|
|
117
|
+
self._subtree_proposed_non_meta_tag_terms: Dict[str, FrozenSet[str]] = {}
|
|
116
118
|
self._loaded = False
|
|
117
119
|
self._revision = 0
|
|
118
120
|
self._timing_enabled = True
|
|
@@ -120,17 +122,20 @@ class NoteStore:
|
|
|
120
122
|
def _get_children_locked(self, parent_id: Optional[str]) -> List[str]:
|
|
121
123
|
return self._ordering.children(self._note_map, parent_id)
|
|
122
124
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
dependencies.add(record.parent_id)
|
|
129
|
-
return dependencies
|
|
125
|
+
def _reference_targets_locked(self, note_id: str) -> set[str]:
|
|
126
|
+
return set(self._backlink_index.get_target_ids(note_id)) & self._note_map.keys()
|
|
127
|
+
|
|
128
|
+
def _referrers_locked(self, note_id: str) -> set[str]:
|
|
129
|
+
return self._backlink_index.get_counts(note_id).keys() & self._note_map.keys()
|
|
130
130
|
|
|
131
|
-
def
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
def _inherited_tag_terms_locked(
|
|
132
|
+
self, note_id: str, effective: Dict[str, FrozenSet[str]], subtree: Dict[str, FrozenSet[str]],
|
|
133
|
+
) -> FrozenSet[str]:
|
|
134
|
+
terms = frozenset().union(*(subtree[target] for target in self._reference_targets_locked(note_id)))
|
|
135
|
+
parent_id = self._note_map[note_id].parent_id
|
|
136
|
+
if parent_id is not None:
|
|
137
|
+
terms |= effective[parent_id]
|
|
138
|
+
return terms
|
|
134
139
|
|
|
135
140
|
def _raw_tag_terms_locked(self, note_id: str) -> FrozenSet[str]:
|
|
136
141
|
record = self._note_map[note_id]
|
|
@@ -153,20 +158,46 @@ class NoteStore:
|
|
|
153
158
|
raise RuntimeError("Integrity failure: unreachable notes during tag inheritance")
|
|
154
159
|
self._effective_non_meta_tag_terms.clear()
|
|
155
160
|
self._effective_proposed_non_meta_tag_terms.clear()
|
|
161
|
+
self._subtree_non_meta_tag_terms.clear()
|
|
162
|
+
self._subtree_proposed_non_meta_tag_terms.clear()
|
|
163
|
+
self._propagate_subtree_tag_terms_locked(visited)
|
|
156
164
|
return self._propagate_inherited_tag_terms_locked(visited)
|
|
157
165
|
|
|
158
|
-
def _recompute_effective_tag_terms_locked(
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
def _recompute_effective_tag_terms_locked(
|
|
167
|
+
self, root_ids: Iterable[str], *, subtree_root_ids: Iterable[str],
|
|
168
|
+
) -> Dict[str, FrozenSet[str]]:
|
|
169
|
+
roots = set(root_ids)
|
|
170
|
+
# Subtree contributions flow upward and across references, never through
|
|
171
|
+
# inherited parent tags. Include former parents after moves/deletions.
|
|
172
|
+
subtree_affected: set[str] = set()
|
|
173
|
+
pending = list(roots | set(subtree_root_ids))
|
|
174
|
+
effective_roots = set(roots)
|
|
175
|
+
while pending:
|
|
176
|
+
note_id = pending.pop()
|
|
177
|
+
assert note_id in self._note_map
|
|
178
|
+
if note_id in subtree_affected:
|
|
179
|
+
continue
|
|
180
|
+
subtree_affected.add(note_id)
|
|
181
|
+
referrers = self._referrers_locked(note_id)
|
|
182
|
+
effective_roots.update(referrers)
|
|
183
|
+
pending.extend(referrers - subtree_affected)
|
|
184
|
+
parent_id = self._note_map[note_id].parent_id
|
|
185
|
+
if parent_id is not None:
|
|
186
|
+
pending.append(parent_id)
|
|
187
|
+
self._assert_acyclic_parent_paths_locked(subtree_affected)
|
|
188
|
+
self._propagate_subtree_tag_terms_locked(subtree_affected)
|
|
189
|
+
|
|
190
|
+
# Ordinary inheritance flows down from changed notes and referrers only;
|
|
191
|
+
# siblings of an edited source descendant need no search-index update.
|
|
161
192
|
affected: set[str] = set()
|
|
162
|
-
pending = list(
|
|
193
|
+
pending = list(effective_roots)
|
|
163
194
|
while pending:
|
|
164
195
|
note_id = pending.pop()
|
|
165
196
|
assert note_id in self._note_map
|
|
166
197
|
if note_id in affected:
|
|
167
198
|
continue
|
|
168
199
|
affected.add(note_id)
|
|
169
|
-
pending.extend(self.
|
|
200
|
+
pending.extend(set(self._get_children_locked(note_id)) - affected)
|
|
170
201
|
self._assert_acyclic_parent_paths_locked(affected)
|
|
171
202
|
return self._propagate_inherited_tag_terms_locked(affected)
|
|
172
203
|
|
|
@@ -183,39 +214,64 @@ class NoteStore:
|
|
|
183
214
|
visited.update(path)
|
|
184
215
|
|
|
185
216
|
def _propagate_inherited_tag_terms_locked(self, affected: set[str]) -> Dict[str, FrozenSet[str]]:
|
|
186
|
-
|
|
217
|
+
dependencies = {}
|
|
187
218
|
for note_id in affected:
|
|
188
219
|
record = self._note_map[note_id]
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
220
|
+
targets = self._reference_targets_locked(note_id)
|
|
221
|
+
self._effective_non_meta_tag_terms[note_id] = record.non_meta_tag_terms | frozenset().union(
|
|
222
|
+
*(self._subtree_non_meta_tag_terms[target] for target in targets))
|
|
223
|
+
self._effective_proposed_non_meta_tag_terms[note_id] = (
|
|
224
|
+
record.proposed_non_meta_tag_terms | frozenset().union(
|
|
225
|
+
*(self._subtree_proposed_non_meta_tag_terms[target] for target in targets)))
|
|
226
|
+
dependencies[note_id] = set()
|
|
227
|
+
if record.parent_id is not None:
|
|
228
|
+
dependencies[note_id].add(record.parent_id)
|
|
229
|
+
self._propagate_tag_dependencies_locked(
|
|
230
|
+
dependencies, self._effective_non_meta_tag_terms, self._effective_proposed_non_meta_tag_terms)
|
|
231
|
+
return {note_id: self._raw_tag_terms_locked(note_id) for note_id in affected}
|
|
232
|
+
|
|
233
|
+
def _propagate_subtree_tag_terms_locked(self, affected: set[str]) -> None:
|
|
234
|
+
dependencies = {}
|
|
235
|
+
for note_id in affected:
|
|
236
|
+
record = self._note_map[note_id]
|
|
237
|
+
self._subtree_non_meta_tag_terms[note_id] = record.non_meta_tag_terms
|
|
238
|
+
self._subtree_proposed_non_meta_tag_terms[note_id] = record.proposed_non_meta_tag_terms
|
|
239
|
+
dependencies[note_id] = (set(self._get_children_locked(note_id))
|
|
240
|
+
| self._reference_targets_locked(note_id))
|
|
241
|
+
self._propagate_tag_dependencies_locked(
|
|
242
|
+
dependencies, self._subtree_non_meta_tag_terms, self._subtree_proposed_non_meta_tag_terms)
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def _propagate_tag_dependencies_locked(
|
|
246
|
+
dependencies: Dict[str, set[str]],
|
|
247
|
+
accepted_terms: Dict[str, FrozenSet[str]], proposed_terms: Dict[str, FrozenSet[str]],
|
|
248
|
+
) -> None:
|
|
249
|
+
# Callers reset the affected closure to direct contributions first, so
|
|
250
|
+
# cycles converge to the least fixed point even when tags are removed.
|
|
251
|
+
dependents: Dict[str, set[str]] = {note_id: set() for note_id in dependencies}
|
|
252
|
+
for note_id, sources in dependencies.items():
|
|
253
|
+
assert note_id in accepted_terms and note_id in proposed_terms
|
|
254
|
+
for source_id in sources:
|
|
255
|
+
if source_id in dependencies:
|
|
193
256
|
dependents[source_id].add(note_id)
|
|
194
257
|
else:
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
pending = deque(affected)
|
|
201
|
-
queued = set(affected)
|
|
258
|
+
accepted_terms[note_id] |= accepted_terms[source_id]
|
|
259
|
+
proposed_terms[note_id] |= proposed_terms[source_id]
|
|
260
|
+
pending = deque(dependencies)
|
|
261
|
+
queued = set(dependencies)
|
|
202
262
|
while pending:
|
|
203
263
|
source_id = pending.popleft()
|
|
204
264
|
queued.remove(source_id)
|
|
205
265
|
for note_id in dependents[source_id]:
|
|
206
|
-
accepted =
|
|
207
|
-
|
|
208
|
-
proposed
|
|
209
|
-
| self._effective_proposed_non_meta_tag_terms[source_id])
|
|
210
|
-
if (accepted == self._effective_non_meta_tag_terms[note_id]
|
|
211
|
-
and proposed == self._effective_proposed_non_meta_tag_terms[note_id]):
|
|
266
|
+
accepted = accepted_terms[note_id] | accepted_terms[source_id]
|
|
267
|
+
proposed = proposed_terms[note_id] | proposed_terms[source_id]
|
|
268
|
+
if accepted == accepted_terms[note_id] and proposed == proposed_terms[note_id]:
|
|
212
269
|
continue
|
|
213
|
-
|
|
214
|
-
|
|
270
|
+
accepted_terms[note_id] = accepted
|
|
271
|
+
proposed_terms[note_id] = proposed
|
|
215
272
|
if note_id not in queued:
|
|
216
273
|
pending.append(note_id)
|
|
217
274
|
queued.add(note_id)
|
|
218
|
-
return {note_id: self._raw_tag_terms_locked(note_id) for note_id in affected}
|
|
219
275
|
|
|
220
276
|
def _publish_tag_updates(self, raw_terms_by_id: Dict[str, FrozenSet[str]]) -> None:
|
|
221
277
|
if not raw_terms_by_id:
|
|
@@ -242,6 +298,8 @@ class NoteStore:
|
|
|
242
298
|
self._ordering = NoteOrdering()
|
|
243
299
|
self._effective_non_meta_tag_terms.clear()
|
|
244
300
|
self._effective_proposed_non_meta_tag_terms.clear()
|
|
301
|
+
self._subtree_non_meta_tag_terms.clear()
|
|
302
|
+
self._subtree_proposed_non_meta_tag_terms.clear()
|
|
245
303
|
self._loaded = False
|
|
246
304
|
search_index.rebuild(
|
|
247
305
|
[],
|
|
@@ -625,7 +683,7 @@ class NoteStore:
|
|
|
625
683
|
self._backlink_index.upsert(record.id, record.content, record.tags)
|
|
626
684
|
self._insert_link(record.parent_id, record.id, record.prev_id, record.next_id)
|
|
627
685
|
|
|
628
|
-
tag_updates = self._recompute_effective_tag_terms_locked({record.id})
|
|
686
|
+
tag_updates = self._recompute_effective_tag_terms_locked({record.id}, subtree_root_ids=())
|
|
629
687
|
effective_tag_terms = tag_updates.pop(record.id)
|
|
630
688
|
|
|
631
689
|
ontology = get_ontology()
|
|
@@ -699,7 +757,8 @@ class NoteStore:
|
|
|
699
757
|
tag_sources_changed = True
|
|
700
758
|
|
|
701
759
|
if tag_sources_changed:
|
|
702
|
-
effective_tag_terms_by_id = self._recompute_effective_tag_terms_locked(
|
|
760
|
+
effective_tag_terms_by_id = self._recompute_effective_tag_terms_locked(
|
|
761
|
+
{note.id}, subtree_root_ids=())
|
|
703
762
|
else:
|
|
704
763
|
effective_tag_terms = self._raw_tag_terms_locked(updated.id)
|
|
705
764
|
|
|
@@ -751,7 +810,11 @@ class NoteStore:
|
|
|
751
810
|
self._insert_link(note.parent_id, note.id, note.prev_id, next_id)
|
|
752
811
|
tag_updates = {}
|
|
753
812
|
if record.parent_id != note.parent_id:
|
|
754
|
-
|
|
813
|
+
former_parents = set()
|
|
814
|
+
if record.parent_id is not None:
|
|
815
|
+
former_parents.add(record.parent_id)
|
|
816
|
+
tag_updates = self._recompute_effective_tag_terms_locked(
|
|
817
|
+
{note.id}, subtree_root_ids=former_parents)
|
|
755
818
|
self._publish_tag_updates(tag_updates)
|
|
756
819
|
|
|
757
820
|
def bulk_update_metadata(self, notes: Iterable[SimpleNamespace], *, rebuild: bool) -> None:
|
|
@@ -768,6 +831,7 @@ class NoteStore:
|
|
|
768
831
|
self._revision += 1
|
|
769
832
|
replacements = {}
|
|
770
833
|
moved_ids: Set[str] = set()
|
|
834
|
+
former_parents: Set[str] = set()
|
|
771
835
|
|
|
772
836
|
for note in payload:
|
|
773
837
|
record = self._note_map[note.id]
|
|
@@ -792,6 +856,8 @@ class NoteStore:
|
|
|
792
856
|
replacements[note.id] = updated
|
|
793
857
|
if record.parent_id != updated.parent_id:
|
|
794
858
|
moved_ids.add(note.id)
|
|
859
|
+
if record.parent_id is not None:
|
|
860
|
+
former_parents.add(record.parent_id)
|
|
795
861
|
next_records = dict(self._note_map)
|
|
796
862
|
next_records.update(replacements)
|
|
797
863
|
if moved_ids:
|
|
@@ -801,7 +867,8 @@ class NoteStore:
|
|
|
801
867
|
self._ordering = next_ordering
|
|
802
868
|
|
|
803
869
|
if moved_ids:
|
|
804
|
-
tag_updates = self._recompute_effective_tag_terms_locked(
|
|
870
|
+
tag_updates = self._recompute_effective_tag_terms_locked(
|
|
871
|
+
moved_ids, subtree_root_ids=former_parents)
|
|
805
872
|
|
|
806
873
|
self._publish_tag_updates(tag_updates)
|
|
807
874
|
|
|
@@ -833,9 +900,14 @@ class NoteStore:
|
|
|
833
900
|
self._backlink_index.remove(removed_id)
|
|
834
901
|
self._effective_non_meta_tag_terms.pop(removed_id, None)
|
|
835
902
|
self._effective_proposed_non_meta_tag_terms.pop(removed_id, None)
|
|
903
|
+
self._subtree_non_meta_tag_terms.pop(removed_id, None)
|
|
904
|
+
self._subtree_proposed_non_meta_tag_terms.pop(removed_id, None)
|
|
836
905
|
|
|
906
|
+
former_parents = set()
|
|
907
|
+
if root.parent_id is not None:
|
|
908
|
+
former_parents.add(root.parent_id)
|
|
837
909
|
tag_updates = self._recompute_effective_tag_terms_locked(
|
|
838
|
-
referrer_ids & self._note_map.keys())
|
|
910
|
+
referrer_ids & self._note_map.keys(), subtree_root_ids=former_parents)
|
|
839
911
|
|
|
840
912
|
if removed_ids:
|
|
841
913
|
search_index.remove_many(removed_ids)
|
|
@@ -939,8 +1011,8 @@ class NoteStore:
|
|
|
939
1011
|
if record is None:
|
|
940
1012
|
raise KeyError(f"Note {note_id} not present in NoteStore")
|
|
941
1013
|
|
|
942
|
-
return
|
|
943
|
-
|
|
1014
|
+
return self._inherited_tag_terms_locked(
|
|
1015
|
+
note_id, self._effective_non_meta_tag_terms, self._subtree_non_meta_tag_terms)
|
|
944
1016
|
|
|
945
1017
|
def get_inherited_proposed_non_meta_tag_terms(self, note_id: str) -> FrozenSet[str]:
|
|
946
1018
|
if not isinstance(note_id, str) or not note_id:
|
|
@@ -950,8 +1022,8 @@ class NoteStore:
|
|
|
950
1022
|
raise RuntimeError("NoteStore is not loaded")
|
|
951
1023
|
if note_id not in self._note_map:
|
|
952
1024
|
raise KeyError(f"Note {note_id} not present in NoteStore")
|
|
953
|
-
return
|
|
954
|
-
|
|
1025
|
+
return self._inherited_tag_terms_locked(
|
|
1026
|
+
note_id, self._effective_proposed_non_meta_tag_terms, self._subtree_proposed_non_meta_tag_terms)
|
|
955
1027
|
|
|
956
1028
|
def apply_bulk_tag_sources(self, changes: Mapping[str, tuple[str, str]]) -> None:
|
|
957
1029
|
"""Publish sources together and rebuild inheritance once for the whole pass."""
|
|
@@ -5565,6 +5565,11 @@ body.pref-show-ai-chat .chat-toggle-button {
|
|
|
5565
5565
|
}
|
|
5566
5566
|
|
|
5567
5567
|
/* Loading state styling */
|
|
5568
|
+
#version-info-modal:has([data-update-pending="true"]),
|
|
5569
|
+
#version-info-modal:has([data-update-pending="true"]) * {
|
|
5570
|
+
cursor: wait !important;
|
|
5571
|
+
}
|
|
5572
|
+
|
|
5568
5573
|
body.loading {
|
|
5569
5574
|
cursor: wait !important;
|
|
5570
5575
|
}
|
|
@@ -124,6 +124,10 @@ export class VersionInfoModal extends BaseModal {
|
|
|
124
124
|
const loading = state.loading === true;
|
|
125
125
|
const error = typeof state.error === 'string' ? state.error : '';
|
|
126
126
|
const info = state.info;
|
|
127
|
+
let isUpdating = state.updatePending;
|
|
128
|
+
if (state.updateJob !== null && !['complete', 'failed'].includes(state.updateJob.status)) {
|
|
129
|
+
isUpdating = true;
|
|
130
|
+
}
|
|
127
131
|
|
|
128
132
|
let bodyHtml = '<p class="version-info-status">Loading version info...</p>';
|
|
129
133
|
if (!loading && info !== null) {
|
|
@@ -137,7 +141,7 @@ export class VersionInfoModal extends BaseModal {
|
|
|
137
141
|
<div class="modal-content version-info-modal-content">
|
|
138
142
|
<h3>Version Info</h3>
|
|
139
143
|
${bodyHtml}
|
|
140
|
-
<section class="version-update-section" aria-live="polite">
|
|
144
|
+
<section class="version-update-section" aria-live="polite" data-update-pending="${isUpdating}">
|
|
141
145
|
<p>${escapeHtml(state.updateMessage)}</p>
|
|
142
146
|
${this.buildUpdateControls(state)}
|
|
143
147
|
</section>
|