Compare commits
3 Commits
d56c37d9b6
...
ca8555f6ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca8555f6ad | ||
|
|
0685428c90 | ||
|
|
eace0e88c3 |
11
.cargo/config.toml
Normal file
11
.cargo/config.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# TEMPORARY WORKAROUND: whisper-rs-sys and llama-cpp-sys-2 both bundle ggml
|
||||||
|
# statically, causing duplicate symbol errors on MSVC (LNK2005/LNK1169).
|
||||||
|
#
|
||||||
|
# /FORCE:MULTIPLE tells the linker to accept duplicate symbols and pick the
|
||||||
|
# first definition. This is safe for development since both ggml copies are
|
||||||
|
# from recent crate versions with compatible ABIs.
|
||||||
|
#
|
||||||
|
# DO NOT SHIP WITH THIS FLAG. Replace with shared system ggml or process
|
||||||
|
# separation before release. See: input/tasks/kon-ggml-dedup.md (in CORBEL repo)
|
||||||
|
[target.x86_64-pc-windows-msvc]
|
||||||
|
rustflags = ["-C", "link-arg=/FORCE:MULTIPLE"]
|
||||||
@@ -39,3 +39,4 @@ arboard = "3.6.1"
|
|||||||
tauri-plugin-mcp = "0.7.1"
|
tauri-plugin-mcp = "0.7.1"
|
||||||
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio"] }
|
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio"] }
|
||||||
uuid = { version = "1", features = ["v4"] }
|
uuid = { version = "1", features = ["v4"] }
|
||||||
|
chrono = "0.4"
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
{"default":{"identifier":"default","description":"Capability for the main window","local":true,"windows":["main","tasks-float"],"permissions":["core:default","core:window:allow-start-dragging","core:window:allow-set-always-on-top","core:window:allow-minimize","core:window:allow-toggle-maximize","core:window:allow-is-maximized","core:window:allow-close","core:window:allow-hide","core:window:allow-show","core:window:allow-set-focus","opener:default","dialog:default","global-shortcut:allow-register","global-shortcut:allow-unregister"]}}
|
{"default":{"identifier":"default","description":"Capability for the main window","local":true,"windows":["main","tasks-float"],"permissions":["core:default","core:window:allow-start-dragging","core:window:allow-set-always-on-top","core:window:allow-minimize","core:window:allow-toggle-maximize","core:window:allow-is-maximized","core:window:allow-close","core:window:allow-hide","core:window:allow-show","core:window:allow-set-focus","opener:default","dialog:default","global-shortcut:allow-register","global-shortcut:allow-unregister","notification:default"]}}
|
||||||
@@ -2498,6 +2498,204 @@
|
|||||||
"const": "mcp:deny-list-tools",
|
"const": "mcp:deny-list-tools",
|
||||||
"markdownDescription": "Denies the list_tools command without any pre-configured scope."
|
"markdownDescription": "Denies the list_tools command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n\n#### This default permission set includes:\n\n- `allow-is-permission-granted`\n- `allow-request-permission`\n- `allow-notify`\n- `allow-register-action-types`\n- `allow-register-listener`\n- `allow-cancel`\n- `allow-get-pending`\n- `allow-remove-active`\n- `allow-get-active`\n- `allow-check-permissions`\n- `allow-show`\n- `allow-batch`\n- `allow-list-channels`\n- `allow-delete-channel`\n- `allow-create-channel`\n- `allow-permission-state`",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:default",
|
||||||
|
"markdownDescription": "This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n\n#### This default permission set includes:\n\n- `allow-is-permission-granted`\n- `allow-request-permission`\n- `allow-notify`\n- `allow-register-action-types`\n- `allow-register-listener`\n- `allow-cancel`\n- `allow-get-pending`\n- `allow-remove-active`\n- `allow-get-active`\n- `allow-check-permissions`\n- `allow-show`\n- `allow-batch`\n- `allow-list-channels`\n- `allow-delete-channel`\n- `allow-create-channel`\n- `allow-permission-state`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the batch command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-batch",
|
||||||
|
"markdownDescription": "Enables the batch command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the cancel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-cancel",
|
||||||
|
"markdownDescription": "Enables the cancel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the check_permissions command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-check-permissions",
|
||||||
|
"markdownDescription": "Enables the check_permissions command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the create_channel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-create-channel",
|
||||||
|
"markdownDescription": "Enables the create_channel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the delete_channel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-delete-channel",
|
||||||
|
"markdownDescription": "Enables the delete_channel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the get_active command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-get-active",
|
||||||
|
"markdownDescription": "Enables the get_active command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the get_pending command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-get-pending",
|
||||||
|
"markdownDescription": "Enables the get_pending command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the is_permission_granted command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-is-permission-granted",
|
||||||
|
"markdownDescription": "Enables the is_permission_granted command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the list_channels command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-list-channels",
|
||||||
|
"markdownDescription": "Enables the list_channels command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the notify command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-notify",
|
||||||
|
"markdownDescription": "Enables the notify command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the permission_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-permission-state",
|
||||||
|
"markdownDescription": "Enables the permission_state command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the register_action_types command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-register-action-types",
|
||||||
|
"markdownDescription": "Enables the register_action_types command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the register_listener command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-register-listener",
|
||||||
|
"markdownDescription": "Enables the register_listener command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the remove_active command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-remove-active",
|
||||||
|
"markdownDescription": "Enables the remove_active command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the request_permission command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-request-permission",
|
||||||
|
"markdownDescription": "Enables the request_permission command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the show command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-show",
|
||||||
|
"markdownDescription": "Enables the show command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the batch command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-batch",
|
||||||
|
"markdownDescription": "Denies the batch command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the cancel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-cancel",
|
||||||
|
"markdownDescription": "Denies the cancel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the check_permissions command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-check-permissions",
|
||||||
|
"markdownDescription": "Denies the check_permissions command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the create_channel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-create-channel",
|
||||||
|
"markdownDescription": "Denies the create_channel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the delete_channel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-delete-channel",
|
||||||
|
"markdownDescription": "Denies the delete_channel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the get_active command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-get-active",
|
||||||
|
"markdownDescription": "Denies the get_active command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the get_pending command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-get-pending",
|
||||||
|
"markdownDescription": "Denies the get_pending command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the is_permission_granted command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-is-permission-granted",
|
||||||
|
"markdownDescription": "Denies the is_permission_granted command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the list_channels command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-list-channels",
|
||||||
|
"markdownDescription": "Denies the list_channels command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the notify command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-notify",
|
||||||
|
"markdownDescription": "Denies the notify command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the permission_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-permission-state",
|
||||||
|
"markdownDescription": "Denies the permission_state command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the register_action_types command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-register-action-types",
|
||||||
|
"markdownDescription": "Denies the register_action_types command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the register_listener command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-register-listener",
|
||||||
|
"markdownDescription": "Denies the register_listener command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the remove_active command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-remove-active",
|
||||||
|
"markdownDescription": "Denies the remove_active command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the request_permission command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-request-permission",
|
||||||
|
"markdownDescription": "Denies the request_permission command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the show command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-show",
|
||||||
|
"markdownDescription": "Denies the show command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
|
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -2498,6 +2498,204 @@
|
|||||||
"const": "mcp:deny-list-tools",
|
"const": "mcp:deny-list-tools",
|
||||||
"markdownDescription": "Denies the list_tools command without any pre-configured scope."
|
"markdownDescription": "Denies the list_tools command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n\n#### This default permission set includes:\n\n- `allow-is-permission-granted`\n- `allow-request-permission`\n- `allow-notify`\n- `allow-register-action-types`\n- `allow-register-listener`\n- `allow-cancel`\n- `allow-get-pending`\n- `allow-remove-active`\n- `allow-get-active`\n- `allow-check-permissions`\n- `allow-show`\n- `allow-batch`\n- `allow-list-channels`\n- `allow-delete-channel`\n- `allow-create-channel`\n- `allow-permission-state`",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:default",
|
||||||
|
"markdownDescription": "This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n\n#### This default permission set includes:\n\n- `allow-is-permission-granted`\n- `allow-request-permission`\n- `allow-notify`\n- `allow-register-action-types`\n- `allow-register-listener`\n- `allow-cancel`\n- `allow-get-pending`\n- `allow-remove-active`\n- `allow-get-active`\n- `allow-check-permissions`\n- `allow-show`\n- `allow-batch`\n- `allow-list-channels`\n- `allow-delete-channel`\n- `allow-create-channel`\n- `allow-permission-state`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the batch command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-batch",
|
||||||
|
"markdownDescription": "Enables the batch command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the cancel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-cancel",
|
||||||
|
"markdownDescription": "Enables the cancel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the check_permissions command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-check-permissions",
|
||||||
|
"markdownDescription": "Enables the check_permissions command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the create_channel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-create-channel",
|
||||||
|
"markdownDescription": "Enables the create_channel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the delete_channel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-delete-channel",
|
||||||
|
"markdownDescription": "Enables the delete_channel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the get_active command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-get-active",
|
||||||
|
"markdownDescription": "Enables the get_active command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the get_pending command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-get-pending",
|
||||||
|
"markdownDescription": "Enables the get_pending command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the is_permission_granted command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-is-permission-granted",
|
||||||
|
"markdownDescription": "Enables the is_permission_granted command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the list_channels command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-list-channels",
|
||||||
|
"markdownDescription": "Enables the list_channels command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the notify command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-notify",
|
||||||
|
"markdownDescription": "Enables the notify command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the permission_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-permission-state",
|
||||||
|
"markdownDescription": "Enables the permission_state command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the register_action_types command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-register-action-types",
|
||||||
|
"markdownDescription": "Enables the register_action_types command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the register_listener command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-register-listener",
|
||||||
|
"markdownDescription": "Enables the register_listener command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the remove_active command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-remove-active",
|
||||||
|
"markdownDescription": "Enables the remove_active command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the request_permission command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-request-permission",
|
||||||
|
"markdownDescription": "Enables the request_permission command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the show command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:allow-show",
|
||||||
|
"markdownDescription": "Enables the show command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the batch command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-batch",
|
||||||
|
"markdownDescription": "Denies the batch command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the cancel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-cancel",
|
||||||
|
"markdownDescription": "Denies the cancel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the check_permissions command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-check-permissions",
|
||||||
|
"markdownDescription": "Denies the check_permissions command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the create_channel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-create-channel",
|
||||||
|
"markdownDescription": "Denies the create_channel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the delete_channel command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-delete-channel",
|
||||||
|
"markdownDescription": "Denies the delete_channel command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the get_active command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-get-active",
|
||||||
|
"markdownDescription": "Denies the get_active command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the get_pending command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-get-pending",
|
||||||
|
"markdownDescription": "Denies the get_pending command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the is_permission_granted command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-is-permission-granted",
|
||||||
|
"markdownDescription": "Denies the is_permission_granted command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the list_channels command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-list-channels",
|
||||||
|
"markdownDescription": "Denies the list_channels command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the notify command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-notify",
|
||||||
|
"markdownDescription": "Denies the notify command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the permission_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-permission-state",
|
||||||
|
"markdownDescription": "Denies the permission_state command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the register_action_types command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-register-action-types",
|
||||||
|
"markdownDescription": "Denies the register_action_types command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the register_listener command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-register-listener",
|
||||||
|
"markdownDescription": "Denies the register_listener command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the remove_active command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-remove-active",
|
||||||
|
"markdownDescription": "Denies the remove_active command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the request_permission command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-request-permission",
|
||||||
|
"markdownDescription": "Denies the request_permission command without any pre-configured scope."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the show command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "notification:deny-show",
|
||||||
|
"markdownDescription": "Denies the show command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
|
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
282
src-tauri/src/commands/export.rs
Normal file
282
src-tauri/src/commands/export.rs
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::AppState;
|
||||||
|
use kon_storage::{list_subtasks, list_tasks, list_transcripts, TaskRow, TranscriptRow};
|
||||||
|
|
||||||
|
/// Slugify a string for use in filenames.
|
||||||
|
fn slugify(s: &str) -> String {
|
||||||
|
s.to_lowercase()
|
||||||
|
.chars()
|
||||||
|
.map(|c| if c.is_alphanumeric() { c } else { '-' })
|
||||||
|
.collect::<String>()
|
||||||
|
.split('-')
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("-")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a `created_at` timestamp into a filename-safe prefix.
|
||||||
|
/// Expected format: "YYYY-MM-DD HH:MM:SS" → "YYYY-MM-DD_HH-MM-SS"
|
||||||
|
fn timestamp_to_filename(created_at: &str) -> String {
|
||||||
|
created_at
|
||||||
|
.replace(' ', "_")
|
||||||
|
.replace(':', "-")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build Markdown content for a single transcription with YAML frontmatter.
|
||||||
|
fn transcription_to_markdown(t: &TranscriptRow) -> String {
|
||||||
|
let title = t.title.as_deref().unwrap_or("Untitled");
|
||||||
|
let word_count = t.text.split_whitespace().count();
|
||||||
|
format!(
|
||||||
|
"---\ntitle: \"{}\"\ndate: \"{}\"\nduration_seconds: {}\nword_count: {}\n---\n\n{}",
|
||||||
|
title, t.created_at, t.duration, word_count, t.text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build Obsidian-compatible Markdown for a transcription.
|
||||||
|
fn transcription_to_obsidian(t: &TranscriptRow, task_titles: &[String]) -> String {
|
||||||
|
let title = t.title.as_deref().unwrap_or("Untitled");
|
||||||
|
let mut md = format!(
|
||||||
|
"---\ntitle: \"{}\"\ndate: \"{}\"\nduration: {}\ntags: [kon, transcription]\n---\n\n{}",
|
||||||
|
title, t.created_at, t.duration, t.text
|
||||||
|
);
|
||||||
|
|
||||||
|
if !task_titles.is_empty() {
|
||||||
|
md.push_str("\n\n## Extracted Tasks\n\n");
|
||||||
|
for title in task_titles {
|
||||||
|
md.push_str(&format!("- [[{}]]\n", title));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
md
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build Obsidian-compatible Markdown for a task.
|
||||||
|
fn task_to_obsidian(task: &TaskRow, subtasks: &[TaskRow]) -> String {
|
||||||
|
let title = &task.text;
|
||||||
|
let mut md = format!(
|
||||||
|
"---\ntitle: \"{}\"\nstatus: \"{}\"\npriority: \"{}\"\nproject: \"{}\"\ncreated: \"{}\"\nsource_transcription: \"{}\"\n---\n\n{}",
|
||||||
|
title,
|
||||||
|
task.status,
|
||||||
|
task.priority,
|
||||||
|
task.project.as_deref().unwrap_or(""),
|
||||||
|
task.created_at,
|
||||||
|
task.source_transcript_id.as_deref().unwrap_or(""),
|
||||||
|
task.text,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !subtasks.is_empty() {
|
||||||
|
md.push_str("\n\n## Micro-steps\n\n");
|
||||||
|
for st in subtasks {
|
||||||
|
let check = if st.done { "x" } else { " " };
|
||||||
|
md.push_str(&format!("- [{}] {}\n", check, st.text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
md
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a CSV row, escaping fields that contain commas or quotes.
|
||||||
|
fn csv_escape(field: &str) -> String {
|
||||||
|
if field.contains(',') || field.contains('"') || field.contains('\n') {
|
||||||
|
format!("\"{}\"", field.replace('"', "\"\""))
|
||||||
|
} else {
|
||||||
|
field.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export all transcriptions as individual files (Markdown or plain text).
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn export_transcriptions(
|
||||||
|
state: tauri::State<'_, AppState>,
|
||||||
|
directory: String,
|
||||||
|
format: String,
|
||||||
|
) -> Result<u32, String> {
|
||||||
|
let dir = Path::new(&directory);
|
||||||
|
if !dir.exists() {
|
||||||
|
return Err(format!("Directory does not exist: {}", directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
let transcripts = list_transcripts(&state.db, i64::MAX)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
let mut count = 0u32;
|
||||||
|
for t in &transcripts {
|
||||||
|
let prefix = timestamp_to_filename(&t.created_at);
|
||||||
|
let (ext, content) = match format.as_str() {
|
||||||
|
"txt" => ("txt", t.text.clone()),
|
||||||
|
_ => ("md", transcription_to_markdown(t)),
|
||||||
|
};
|
||||||
|
let filename = format!("{}_transcription.{}", prefix, ext);
|
||||||
|
let path = dir.join(&filename);
|
||||||
|
std::fs::write(&path, &content)
|
||||||
|
.map_err(|e| format!("Failed to write {}: {}", filename, e))?;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export all top-level tasks with subtasks as JSON or CSV.
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn export_tasks(
|
||||||
|
state: tauri::State<'_, AppState>,
|
||||||
|
directory: String,
|
||||||
|
format: String,
|
||||||
|
) -> Result<String, String> {
|
||||||
|
let dir = Path::new(&directory);
|
||||||
|
if !dir.exists() {
|
||||||
|
return Err(format!("Directory does not exist: {}", directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
let all_tasks = list_tasks(&state.db).await.map_err(|e| e.to_string())?;
|
||||||
|
let top_level: Vec<&TaskRow> = all_tasks.iter().filter(|t| t.parent_task_id.is_none()).collect();
|
||||||
|
|
||||||
|
let today = chrono::Local::now().format("%Y-%m-%d").to_string();
|
||||||
|
let (filename, content) = match format.as_str() {
|
||||||
|
"csv" => {
|
||||||
|
let mut csv = String::from("id,text,priority,project,status,bucket,effort,done,done_at,created_at,parent_task_id\n");
|
||||||
|
for task in &top_level {
|
||||||
|
csv.push_str(&format!(
|
||||||
|
"{},{},{},{},{},{},{},{},{},{},\n",
|
||||||
|
csv_escape(&task.id),
|
||||||
|
csv_escape(&task.text),
|
||||||
|
csv_escape(&task.priority),
|
||||||
|
csv_escape(task.project.as_deref().unwrap_or("")),
|
||||||
|
csv_escape(&task.status),
|
||||||
|
csv_escape(&task.bucket),
|
||||||
|
csv_escape(task.effort.as_deref().unwrap_or("")),
|
||||||
|
task.done,
|
||||||
|
csv_escape(task.done_at.as_deref().unwrap_or("")),
|
||||||
|
csv_escape(&task.created_at),
|
||||||
|
));
|
||||||
|
// Include subtasks
|
||||||
|
let subs = list_subtasks(&state.db, &task.id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
for st in &subs {
|
||||||
|
csv.push_str(&format!(
|
||||||
|
"{},{},{},{},{},{},{},{},{},{},{}\n",
|
||||||
|
csv_escape(&st.id),
|
||||||
|
csv_escape(&st.text),
|
||||||
|
csv_escape(&st.priority),
|
||||||
|
csv_escape(st.project.as_deref().unwrap_or("")),
|
||||||
|
csv_escape(&st.status),
|
||||||
|
csv_escape(&st.bucket),
|
||||||
|
csv_escape(st.effort.as_deref().unwrap_or("")),
|
||||||
|
st.done,
|
||||||
|
csv_escape(st.done_at.as_deref().unwrap_or("")),
|
||||||
|
csv_escape(&st.created_at),
|
||||||
|
csv_escape(&task.id),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(format!("kon-tasks-{}.csv", today), csv)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// JSON format
|
||||||
|
let mut tasks_json = Vec::new();
|
||||||
|
for task in &top_level {
|
||||||
|
let subs = list_subtasks(&state.db, &task.id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let subtasks_json: Vec<serde_json::Value> = subs
|
||||||
|
.iter()
|
||||||
|
.map(|st| {
|
||||||
|
serde_json::json!({
|
||||||
|
"id": st.id,
|
||||||
|
"text": st.text,
|
||||||
|
"priority": st.priority,
|
||||||
|
"project": st.project,
|
||||||
|
"status": st.status,
|
||||||
|
"bucket": st.bucket,
|
||||||
|
"effort": st.effort,
|
||||||
|
"done": st.done,
|
||||||
|
"done_at": st.done_at,
|
||||||
|
"created_at": st.created_at,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
tasks_json.push(serde_json::json!({
|
||||||
|
"id": task.id,
|
||||||
|
"text": task.text,
|
||||||
|
"priority": task.priority,
|
||||||
|
"project": task.project,
|
||||||
|
"status": task.status,
|
||||||
|
"bucket": task.bucket,
|
||||||
|
"effort": task.effort,
|
||||||
|
"done": task.done,
|
||||||
|
"done_at": task.done_at,
|
||||||
|
"created_at": task.created_at,
|
||||||
|
"notes": task.notes,
|
||||||
|
"source_transcript_id": task.source_transcript_id,
|
||||||
|
"subtasks": subtasks_json,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
let json_str = serde_json::to_string_pretty(&tasks_json)
|
||||||
|
.map_err(|e| format!("JSON serialization failed: {}", e))?;
|
||||||
|
(format!("kon-tasks-{}.json", today), json_str)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = dir.join(&filename);
|
||||||
|
std::fs::write(&path, &content)
|
||||||
|
.map_err(|e| format!("Failed to write {}: {}", filename, e))?;
|
||||||
|
|
||||||
|
Ok(path.to_string_lossy().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Export transcriptions and tasks as Obsidian-compatible Markdown.
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn export_to_obsidian(
|
||||||
|
state: tauri::State<'_, AppState>,
|
||||||
|
directory: String,
|
||||||
|
) -> Result<u32, String> {
|
||||||
|
let dir = Path::new(&directory);
|
||||||
|
if !dir.exists() {
|
||||||
|
return Err(format!("Directory does not exist: {}", directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
let transcripts = list_transcripts(&state.db, i64::MAX)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let all_tasks = list_tasks(&state.db).await.map_err(|e| e.to_string())?;
|
||||||
|
let top_level: Vec<&TaskRow> = all_tasks.iter().filter(|t| t.parent_task_id.is_none()).collect();
|
||||||
|
|
||||||
|
let mut count = 0u32;
|
||||||
|
|
||||||
|
// Export transcriptions
|
||||||
|
for t in &transcripts {
|
||||||
|
// Find tasks sourced from this transcription
|
||||||
|
let linked_titles: Vec<String> = top_level
|
||||||
|
.iter()
|
||||||
|
.filter(|task| task.source_transcript_id.as_deref() == Some(&t.id))
|
||||||
|
.map(|task| task.text.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let content = transcription_to_obsidian(t, &linked_titles);
|
||||||
|
let prefix = timestamp_to_filename(&t.created_at);
|
||||||
|
let filename = format!("{}_transcription.md", prefix);
|
||||||
|
let path = dir.join(&filename);
|
||||||
|
std::fs::write(&path, &content)
|
||||||
|
.map_err(|e| format!("Failed to write {}: {}", filename, e))?;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export tasks
|
||||||
|
for task in &top_level {
|
||||||
|
let subs = list_subtasks(&state.db, &task.id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let content = task_to_obsidian(task, &subs);
|
||||||
|
let slug = slugify(&task.text);
|
||||||
|
let filename = format!("task-{}.md", slug);
|
||||||
|
let path = dir.join(&filename);
|
||||||
|
std::fs::write(&path, &content)
|
||||||
|
.map_err(|e| format!("Failed to write {}: {}", filename, e))?;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
pub mod audio;
|
pub mod audio;
|
||||||
pub mod clipboard;
|
pub mod clipboard;
|
||||||
|
pub mod export;
|
||||||
pub mod hardware;
|
pub mod hardware;
|
||||||
pub mod history;
|
pub mod history;
|
||||||
pub mod llm;
|
pub mod llm;
|
||||||
|
|||||||
@@ -192,6 +192,10 @@ pub fn run() {
|
|||||||
// Hardware
|
// Hardware
|
||||||
commands::hardware::probe_system,
|
commands::hardware::probe_system,
|
||||||
commands::hardware::rank_models,
|
commands::hardware::rank_models,
|
||||||
|
// Export
|
||||||
|
commands::export::export_transcriptions,
|
||||||
|
commands::export::export_tasks,
|
||||||
|
commands::export::export_to_obsidian,
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running Kon");
|
.expect("error while running Kon");
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import ZonePicker from "$lib/components/ZonePicker.svelte";
|
import ZonePicker from "$lib/components/ZonePicker.svelte";
|
||||||
import AccessibilityControls from "$lib/components/AccessibilityControls.svelte";
|
import AccessibilityControls from "$lib/components/AccessibilityControls.svelte";
|
||||||
import { getPreferences, updatePreferences } from "$lib/stores/preferences.svelte.js";
|
import { getPreferences, updatePreferences } from "$lib/stores/preferences.svelte.js";
|
||||||
import { Check, ChevronRight } from "lucide-svelte";
|
import { Check, ChevronRight, Download } from "lucide-svelte";
|
||||||
import { open } from "@tauri-apps/plugin-dialog";
|
import { open } from "@tauri-apps/plugin-dialog";
|
||||||
|
|
||||||
const prefs = getPreferences();
|
const prefs = getPreferences();
|
||||||
@@ -173,6 +173,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Export state ---
|
||||||
|
let exportTranscriptFormat = $state("md");
|
||||||
|
let exportTaskFormat = $state("json");
|
||||||
|
let exportStatus = $state("");
|
||||||
|
let exporting = $state("");
|
||||||
|
|
||||||
|
async function doExport(type) {
|
||||||
|
try {
|
||||||
|
const folder = await open({ directory: true, title: "Select export folder" });
|
||||||
|
if (!folder) return;
|
||||||
|
|
||||||
|
exporting = type;
|
||||||
|
exportStatus = "";
|
||||||
|
|
||||||
|
if (type === "transcriptions") {
|
||||||
|
const count = await invoke("export_transcriptions", { directory: folder, format: exportTranscriptFormat });
|
||||||
|
exportStatus = count === 0 ? "No transcriptions yet" : `Exported ${count} transcription${count === 1 ? "" : "s"} to ${folder}`;
|
||||||
|
} else if (type === "tasks") {
|
||||||
|
const path = await invoke("export_tasks", { directory: folder, format: exportTaskFormat });
|
||||||
|
exportStatus = `Tasks exported to ${path}`;
|
||||||
|
} else if (type === "obsidian") {
|
||||||
|
const count = await invoke("export_to_obsidian", { directory: folder });
|
||||||
|
exportStatus = count === 0 ? "Nothing to export yet" : `Exported ${count} file${count === 1 ? "" : "s"} to ${folder}`;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
exportStatus = typeof err === "string" ? err : "Export failed";
|
||||||
|
} finally {
|
||||||
|
exporting = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Profile management ---
|
// --- Profile management ---
|
||||||
function createProfile() {
|
function createProfile() {
|
||||||
if (!newProfileName.trim()) return;
|
if (!newProfileName.trim()) return;
|
||||||
@@ -621,6 +652,86 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Export -->
|
||||||
|
<div class="border-b border-border-subtle">
|
||||||
|
<button
|
||||||
|
class="flex items-center justify-between w-full py-4 px-5 text-left"
|
||||||
|
onclick={() => openSection = openSection === 'export' ? null : 'export'}
|
||||||
|
>
|
||||||
|
<h3 class="font-display text-[18px] italic text-text">Export Your Data</h3>
|
||||||
|
<span class="text-text-tertiary text-[16px] leading-none">{openSection === 'export' ? '−' : '+'}</span>
|
||||||
|
</button>
|
||||||
|
{#if openSection === 'export'}
|
||||||
|
<div class="px-5 pb-5 animate-fade-in">
|
||||||
|
<p class="text-[11px] text-text-tertiary mb-4">All your data stays yours. Export everything as plain text files.</p>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<!-- Export Transcriptions -->
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
class="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-[12px] font-medium
|
||||||
|
bg-accent/10 text-accent hover:bg-accent/20 disabled:opacity-50"
|
||||||
|
disabled={exporting === "transcriptions"}
|
||||||
|
onclick={() => doExport("transcriptions")}
|
||||||
|
>
|
||||||
|
<Download class="w-3.5 h-3.5" />
|
||||||
|
{exporting === "transcriptions" ? "Exporting..." : "Export Transcriptions"}
|
||||||
|
</button>
|
||||||
|
<select
|
||||||
|
class="bg-bg-input border border-border-subtle rounded-lg px-2 py-1 text-[11px] text-text-secondary
|
||||||
|
focus:outline-none focus:border-accent appearance-none cursor-pointer"
|
||||||
|
bind:value={exportTranscriptFormat}
|
||||||
|
>
|
||||||
|
<option value="md">Markdown</option>
|
||||||
|
<option value="txt">Plain Text</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Export Tasks -->
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
class="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-[12px] font-medium
|
||||||
|
bg-accent/10 text-accent hover:bg-accent/20 disabled:opacity-50"
|
||||||
|
disabled={exporting === "tasks"}
|
||||||
|
onclick={() => doExport("tasks")}
|
||||||
|
>
|
||||||
|
<Download class="w-3.5 h-3.5" />
|
||||||
|
{exporting === "tasks" ? "Exporting..." : "Export Tasks"}
|
||||||
|
</button>
|
||||||
|
<select
|
||||||
|
class="bg-bg-input border border-border-subtle rounded-lg px-2 py-1 text-[11px] text-text-secondary
|
||||||
|
focus:outline-none focus:border-accent appearance-none cursor-pointer"
|
||||||
|
bind:value={exportTaskFormat}
|
||||||
|
>
|
||||||
|
<option value="json">JSON</option>
|
||||||
|
<option value="csv">CSV</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Export to Obsidian -->
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
class="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-[12px] font-medium
|
||||||
|
bg-accent/10 text-accent hover:bg-accent/20 disabled:opacity-50"
|
||||||
|
disabled={exporting === "obsidian"}
|
||||||
|
onclick={() => doExport("obsidian")}
|
||||||
|
>
|
||||||
|
<Download class="w-3.5 h-3.5" />
|
||||||
|
{exporting === "obsidian" ? "Exporting..." : "Export to Obsidian"}
|
||||||
|
</button>
|
||||||
|
<span class="text-[10px] text-text-tertiary">Markdown + wikilinks, both transcriptions and tasks</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if exportStatus}
|
||||||
|
<p class="text-[11px] text-text-secondary mt-3 bg-bg-input rounded-lg px-3 py-2 border border-border-subtle">
|
||||||
|
{exportStatus}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Hotkey -->
|
<!-- Hotkey -->
|
||||||
<div class="border-b border-border-subtle">
|
<div class="border-b border-border-subtle">
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user