Compare commits
58 Commits
7d8d7f6110
...
627d98c199
| Author | SHA1 | Date | |
|---|---|---|---|
| 627d98c199 | |||
| 5db45375f3 | |||
| cb814d94fa | |||
| 4393a40ad2 | |||
| 7e854bd5d1 | |||
| 1c664523a6 | |||
| ebaa428f05 | |||
| 97e9b26251 | |||
| 73beaea077 | |||
| 9a42468cfd | |||
| 2b0023d3e1 | |||
| 790848342c | |||
| f4e79276f0 | |||
| ef1201f753 | |||
| d128ac92dc | |||
| 4432251158 | |||
| 79cd72f24f | |||
| 727ef480c5 | |||
| 2c20ab748e | |||
| cd2c4d3782 | |||
| 507a6f9f9a | |||
| ee36277c21 | |||
| f7a5c21d52 | |||
| e5e24eb7f3 | |||
| 07cfb06ee6 | |||
| 6d414d1592 | |||
| f255f9a5a1 | |||
| 7df435d18d | |||
| ffb83f9c71 | |||
| a4e863751b | |||
| e2b739ec6b | |||
| 18f8ccc62a | |||
| 625be7503d | |||
| 4da563df70 | |||
| f0765c1f42 | |||
| 43e1fb1c79 | |||
| b211342e8a | |||
| f5f1b76bfb | |||
| d5bfefefd1 | |||
| a7d5b58dbf | |||
| 6fa837ab61 | |||
| a3947147d6 | |||
| f9bea7ecaa | |||
| b40c8eae06 | |||
| 14e1f40ecf | |||
| b2b0afdaf6 | |||
| f1c96d66e6 | |||
| e92af02673 | |||
| db20dd5107 | |||
| 56ab395eb2 | |||
| 7671d6e39a | |||
| c8c7814db4 | |||
| 7bbdab340c | |||
| 41393e59bb | |||
| fbd573d9e5 | |||
| 30727b4bee | |||
| c7b7c40137 | |||
| e0e86b9199 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,5 +11,5 @@ npm-debug.log*
|
|||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
src-tauri/target/
|
src-tauri/target/
|
||||||
src-tauri/gen/Cargo.lock
|
src-tauri/gen/
|
||||||
*.lock
|
*.lock
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -1,16 +1,19 @@
|
|||||||
# TextDB
|
# TextDB
|
||||||
|
|
||||||
TextDB is a **non-destructive**, completely offline, local-first text editor with built-in Markdown preview **and local AI conversion**. Its core promise is simple: your edits never overwrite your history. Every manual save is immutable and append-only, and autosaves are kept as a separate draft layer. It can also convert plain text to Markdown using a local Ollama model (recommended: `ministral:3b`). No accounts, no telemetry, no remote fonts, and no outside network calls.
|
TextDB is a **non-destructive**, completely offline, local-first text editor with built-in Markdown preview and local AI writing tools. Its core promise is simple: your edits never overwrite your history. Every manual save is immutable and append-only, and autosaves are kept as a separate draft layer. With a local Ollama model (recommended: `ministral:3b`), it can convert plain text to Markdown, proofread, summarize, translate, rewrite, change style, or run a custom instruction against a whole text or a selected passage. No accounts, no telemetry, no remote fonts, and no outside network calls.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Non-destructive editing: immutable manual saves + safe autosaved drafts.
|
- Non-destructive editing: immutable manual saves + safe autosaved drafts.
|
||||||
- Version history panel with quick restore.
|
- Version history panel with quick restore.
|
||||||
- Markdown preview mode with split view.
|
- Markdown preview mode with split view, printable output, clickable links, and automatic raw URL detection.
|
||||||
- Convert plain text to Markdown manually or via local Ollama (recommended model: `ministral:3b`).
|
- Local AI tools via Ollama for Markdown conversion, proofreading, summarizing, translation, rewriting, style changes, and custom prompts.
|
||||||
|
- AI edits can run on the whole text or just the selected part, and results are written back as drafts.
|
||||||
|
- In-document search with Cmd/Ctrl+F, result highlighting, and next / previous navigation.
|
||||||
|
- Live document stats in the footer: characters, words, sentences, and estimated tokens.
|
||||||
- Sort your texts in nested folders with expandable tree; move items around via right-click.
|
- Sort your texts in nested folders with expandable tree; move items around via right-click.
|
||||||
- Open/import `.txt` and `.md` files; export to `.txt` or print / `.pdf`.
|
- Open/import text files from the file picker or system “Open With”; export to `.txt`, export the database, or print / `.pdf`.
|
||||||
- Settings for theme, text size, and optional display of line numbers in the textarea.
|
- Settings for theme, text size, line numbers, split view, Ollama connection, and editable AI prompt templates.
|
||||||
|
|
||||||
## Use cases
|
## Use cases
|
||||||
|
|
||||||
@@ -19,6 +22,7 @@ TextDB is a **non-destructive**, completely offline, local-first text editor wit
|
|||||||
- **Research notes**: maintain evolving notes with an audit trail of changes.
|
- **Research notes**: maintain evolving notes with an audit trail of changes.
|
||||||
- **Sensitive drafts**: keep local-only writing with immutable history and zero cloud sync.
|
- **Sensitive drafts**: keep local-only writing with immutable history and zero cloud sync.
|
||||||
- **Prompt or snippet library**: store and iterate on reusable text safely.
|
- **Prompt or snippet library**: store and iterate on reusable text safely.
|
||||||
|
- **Offline writing assistant**: run local AI edits without sending text to external services.
|
||||||
|
|
||||||
## Tech stack
|
## Tech stack
|
||||||
|
|
||||||
@@ -40,12 +44,13 @@ npm install
|
|||||||
npm run tauri dev
|
npm run tauri dev
|
||||||
```
|
```
|
||||||
|
|
||||||
For AI based text-to-markdown conversion, install Ollama: `https://ollama.com`
|
For local AI tools, install Ollama: `https://ollama.com`
|
||||||
|
|
||||||
## Autosave + versioning behavior
|
## Autosave + versioning behavior
|
||||||
|
|
||||||
- Typing triggers a debounced autosave (~600ms) that writes to the draft record.
|
- Typing triggers a debounced autosave (~600ms) that writes to the draft record.
|
||||||
- Manual saves (Cmd/Ctrl+S or the button) append a new version and clear any draft.
|
- Manual saves (Cmd/Ctrl+S or the button) append a new version and clear any draft.
|
||||||
|
- AI edits preserve the current text state first when needed, then write the AI result back as a new draft.
|
||||||
- Autosave only overwrites the single draft row; manual versions are never overwritten.
|
- Autosave only overwrites the single draft row; manual versions are never overwritten.
|
||||||
- History shows all manual versions plus the current draft (if any).
|
- History shows all manual versions plus the current draft (if any).
|
||||||
|
|
||||||
@@ -54,4 +59,4 @@ For AI based text-to-markdown conversion, install Ollama: `https://ollama.com`
|
|||||||
- SQLite is loaded via `Database.load("sqlite:text.db")`.
|
- SQLite is loaded via `Database.load("sqlite:text.db")`.
|
||||||
- The database file is stored in the Tauri app data directory.
|
- The database file is stored in the Tauri app data directory.
|
||||||
- The app is fully offline by design: no telemetry, no external fonts, no CDNs.
|
- The app is fully offline by design: no telemetry, no external fonts, no CDNs.
|
||||||
- UI settings (theme, text size, line numbers, sidebar + folder collapse state, last selected text) are stored in localStorage.
|
- UI settings (theme, text size, line numbers, split view, sidebar + folder collapse state, last selected text, and AI prompt templates) are stored in localStorage.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/commands": "^6.10.1",
|
"@codemirror/commands": "^6.10.1",
|
||||||
|
"@codemirror/search": "^6.6.0",
|
||||||
"@codemirror/state": "^6.5.4",
|
"@codemirror/state": "^6.5.4",
|
||||||
"@codemirror/view": "^6.39.12",
|
"@codemirror/view": "^6.39.12",
|
||||||
"@tauri-apps/api": "^2.0.0",
|
"@tauri-apps/api": "^2.0.0",
|
||||||
|
|||||||
142
src-tauri/Cargo.lock
generated
142
src-tauri/Cargo.lock
generated
@@ -1574,9 +1574,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ico"
|
name = "ico"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98"
|
checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"png 0.17.16",
|
"png 0.17.16",
|
||||||
@@ -2244,38 +2244,9 @@ checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2",
|
"block2",
|
||||||
"libc",
|
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-cloud-kit",
|
|
||||||
"objc2-core-data",
|
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
"objc2-core-graphics",
|
"objc2-core-graphics",
|
||||||
"objc2-core-image",
|
|
||||||
"objc2-core-text",
|
|
||||||
"objc2-core-video",
|
|
||||||
"objc2-foundation",
|
|
||||||
"objc2-quartz-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "objc2-cloud-kit"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
"objc2",
|
|
||||||
"objc2-foundation",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "objc2-core-data"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
"objc2",
|
|
||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2303,41 +2274,6 @@ dependencies = [
|
|||||||
"objc2-io-surface",
|
"objc2-io-surface",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "objc2-core-image"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006"
|
|
||||||
dependencies = [
|
|
||||||
"objc2",
|
|
||||||
"objc2-foundation",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "objc2-core-text"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
"objc2",
|
|
||||||
"objc2-core-foundation",
|
|
||||||
"objc2-core-graphics",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "objc2-core-video"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
"objc2",
|
|
||||||
"objc2-core-foundation",
|
|
||||||
"objc2-core-graphics",
|
|
||||||
"objc2-io-surface",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-encode"
|
name = "objc2-encode"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
@@ -2361,7 +2297,6 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"block2",
|
"block2",
|
||||||
"libc",
|
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
]
|
]
|
||||||
@@ -2377,16 +2312,6 @@ dependencies = [
|
|||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "objc2-javascript-core"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2a1e6550c4caed348956ce3370c9ffeca70bb1dbed4fa96112e7c6170e074586"
|
|
||||||
dependencies = [
|
|
||||||
"objc2",
|
|
||||||
"objc2-core-foundation",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-quartz-core"
|
name = "objc2-quartz-core"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -2399,17 +2324,6 @@ dependencies = [
|
|||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "objc2-security"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.10.0",
|
|
||||||
"objc2",
|
|
||||||
"objc2-core-foundation",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-ui-kit"
|
name = "objc2-ui-kit"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -2434,8 +2348,6 @@ dependencies = [
|
|||||||
"objc2-app-kit",
|
"objc2-app-kit",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
"objc2-javascript-core",
|
|
||||||
"objc2-security",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3074,9 +2986,9 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.28"
|
version = "0.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -3093,7 +3005,6 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@@ -4010,9 +3921,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri"
|
name = "tauri"
|
||||||
version = "2.9.5"
|
version = "2.10.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a3868da5508446a7cd08956d523ac3edf0a8bc20bf7e4038f9a95c2800d2033"
|
checksum = "da77cc00fb9028caf5b5d4650f75e31f1ef3693459dfca7f7e506d1ecef0ba2d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -4061,9 +3972,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-build"
|
name = "tauri-build"
|
||||||
version = "2.5.3"
|
version = "2.5.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17fcb8819fd16463512a12f531d44826ce566f486d7ccd211c9c8cebdaec4e08"
|
checksum = "4bbc990d1dbf57a8e1c7fa2327f2a614d8b757805603c1b9ba5c81bade09fd4d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
@@ -4083,9 +3994,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-codegen"
|
name = "tauri-codegen"
|
||||||
version = "2.5.2"
|
version = "2.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa9844cefcf99554a16e0a278156ae73b0d8680bbc0e2ad1e4287aadd8489cf"
|
checksum = "d4a24476afd977c5d5d169f72425868613d82747916dd29e0a357c84c4bd6d29"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"brotli",
|
"brotli",
|
||||||
@@ -4110,9 +4021,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-macros"
|
name = "tauri-macros"
|
||||||
version = "2.5.2"
|
version = "2.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3764a12f886d8245e66b7ee9b43ccc47883399be2019a61d80cf0f4117446fde"
|
checksum = "d39b349a98dadaffebb73f0a40dcd1f23c999211e5a2e744403db384d0c33de7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -4236,9 +4147,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "2.9.2"
|
version = "2.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87f766fe9f3d1efc4b59b17e7a891ad5ed195fa8d23582abb02e6c9a01137892"
|
checksum = "2826d79a3297ed08cd6ea7f412644ef58e32969504bc4fbd8d7dbeabc4445ea2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cookie",
|
"cookie",
|
||||||
"dpi",
|
"dpi",
|
||||||
@@ -4261,9 +4172,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime-wry"
|
name = "tauri-runtime-wry"
|
||||||
version = "2.9.3"
|
version = "2.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "187a3f26f681bdf028f796ccf57cf478c1ee422c50128e5a0a6ebeb3f5910065"
|
checksum = "e11ea2e6f801d275fdd890d6c9603736012742a1c33b96d0db788c9cdebf7f9e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
@@ -4271,7 +4182,6 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-app-kit",
|
"objc2-app-kit",
|
||||||
"objc2-foundation",
|
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
@@ -4288,9 +4198,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-utils"
|
name = "tauri-utils"
|
||||||
version = "2.8.1"
|
version = "2.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76a423c51176eb3616ee9b516a9fa67fed5f0e78baaba680e44eb5dd2cc37490"
|
checksum = "219a1f983a2af3653f75b5747f76733b0da7ff03069c7a41901a5eb3ace4557d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"brotli",
|
"brotli",
|
||||||
@@ -5001,9 +4911,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-streams"
|
name = "wasm-streams"
|
||||||
version = "0.4.2"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
|
checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -5094,9 +5004,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webkit2gtk"
|
name = "webkit2gtk"
|
||||||
version = "2.0.1"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a"
|
checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"cairo-rs",
|
"cairo-rs",
|
||||||
@@ -5118,9 +5028,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webkit2gtk-sys"
|
name = "webkit2gtk-sys"
|
||||||
version = "2.0.1"
|
version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c"
|
checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"cairo-sys-rs",
|
"cairo-sys-rs",
|
||||||
@@ -5739,9 +5649,9 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wry"
|
name = "wry"
|
||||||
version = "0.53.5"
|
version = "0.54.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "728b7d4c8ec8d81cab295e0b5b8a4c263c0d41a785fb8f8c4df284e5411140a2"
|
checksum = "bb26159b420aa77684589a744ae9a9461a95395b848764ad12290a14d960a11a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"block2",
|
"block2",
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ description = "TextDB"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2", features = [] }
|
tauri-build = { version = "2.5", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
tauri = { version = "2", features = [] }
|
tauri = { version = "2.10", features = [] }
|
||||||
tauri-plugin-clipboard-manager = { version = "2" }
|
tauri-plugin-clipboard-manager = { version = "2" }
|
||||||
tauri-plugin-dialog = { version = "2" }
|
tauri-plugin-dialog = { version = "2" }
|
||||||
tauri-plugin-fs = { version = "2" }
|
tauri-plugin-fs = { version = "2" }
|
||||||
|
|||||||
@@ -63,7 +63,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ext": [
|
"ext": [
|
||||||
"md"
|
"md",
|
||||||
|
"markdown"
|
||||||
],
|
],
|
||||||
"name": "Markdown Document",
|
"name": "Markdown Document",
|
||||||
"description": "Markdown document",
|
"description": "Markdown document",
|
||||||
@@ -71,8 +72,7 @@
|
|||||||
"role": "Editor",
|
"role": "Editor",
|
||||||
"rank": "Default",
|
"rank": "Default",
|
||||||
"contentTypes": [
|
"contentTypes": [
|
||||||
"net.daringfireball.markdown",
|
"net.daringfireball.markdown"
|
||||||
"public.plain-text"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
1507
src/App.tsx
1507
src/App.tsx
File diff suppressed because it is too large
Load Diff
262
src/index.css
262
src/index.css
@@ -135,6 +135,7 @@ body {
|
|||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: var(--bg-elevated);
|
background: var(--bg-elevated);
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
|
caret-color: var(--ink);
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,6 +238,7 @@ body {
|
|||||||
font: inherit;
|
font: inherit;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
background: var(--bg-input);
|
background: var(--bg-input);
|
||||||
|
caret-color: var(--ink);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
@@ -360,6 +362,7 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
font: inherit;
|
font: inherit;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
background: var(--bg-input);
|
background: var(--bg-input);
|
||||||
|
caret-color: var(--ink);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
@@ -489,6 +492,7 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
padding: 6px 2px 8px;
|
padding: 6px 2px 8px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
|
caret-color: var(--ink);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-input:focus {
|
.title-input:focus {
|
||||||
@@ -504,6 +508,11 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-line--secondary {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -591,10 +600,26 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .editor__textarea-wrap--preview {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not([data-theme="light"]) .editor__textarea-wrap--preview {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
.editor__textarea-wrap--split {
|
.editor__textarea-wrap--split {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .editor__textarea-wrap--split {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not([data-theme="light"]) .editor__textarea-wrap--split {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
.editor__textarea-wrap--split .editor__codemirror {
|
.editor__textarea-wrap--split .editor__codemirror {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-right: 1px solid var(--border);
|
border-right: 1px solid var(--border);
|
||||||
@@ -604,6 +629,14 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .editor__textarea-wrap--split .markdown-preview {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not([data-theme="light"]) .editor__textarea-wrap--split .markdown-preview {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
.editor__textarea-wrap:focus-within {
|
.editor__textarea-wrap:focus-within {
|
||||||
border-color: rgba(255, 255, 255, 0.18);
|
border-color: rgba(255, 255, 255, 0.18);
|
||||||
}
|
}
|
||||||
@@ -627,6 +660,16 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-editor.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor,
|
||||||
|
.editor__codemirror .cm-dropCursor {
|
||||||
|
border-left-color: #f5f5f5 !important;
|
||||||
|
border-left-width: 1.6px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-fatCursor {
|
||||||
|
background: rgba(245, 245, 245, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
.editor__codemirror .cm-scroller {
|
.editor__codemirror .cm-scroller {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
@@ -649,6 +692,76 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-panels {
|
||||||
|
background: var(--bg-elevated);
|
||||||
|
color: var(--ink);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-panels-top {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-search {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-search label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-search input,
|
||||||
|
.editor__codemirror .cm-search button {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-search input[type="text"] {
|
||||||
|
min-width: 220px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: var(--bg-input);
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-search button {
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
padding: 8px 10px;
|
||||||
|
background: var(--bg-input);
|
||||||
|
color: var(--ink);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-search button:hover {
|
||||||
|
border-color: var(--accent-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-search [name="close"] {
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-search input[type="checkbox"] {
|
||||||
|
accent-color: var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-searchMatch {
|
||||||
|
background: rgba(214, 214, 96, 0.28);
|
||||||
|
outline: 1px solid rgba(214, 214, 96, 0.38);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__codemirror .cm-searchMatch.cm-searchMatch-selected {
|
||||||
|
background: rgba(233, 216, 112, 0.42);
|
||||||
|
}
|
||||||
|
|
||||||
.markdown-preview {
|
.markdown-preview {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -657,6 +770,14 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .markdown-preview {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not([data-theme="light"]) .markdown-preview {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
.markdown-preview--hidden {
|
.markdown-preview--hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -670,6 +791,19 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
|
|
||||||
.editor__footer-status {
|
.editor__footer-status {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor__stats {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.banner {
|
.banner {
|
||||||
@@ -829,6 +963,10 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal__card--wide {
|
||||||
|
width: min(560px, 92vw);
|
||||||
|
}
|
||||||
|
|
||||||
.modal__title {
|
.modal__title {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
@@ -837,6 +975,37 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal__textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 170px;
|
||||||
|
resize: vertical;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: var(--bg-input);
|
||||||
|
color: var(--ink);
|
||||||
|
caret-color: var(--ink);
|
||||||
|
padding: 12px 14px;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__hint {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__selection-preview {
|
||||||
|
max-height: 180px;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: var(--bg-input);
|
||||||
|
color: var(--ink);
|
||||||
|
padding: 12px 14px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
.modal__actions {
|
.modal__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
@@ -927,6 +1096,8 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
overflow: auto;
|
||||||
z-index: 30;
|
z-index: 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -939,7 +1110,8 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
.settings-panel {
|
.settings-panel {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: min(420px, 92vw);
|
width: min(560px, calc(100vw - 32px));
|
||||||
|
max-height: calc(100vh - 32px);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
background: var(--bg-elevated);
|
background: var(--bg-elevated);
|
||||||
@@ -948,6 +1120,7 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-panel__header {
|
.settings-panel__header {
|
||||||
@@ -960,12 +1133,26 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-panel__body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 4px;
|
||||||
|
margin-right: -4px;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-panel__section {
|
.settings-panel__section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-panel__section--flush {
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-panel__section-title {
|
.settings-panel__section-title {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
@@ -985,6 +1172,7 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: var(--bg-input);
|
background: var(--bg-input);
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
|
caret-color: var(--ink);
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
}
|
}
|
||||||
@@ -1025,10 +1213,82 @@ body:not([data-theme="light"]) .folder-item {
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-panel__status {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel__status--success {
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel__status--error {
|
||||||
|
color: #d36b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-accordion {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--bg-input);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-accordion__toggle {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--ink);
|
||||||
|
padding: 12px 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
font: inherit;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-accordion__indicator {
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-accordion__body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 14px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-accordion__actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
body[data-theme="light"] .editor__textarea-wrap:focus-within {
|
body[data-theme="light"] .editor__textarea-wrap:focus-within {
|
||||||
border-color: var(--accent-strong);
|
border-color: var(--accent-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .editor__codemirror .cm-editor.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor,
|
||||||
|
body[data-theme="light"] .editor__codemirror .cm-dropCursor {
|
||||||
|
border-left-color: #1f1f1f !important;
|
||||||
|
border-left-width: 1.6px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .editor__codemirror .cm-fatCursor {
|
||||||
|
background: rgba(31, 31, 31, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .editor__codemirror .cm-searchMatch {
|
||||||
|
background: rgba(219, 199, 81, 0.3);
|
||||||
|
outline-color: rgba(170, 141, 33, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
body[data-theme="light"] .editor__codemirror .cm-searchMatch.cm-searchMatch-selected {
|
||||||
|
background: rgba(219, 199, 81, 0.44);
|
||||||
|
}
|
||||||
|
|
||||||
.settings-panel__section--row {
|
.settings-panel__section--row {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -434,3 +434,9 @@ export async function deleteTextVersion(promptId: string, versionId: string) {
|
|||||||
[nextId, promptId]
|
[nextId, promptId]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function exportDatabaseSnapshot(destinationPath: string) {
|
||||||
|
const db = await getDb();
|
||||||
|
const escapedPath = destinationPath.replace(/'/g, "''");
|
||||||
|
await db.execute(`VACUUM INTO '${escapedPath}'`);
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,9 +17,14 @@
|
|||||||
"Liberation Mono", "Courier New", monospace;
|
"Liberation Mono", "Courier New", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body:not([data-theme="light"]) .md-root {
|
||||||
|
--md-surface-alt: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
body[data-theme="light"] .md-root {
|
body[data-theme="light"] .md-root {
|
||||||
--md-link-color: #0a66c2;
|
--md-link-color: #0a66c2;
|
||||||
--md-link-hover-color: #004182;
|
--md-link-hover-color: #004182;
|
||||||
|
--md-surface-alt: var(--bg);
|
||||||
--md-code-inline-bg: rgba(0, 0, 0, 0.06);
|
--md-code-inline-bg: rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -179,15 +179,55 @@ export function markdownToHTML(text) {
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
html = html.replace(/\[([^\]]+?)\]\(([^)]+?)\)/g, (_, label, href) => {
|
const renderExternalLink = (label, hrefRaw) => {
|
||||||
const url = safeLink(href);
|
const url = safeLink(hrefRaw);
|
||||||
const tooltip = escapeHtml(href || '');
|
const tooltip = escapeHtml(hrefRaw || '');
|
||||||
if (!url) return label;
|
if (!url) return label;
|
||||||
return `<a class="md-link md-link--external" href="${escapeAttr(
|
return `<a class="md-link md-link--external" href="${escapeAttr(
|
||||||
url
|
url
|
||||||
)}" target="_blank" rel="noreferrer noopener"><span class="md-link__label">${label}</span> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="md-icon md-icon-external"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg><span class="md-link__tooltip">${tooltip}</span></a>`;
|
)}" target="_blank" rel="noreferrer noopener"><span class="md-link__label">${label}</span> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="md-icon md-icon-external"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg><span class="md-link__tooltip">${tooltip}</span></a>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
html = html.replace(/\[([^\]]+?)\]\(([^)]+?)\)/g, (_, label, href) => {
|
||||||
|
return renderExternalLink(label, href);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const autoLinkPlainUrls = (source) => {
|
||||||
|
const protectedChunkRe = /(<a\b[\s\S]*?<\/a>|<code>[\s\S]*?<\/code>|@@CODEBLOCK\d+@@)/g;
|
||||||
|
const isProtectedChunk = /^(<a\b[\s\S]*<\/a>|<code>[\s\S]*<\/code>|@@CODEBLOCK\d+@@)$/;
|
||||||
|
const trimTrailingPunctuation = (value) => {
|
||||||
|
let linked = value;
|
||||||
|
let trailing = '';
|
||||||
|
while (linked.length > 0) {
|
||||||
|
const last = linked[linked.length - 1];
|
||||||
|
if (!/[)\].,!?;:]/.test(last)) break;
|
||||||
|
if (last === ')') {
|
||||||
|
const opens = (linked.match(/\(/g) || []).length;
|
||||||
|
const closes = (linked.match(/\)/g) || []).length;
|
||||||
|
if (closes <= opens) break;
|
||||||
|
}
|
||||||
|
linked = linked.slice(0, -1);
|
||||||
|
trailing = last + trailing;
|
||||||
|
}
|
||||||
|
return { linked, trailing };
|
||||||
|
};
|
||||||
|
|
||||||
|
return source
|
||||||
|
.split(protectedChunkRe)
|
||||||
|
.map((part) => {
|
||||||
|
if (!part || isProtectedChunk.test(part)) return part;
|
||||||
|
return part.replace(/(^|[\s(>])((?:https?:\/\/|www\.)[^\s<]+)/g, (_, lead, rawUrl) => {
|
||||||
|
const { linked, trailing } = trimTrailingPunctuation(rawUrl);
|
||||||
|
if (!linked) return `${lead}${rawUrl}`;
|
||||||
|
const href = /^www\./i.test(linked) ? `https://${linked}` : linked;
|
||||||
|
return `${lead}${renderExternalLink(linked, href)}${trailing}`;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
html = autoLinkPlainUrls(html);
|
||||||
|
|
||||||
// 6) Convert line-breaks to HTML paragraphs and <br /> inside paragraphs
|
// 6) Convert line-breaks to HTML paragraphs and <br /> inside paragraphs
|
||||||
const linesWithHtml = html.split("\n");
|
const linesWithHtml = html.split("\n");
|
||||||
const htmlLines = [];
|
const htmlLines = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user