From 2b86a407bab4a0c438b16db9c3c2de4af727adc6 Mon Sep 17 00:00:00 2001 From: PJ Date: Sat, 3 Oct 2026 22:48:49 +0530 Subject: [PATCH] some more fixes --- CLAUDE.md => AGENTS.md | 1 + package.json | 2 + pnpm-lock.yaml | 18 + simplify/.research/build-tooling.md | 450 ++++++++++ simplify/.research/ci-release-signing.md | 448 ++++++++++ simplify/.research/design-system.md | 399 +++++++++ simplify/.research/docs-conventions.md | 442 ++++++++++ simplify/.research/frontend-utils.md | 450 ++++++++++ simplify/.research/icons-and-primitives.md | 380 ++++++++ simplify/.research/memories-raw.md | 976 +++++++++++++++++++++ simplify/.research/react-components.md | 516 +++++++++++ simplify/.research/repo-facts.md | 65 ++ simplify/.research/rust-core.md | 499 +++++++++++ simplify/.research/rust-google-sync.md | 438 +++++++++ simplify/.research/testing.md | 399 +++++++++ simplify/.research/typesetting-and-text.md | 430 +++++++++ simplify/README.md | 27 + simplify/accounts.md | 481 ++++++++++ simplify/design-system.md | 439 +++++++++ simplify/guidelines/README.md | 31 + simplify/guidelines/app-facts.md | 104 +++ simplify/guidelines/code-style.md | 60 ++ simplify/guidelines/design-language.md | 54 ++ simplify/guidelines/distribution.md | 106 +++ simplify/guidelines/errors-and-feedback.md | 37 + simplify/guidelines/git.md | 29 + simplify/guidelines/platform.md | 80 ++ simplify/guidelines/prose-and-docs.md | 74 ++ simplify/guidelines/working-together.md | 76 ++ simplify/hooks.md | 558 ++++++++++++ simplify/migration.md | 189 ++++ simplify/naming.md | 404 +++++++++ simplify/overview.md | 133 +++ simplify/release.md | 417 +++++++++ simplify/repo-layout.md | 163 ++++ simplify/risks.md | 106 +++ simplify/rust-crates.md | 468 ++++++++++ simplify/testing.md | 408 +++++++++ simplify/toolchain.md | 400 +++++++++ simplify/typesetting.md | 398 +++++++++ simplify/ui-kit.md | 377 ++++++++ src-tauri/Cargo.lock | 254 +++++- src-tauri/Cargo.toml | 2 +- src-tauri/capabilities/default.json | 3 +- src-tauri/src/lib.rs | 10 + src-tauri/src/library.rs | 2 +- src/App.tsx | 17 +- src/components/BackupSettings.tsx | 2 +- src/components/EditorView.tsx | 4 +- src/components/KeyboardShortcuts.tsx | 118 +++ src/components/RowMenu.tsx | 2 +- src/components/Sidebar.tsx | 18 + src/editor/FloatingToolbar.tsx | 57 +- src/editor/extensions.ts | 3 + src/editor/indent.ts | 4 +- src/editor/paste.ts | 23 +- src/export/epub.ts | 17 + src/export/typst.ts | 4 + src/focus.ts | 10 +- src/import/epub.ts | 14 +- src/library.ts | 2 +- src/styles/app.css | 64 +- src/styles/shortcuts.css | 54 ++ 63 files changed, 12172 insertions(+), 44 deletions(-) rename CLAUDE.md => AGENTS.md (90%) create mode 100644 simplify/.research/build-tooling.md create mode 100644 simplify/.research/ci-release-signing.md create mode 100644 simplify/.research/design-system.md create mode 100644 simplify/.research/docs-conventions.md create mode 100644 simplify/.research/frontend-utils.md create mode 100644 simplify/.research/icons-and-primitives.md create mode 100644 simplify/.research/memories-raw.md create mode 100644 simplify/.research/react-components.md create mode 100644 simplify/.research/repo-facts.md create mode 100644 simplify/.research/rust-core.md create mode 100644 simplify/.research/rust-google-sync.md create mode 100644 simplify/.research/testing.md create mode 100644 simplify/.research/typesetting-and-text.md create mode 100644 simplify/README.md create mode 100644 simplify/accounts.md create mode 100644 simplify/design-system.md create mode 100644 simplify/guidelines/README.md create mode 100644 simplify/guidelines/app-facts.md create mode 100644 simplify/guidelines/code-style.md create mode 100644 simplify/guidelines/design-language.md create mode 100644 simplify/guidelines/distribution.md create mode 100644 simplify/guidelines/errors-and-feedback.md create mode 100644 simplify/guidelines/git.md create mode 100644 simplify/guidelines/platform.md create mode 100644 simplify/guidelines/prose-and-docs.md create mode 100644 simplify/guidelines/working-together.md create mode 100644 simplify/hooks.md create mode 100644 simplify/migration.md create mode 100644 simplify/naming.md create mode 100644 simplify/overview.md create mode 100644 simplify/release.md create mode 100644 simplify/repo-layout.md create mode 100644 simplify/risks.md create mode 100644 simplify/rust-crates.md create mode 100644 simplify/testing.md create mode 100644 simplify/toolchain.md create mode 100644 simplify/typesetting.md create mode 100644 simplify/ui-kit.md create mode 100644 src/components/KeyboardShortcuts.tsx create mode 100644 src/styles/shortcuts.css diff --git a/CLAUDE.md b/AGENTS.md similarity index 90% rename from CLAUDE.md rename to AGENTS.md index e4de9e9..74fb1c2 100644 --- a/CLAUDE.md +++ b/AGENTS.md @@ -1,5 +1,6 @@ ## Project Guidelines +- After completing changes, build and install the updated app for manual testing. - Do not call the task done until it is fully complete and tested. - Do not dismiss bug as a pre-existing" issue even if it was present before your change. It does not matter, it's still your responsibility to fix it. When you see a bug, fix it. Don't ignore it. diff --git a/package.json b/package.json index d906e18..8a93150 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,13 @@ }, "dependencies": { "@tauri-apps/api": "^2", + "@tauri-apps/plugin-clipboard-manager": "2.3.3", "@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-process": "^2", "@tauri-apps/plugin-updater": "^2", "@tiptap/core": "^3.27.1", + "@tiptap/extension-list": "3.27.1", "@tiptap/extension-placeholder": "^3.27.1", "@tiptap/pm": "^3.27.1", "@tiptap/react": "^3.27.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ee989e..e016f44 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@tauri-apps/api': specifier: ^2 version: 2.11.1 + '@tauri-apps/plugin-clipboard-manager': + specifier: 2.3.3 + version: 2.3.3 '@tauri-apps/plugin-dialog': specifier: ^2.7.1 version: 2.7.1 @@ -26,6 +29,9 @@ importers: '@tiptap/core': specifier: ^3.27.1 version: 3.27.1(@tiptap/pm@3.27.1) + '@tiptap/extension-list': + specifier: 3.27.1 + version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1) '@tiptap/extension-placeholder': specifier: ^3.27.1 version: 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) @@ -540,6 +546,9 @@ packages: '@tauri-apps/api@2.11.1': resolution: {integrity: sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==} + '@tauri-apps/api@2.12.1': + resolution: {integrity: sha512-DeyFHa3wynpyoqTDikDEDGTJIq4LQ5USfolQGRmGIWT6JMADyxZBTDa5cAdT3tDg73rUXufPaCwN7aXBos4OnQ==} + '@tauri-apps/cli-darwin-arm64@2.11.3': resolution: {integrity: sha512-BxpaM8bsCoXs3wd4WKYhas/G1gs7+r7B+e4WnyRk2GEoVOouJB1hoL6E6YLXZDXbYci6VFdrNnobQwd2uVL4ew==} engines: {node: '>= 10'} @@ -611,6 +620,9 @@ packages: engines: {node: '>= 10'} hasBin: true + '@tauri-apps/plugin-clipboard-manager@2.3.3': + resolution: {integrity: sha512-KnyoTs9gj1yEgDkSPUNjOIOHjJTr5wk8IWcYMOWxYTIJCip6QwlyPW8u2X+6bd6kHM4fAdZNpxoal0gy/TwJbg==} + '@tauri-apps/plugin-dialog@2.7.1': resolution: {integrity: sha512-OK1UBXYt+ojcmxMktzzuyonYIFta8CmAASpX+CA+DTGK24KlHjhYI6x2iOJ/TjZF4N7/ACK1oFmEOjIY9IhzOQ==} @@ -1431,6 +1443,8 @@ snapshots: '@tauri-apps/api@2.11.1': {} + '@tauri-apps/api@2.12.1': {} + '@tauri-apps/cli-darwin-arm64@2.11.3': optional: true @@ -1478,6 +1492,10 @@ snapshots: '@tauri-apps/cli-win32-ia32-msvc': 2.11.3 '@tauri-apps/cli-win32-x64-msvc': 2.11.3 + '@tauri-apps/plugin-clipboard-manager@2.3.3': + dependencies: + '@tauri-apps/api': 2.12.1 + '@tauri-apps/plugin-dialog@2.7.1': dependencies: '@tauri-apps/api': 2.11.1 diff --git a/simplify/.research/build-tooling.md b/simplify/.research/build-tooling.md new file mode 100644 index 0000000..a821441 --- /dev/null +++ b/simplify/.research/build-tooling.md @@ -0,0 +1,450 @@ +# Build and developer tooling across the four apps + +Scope: package.json, lockfiles, vite, tsconfig, index.html, justfiles, scripts, gitignore, nix, +editor config, Cargo profiles, capabilities. Not CI, signing or tests, bar where build leaks in. + +Paths: margin `/Users/pj/Workspace/projects/python/margin`, margin-calendar +`/Users/pj/Workspace/projects/python/margin-caledar`, margin-docs +`/Users/pj/Workspace/projects/rust/margin-editor`, margin-mail +`/Users/pj/Workspace/projects/rust/margin-mail`. Cites below are relative to those roots. + +## Five findings first + +1. **margin-docs cannot install on a fresh clone or on its own CI.** Its `package.json:33` asks for + `"margin-shared": "file:../../python/margin/shared"` but `.github/workflows/ci.yml:19` does one + checkout. The frontend job dies inside `pnpm install --frozen-lockfile` with exit 254; four of + the last five runs failed (run 33308997470, 2026-08-30: `rust: success`, `frontend: failure`). + margin-mail is the only app that solved it, with a second checkout of `priyanshujain/margin` + into `python/margin` (`.github/workflows/ci.yml:26-31`, `release.yml:109`). +2. **The four tsconfigs are three identical files plus one that differs by two lines.** md5 of + margin, margin-calendar and margin-docs `tsconfig.json` is `468c4a26...`; margin-mail differs + only in `target` and `lib`. All four `tsconfig.node.json` are byte identical (`767b2e9a...`). +3. **The three justfiles are one file with the product name swapped**, plus two extra recipes and a + signing block in margin-mail. margin has no justfile at all, so the "every fix ends with + `just install`" rule is unenforceable there. +4. **No prettier, no eslint, no biome, no .editorconfig, no rustfmt.toml, no rust-toolchain file in + any of the four.** Confirmed by search over the repo roots and by grep over each package.json. + The only editor config is `margin/.vscode/extensions.json`, two recommendations, and no sibling + has one. +5. **Nix exists only in margin-calendar** and is a publishing artifact, not a toolchain: it + repackages the released `.deb`. Worth copying per app, but not shared build config. + +## package.json + +### Scripts + +| script | margin | calendar | docs | mail | +| --- | --- | --- | --- | --- | +| `dev` | `vite` | `vite` | `vite` | `vite` | +| `build` | `tsc && vite build` | same | same | same | +| `preview` | `vite preview` | same | same | same | +| `tauri` | `tauri` | same | same | same | +| `dmg` | `tauri build --bundles dmg` (`:11`) | absent | absent | absent | +| `test` | absent | `vitest run` | `vitest run` | `vitest run` | +| `test:watch` | absent | `vitest` | `vitest` | `vitest` | +| `test:ui` | absent | `playwright test` | `playwright test` | `playwright test` | +| `fonts:sync` | `node node_modules/margin-shared/bin/sync-fonts.mjs .` (`:12`) | absent | same (`:15`) | `margin-shared-fonts .` (`:15`) | +| `fonts:check` | same with `--check` (`:13`) | absent | same (`:16`) | `margin-shared-fonts . --check` (`:16`) | + +Two drifts worth folding: margin and margin-docs invoke the font sync by path into `node_modules`, +margin-mail uses the `margin-shared-fonts` bin the package already declares +(`python/margin/shared/package.json:16-18`) and which is linked in all three consumers. The bin form +is the correct one. margin-calendar has no font sync and vendors four files under `public/fonts` +against eighteen in the others, so it is not on the shared face set. + +`license` also drifts: `"SEE LICENSE IN LICENSE"` in margin (`:41`) and mail (`:5`), `"MIT"` in +calendar (`:5`) and docs (`:3`), matching `LicenseRef-FSL-1.1-MIT` and `MIT` respectively in the +Cargo manifests. Consistent within an app, but the four are not on one licence. + +### Version drift (declared spec, then what the lockfile resolved) + +| package | margin | calendar | docs | mail | +| --- | --- | --- | --- | --- | +| react, react-dom | `^19.1.0` -> 19.2.7 | `^19.1.0` -> 19.2.8 | 19.2.8 | 19.2.8 | +| vite | `^7.0.4` -> 7.3.5 | 7.3.6 | 7.3.6 | 7.3.6 | +| typescript | `~5.8.3` -> 5.8.3 | 5.8.3 | 5.8.3 | 5.8.3 | +| @vitejs/plugin-react | `^4.6.0` -> 4.7.0 | 4.7.0 | 4.7.0 | 4.7.0 | +| vitest | absent | `^3.2.4` -> 3.2.7 | 3.2.7 | 3.2.7 | +| @playwright/test | absent | 1.62.1 | 1.62.1 | 1.62.1 | +| zustand | `^5.0.14` -> 5.0.14 | 5.0.14 | 5.0.15 | 5.0.15 | +| @types/react | 19.2.17 | 19.2.18 | 19.2.18 | 19.2.18 | +| @types/react-dom | 19.2.3 | 19.2.4 | 19.2.4 | 19.2.7 | +| @types/node | absent | absent | `^22.20.1` -> 22.20.1 | `^24.0.0` -> 24.13.3 | +| @tauri-apps/api | `^2` -> 2.11.1 | 2.11.1 | 2.11.1 | 2.11.1 | +| @tauri-apps/cli | `^2` -> 2.11.3 | 2.11.4 | 2.11.4 | 2.11.4 | +| plugin-opener | 2.5.4 | 2.5.4 | 2.5.4 | 2.5.5 | +| plugin-process | 2.3.1 | 2.3.1 | 2.3.1 | 2.3.1 | +| plugin-updater | 2.10.1 | 2.10.1 | 2.10.1 | 2.11.0 | +| plugin-dialog | `^2.7.1` -> 2.7.1 | absent | `^2` -> 2.7.2 | absent | +| plugin-notification | absent | absent | absent | 2.4.0 | +| plugin-os | absent | absent | absent | 2.3.2 | +| tiptap | `^3.27.1` -> 3.27.1 | absent | `3.30.2` exact | `^3.31.2` -> 3.31.2 | + +Nothing here is a real incompatibility. Every spec except margin-docs' tiptap is a caret or tilde, +so the drift is purely "when was `pnpm install` last run here": margin is the stale one, a patch +behind on react and vite and two `@types` bumps behind. The one deliberate difference is margin-docs +pinning tiptap exactly at 3.30.2 (`package.json:24-28`) while the others float. + +`@types/node` is the only genuine split: 22 in docs, 24 in mail, absent in the other two. Since +`tsconfig.json` in all four sets no `types` array, the presence of `@types/node` silently changes +what global names typecheck per app. + +No app declares a `packageManager` field, so nothing pins pnpm from the repo itself. + +## The margin-shared relative path + +- margin: `"margin-shared": "file:./shared"` (`package.json:26`), inside its own repo, and the + directory is tracked (26 files under `shared/`). +- margin-docs: `"file:../../python/margin/shared"` (`package.json:33`). +- margin-mail: `"file:../../python/margin/shared"` (`package.json:27`). + +The lockfiles record the literal relative string, with no integrity hash: +`rust/margin-mail/pnpm-lock.yaml:918` is `margin-shared@file:../../python/margin/shared:` with +`resolution: {directory: ../../python/margin/shared, type: directory}` and the snapshot at +`:1957` is `{}`. Same shape at `rust/margin-editor/pnpm-lock.yaml:1449`. + +How fragile: pnpm resolves the path relative to the importer directory, so the dependency is not +"the margin repo", it is "two directories up, then `python/margin/shared`". That encodes PJ's local +grouping (`Workspace/projects/python`, `Workspace/projects/rust`) into a committed manifest. + +- **Fresh clone.** Cloning margin-mail into `~/code/margin-mail` makes the target + `/Users/pj/python/margin/shared`. `pnpm install` then stops with + `ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND Could not install from "..." as it does not exist.` + (reproduced directly, exit non-zero, nothing installed). This is not a warning that degrades to a + missing font, it is a hard install failure before any other dependency lands. +- **CI.** margin-mail works only because `ci.yml:26-31` checks the sibling out at the exact path + `python/margin` and runs everything with `working-directory: rust/margin-mail`. margin-docs does + not do this and its frontend job has been red since the dependency landed. +- **Anyone else.** A contributor must clone two repositories into a two-level layout whose folder + names (`python`, `rust`) mean nothing to them and appear in no documentation. The margin repo is + public, so it is possible, just undiscoverable. +- **Reproducibility.** A directory dependency has no hash, so `pnpm install --frozen-lockfile` + consumes whatever is in `shared/` at that moment, uncommitted edits included. The lockfile is not + frozen with respect to shared code. +- **Publishing.** `shared/package.json:4` is `"private": true`, so today it cannot go to a registry + without a deliberate change. + +Three ways out, in order of how much they cost: + +1. **Give shared its own repo and depend on a git tag.** Removes the path assumption entirely and + gets an immutable resolution. npm and pnpm git dependencies cannot point at a subdirectory, so + this means moving `shared/` out of the margin repo, which also fixes margin depending on it via + `file:./shared`. +2. **Publish `margin-shared` to npm** (or a GitHub npm registry) and depend on a version. Same + benefit, plus a real integrity hash in the lockfile. Costs a publish step per change to shared. +3. **Keep the relative path but make it discoverable and enforced:** an `.env`-style documented + layout, a preinstall check that fails with a readable message instead of pnpm's error, and the + second checkout added to margin-docs CI. This is the cheap fix and it leaves the reproducibility + hole open. + +If a shared toolchain package is going to exist anyway, it should be delivered the same way as +whatever is chosen here, and the two should not use different mechanisms. + +## pnpm and workspaces + +All four lockfiles are `lockfileVersion: '9.0'` (line 1) with identical settings blocks +(`autoInstallPeers: true`, `excludeLinksFromLockfile: false`). No `pnpm-workspace.yaml` and no +`.npmrc` in any of the four. There is no workspace today and no way to create one across four git +repos without either submodules or a monorepo merge. + +Local installs all report `packageManager: pnpm@10.12.4` in `node_modules/.modules.yaml`, which is +install state rather than a committed pin; CI pins `pnpm/action-setup@v6` `version: 10` and node 26. + +## vite.config.ts + +Ports, which are the one thing that must stay per app and are correctly staggered: + +| app | server.port | hmr.port | tauri devUrl | +| --- | --- | --- | --- | +| margin | 1420 (`:17`) | 1421 (`:23`) | `http://localhost:1420` | +| calendar | 1430 (`:13`) | 1431 (`:20`) | `http://localhost:1430` | +| docs | 1440 (`:23`) | 1441 (`:30`) | `http://localhost:1440` | +| mail | 1450 (`:16`) | 1451 (`:22`) | `http://localhost:1450` | + +Everything else in the file is the same four properties: `plugins: [react()]`, +`clearScreen: false`, `strictPort: true`, `host: host || false` where `host` is +`process.env.TAURI_DEV_HOST` behind a `@ts-expect-error` comment in all four (`:4-5` in each), the +same conditional `hmr` block, and `watch.ignored`. + +Real differences: + +- margin imports `defineConfig` from `"vite"` (`:1`) and exports an async factory, + `defineConfig(async () => ({ ... }))` (`:8`), for no reason visible in the file. The other three + import from `"vitest/config"` and export a plain object, because they carry a `test` block. +- margin ignores `"**/website/**"` as well as src-tauri (`:29`); the others ignore only src-tauri. +- margin-docs is the only one with a `build` block: `assetsInlineLimit` as a function that returns + `false` for `woff2?|ttf|otf|eot` (`:19`), because the app CSP is `font-src 'self'` and a data URI + font would be refused. +- The `test` blocks: calendar and mail are identical (`include: ["src/**/*.test.ts"]`, + `environment: "node"`); docs adds `maxWorkers: "50%"`, `testTimeout: 30_000`, + `hookTimeout: 30_000`, `teardownTimeout: 30_000` (`:57-69`). + +Nothing in any of the four sets `define`, `envPrefix`, `resolve.alias`, `build.target`, `minify` or +`sourcemap`. So there is no alias story to preserve and no env prefix convention to standardise. + +## tsconfig.json and tsconfig.node.json + +`tsconfig.json` is identical in margin, calendar and docs. margin-mail differs in exactly two +options: + +- `"target": "ES2022"` versus `"ES2020"` (`rust/margin-mail/tsconfig.json:3`) +- `"lib": ["ES2022", "DOM", "DOM.Iterable"]` versus `["ES2020", ...]` (`:5`) + +Everything else matches across all four: `useDefineForClassFields`, `module: "ESNext"`, +`skipLibCheck`, `moduleResolution: "bundler"`, `allowImportingTsExtensions`, `resolveJsonModule`, +`isolatedModules`, `noEmit`, `jsx: "react-jsx"`, `strict`, `noUnusedLocals`, `noUnusedParameters`, +`noFallthroughCasesInSwitch`, `include: ["src"]`, and a reference to `./tsconfig.node.json`. + +`tsconfig.node.json` is byte identical in all four: `composite`, `skipLibCheck`, `module: ESNext`, +`moduleResolution: bundler`, `allowSyntheticDefaultImports`, `include: ["vite.config.ts"]`. + +`tests/tsconfig.json` exists in calendar, docs and mail (margin has no tests directory). Calendar +and docs are byte identical. margin-mail adds `"types": ["node"]` (`:16-18`) and is written with +one array element per line, which is a formatting drift nothing enforces. + +Nothing runs `tests/tsconfig.json`. `pnpm build` is `tsc && vite build`, and root `tsc` only sees +`include: ["src"]`. No package.json script, justfile recipe or workflow step in any of the three +references it. The Playwright specs are therefore type checked by nobody. + +None of the four sets `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, +`verbatimModuleSyntax` or `paths`. + +## index.html + +Identical structure in all four: ``, `lang="en"`, `charset=UTF-8`, a title, a +`
`, and `