diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ccaced5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig — https://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..6081c86 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,6 @@ +# Revisions to ignore in `git blame` — bulk reformats with no logic changes. +# Enable locally with: git config blame.ignoreRevsFile .git-blame-ignore-revs +# (GitHub applies it automatically.) + +# Phase E2 — prettier --write across the repo +91c11503a665307c4ce2f5bb5b6a15ef2e16fa75 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..c4dc6c4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,25 @@ +--- +name: Bug report +about: Something isn't working as expected +title: '' +labels: bug +assignees: '' +--- + +**What happened** +A clear description of the bug. + +**Steps to reproduce** 1. 2. 3. + +**Expected behaviour** +What you expected to happen instead. + +**Environment** + +- Build SHA (from the About page): +- Browser / OS: +- Mode: local-only / signed-in + +**Notes** +Screenshots, console errors, or anything else useful. Please do **not** paste +confidential source text. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..245c5e5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for Verbalis +title: '' +labels: enhancement +assignees: '' +--- + +**The problem** +What are you trying to do that Verbalis makes hard today? + +**Proposed solution** +What you'd like to see. Keep in mind Verbalis is local-first and privacy-first — +features should respect that (nothing leaves the device unless the user asks). + +**Alternatives considered** +Any workarounds or other approaches. + +**Notes** +Anything else — prior art in memoQ / Trados / OmegaT, mockups, etc. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..eb6eddd --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ +## Summary + + + +## Changes + + + +- + +## Testing + + + +- [ ] `pnpm typecheck` +- [ ] `pnpm lint` +- [ ] `pnpm test:unit` +- [ ] `pnpm test:e2e` (for UI-facing changes) +- [ ] `pnpm build` + +## Notes + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 64d7fc9..c8d82e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,8 @@ jobs: - run: pnpm install - run: pnpm generate-icons - run: pnpm typecheck + - run: pnpm lint + - run: pnpm format:check - run: pnpm test:unit - run: pnpm build # Regression guard for the cloud path (ROADMAP §3.1): build once with the diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..209e3ef --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..aa70a3f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +dist +dev-dist +coverage +node_modules +pnpm-lock.yaml +src-tauri/target +public/dictionaries +public/corpora diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6c900fd --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": false, + "singleQuote": true, + "trailingComma": "all", + "printWidth": 100, + "tabWidth": 2, + "arrowParens": "always" +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 03adc8d..c5ee1a9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { - "IDX.aI.enableInlineCompletion": true, - "IDX.aI.enableCodebaseIndexing": true -} \ No newline at end of file + "IDX.aI.enableInlineCompletion": true, + "IDX.aI.enableCodebaseIndexing": true +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7387e4f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,60 @@ +# Changelog + +Notable changes to Verbalis. Format loosely follows +[Keep a Changelog](https://keepachangelog.com/). + +## [Unreleased] — polish pass ("Snow Leopard") + +A refinement release: correctness, coherence, and repo standards — no new +features. + +### Fixed + +- Contact email corrected to `pedrobritx@gmail.com` across the app, README and + LICENSE. +- Sidebar / status-filter active highlight now adapts to the light theme (it + hardcoded the dark-theme accent) via a new `--color-accent-subtle` token. +- Top-bar breadcrumb no longer shows raw paths for `/corpora`, `/addons`, + `/guide`, `/about`. + +### Changed + +- Personal / portfolio links now point at the `britx.me` domain. +- **Quick lookup follows the open project's languages.** Opening lookup from + inside a project defaults its source/target to that project instead of the + global lookup defaults; the import dialog seeds its pair from your history + (distinct `pt→en` fallback for this PT↔EN-oriented tool) instead of a fixed + `en→es`, and remembers the pair you import with. +- **Signed-in users are attributed by their account name.** Tracked changes, + comments, collaboration presence and version sign-off use your account name + automatically; Settings no longer asks for a separate local name when signed + in. The device-local author id is unchanged. +- The first-run welcome screen is a proper hero (brand, value props, three-step + flow); shared links and PWA installs get Open Graph / Twitter / theme-color + metadata. + +### Added + +- **Tooling the repo lacked:** ESLint (flat config) + Prettier + knip + + `.editorconfig` / `.nvmrc`, with `lint` and `format:check` wired into CI. +- Contributor hygiene: `CONTRIBUTING.md`, `SECURITY.md`, PR + issue templates, + and a `docs/` index. + +### Removed + +- Dead code: an unused `tooltip` UI primitive, an orphaned `dictionary` feature, + an unused schema barrel, and the unused `jimp` / `@radix-ui/react-tooltip` + dependencies. + +### Internal + +- Name-fallback logic centralised in `features/account/displayName.ts`. +- Repo-wide `prettier --write` isolated in one commit (see + `.git-blame-ignore-revs`). +- Historical planning docs moved under `docs/history/`. + +--- + +Earlier history (the six-milestone Verbalis 2.0 "Translation IDE" revamp — +tracked changes, comments, accounts, real-time collaboration, roles, extensions +& connectors) is recorded in [`docs/revamp/STATUS.md`](docs/revamp/STATUS.md). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f795a4f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing to Verbalis + +Thanks for your interest in Verbalis. A few things to know before you start. + +## License context + +Verbalis is **source-available, not open source** (see [`LICENSE.md`](LICENSE.md)). +The source is here so you can read it, self-host it, and use it as an individual +for free. Contributions are welcome, but by opening a pull request you agree that +your contribution is licensed under the same terms and that the maintainer may +relicense it as part of the project. For anything substantial, please open an +issue to discuss first. + +## Development + +Verbalis is a local-first React + TypeScript + Vite app. It runs 100% locally +with no account or backend. + +```bash +pnpm install +pnpm dev # start the dev server +pnpm test:unit # unit tests (vitest) +pnpm test:e2e # end-to-end (playwright) +pnpm typecheck # tsc, no emit +pnpm lint # eslint +pnpm format:check # prettier check +pnpm build # production build +``` + +- **Node** 20 (see `.nvmrc`) and **pnpm** (pinned via `packageManager` in + `package.json`). +- The optional signed-in mode is behind `VITE_SUPABASE_*` — leave it unset to + develop the local-only path. See [`docs/cloud.md`](docs/cloud.md). + +## Before you open a PR + +Every change must be green: + +```bash +pnpm typecheck && pnpm lint && pnpm test:unit && pnpm build +``` + +Run `pnpm test:e2e` too for anything UI-facing. CI runs all of these. + +## Conventions + +- **Keep `core/*` pure.** Business logic goes in `src/core` as pure functions + with injected I/O (`fetchImpl`, clients); push side effects to the edges. This + is why the test suite is strong — keep it that way. +- **The optional cloud must tree-shake to zero** when unconfigured. Load Supabase + only through the dynamic `getSupabase()` path; never import + `@supabase/supabase-js` statically. +- **Match the surrounding code** — its naming, comment density, and idioms. +- Architecture notes live in [`docs/architecture.md`](docs/architecture.md). + +## Reporting bugs / requesting features + +Use the issue templates. For anything security-related, follow +[`SECURITY.md`](SECURITY.md) instead of opening a public issue. diff --git a/LICENSE.md b/LICENSE.md index 18aa994..d0ea845 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -33,8 +33,8 @@ licence to use, copy, modify, and self-host the Software for their own work, > existing "Created by Pedro Brito" credit and the link to > intact in the application's > About/credits screen, and — where you publish work, a fork, or a derivative -> built on the Software — state that it is *"powered by Verbalis, created by -> Pedro Brito"* with a link back to this repository. +> built on the Software — state that it is _"powered by Verbalis, created by +> Pedro Brito"_ with a link back to this repository. That attribution is the only thing asked of Individuals. @@ -49,7 +49,7 @@ If you are an Organization (any entity larger than one person), please contact the Author to arrange terms. Pricing is set individually and scaled to the size and nature of your use: -- **Email:** pedrohbrito@me.com +- **Email:** pedrobritx@gmail.com - **GitHub:** - **LinkedIn:** @@ -78,6 +78,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --- -*Not sure which tier applies to you? If you are one person using Verbalis for +_Not sure which tier applies to you? If you are one person using Verbalis for your own translation work, you are covered for free under Section 2 — just keep -the credit. Anything bigger, reach out and we'll sort out fair terms.* +the credit. Anything bigger, reach out and we'll sort out fair terms._ diff --git a/README.md b/README.md index 40d2cc6..6777d3c 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ license — see [`LICENSE.md`](LICENSE.md): **credit the developer** and keep the link back to this project. - **Organizations — get in touch.** Any team or company larger than one person needs a commercial license. Pricing is set individually and fairly — - contact **pedrohbrito@me.com**. + contact **pedrobritx@gmail.com**. --- diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..92d84aa --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,34 @@ +# Security Policy + +Verbalis is a privacy-first, local-first CAT tool: by design, your files, +translation memory and glossaries stay in your browser. Security reports are +taken seriously. + +## Reporting a vulnerability + +**Please do not open a public issue for security vulnerabilities.** + +Instead, email **pedrobritx@gmail.com** with: + +- a description of the issue and its impact, +- steps to reproduce (a proof of concept if you have one), +- the affected version / build (the About page shows the build SHA). + +You can expect an acknowledgement within a few days. Please give a reasonable +window to investigate and ship a fix before any public disclosure. + +## Scope + +Especially relevant for this project: + +- anything that could cause user data (source text, TM, glossaries) to **leave + the device** unexpectedly in local-only mode, +- issues in the optional signed-in mode (Supabase auth, RLS, cloud sync, + collaboration) that could expose one user's data to another, +- the storage-connector OAuth flows (Google Drive, OneDrive). + +## Out of scope + +- Vulnerabilities in third-party dependencies without a demonstrated exploit in + Verbalis (report those upstream). +- Anything requiring physical access to an unlocked device. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..394122a --- /dev/null +++ b/docs/README.md @@ -0,0 +1,40 @@ +# Verbalis documentation + +An index of what lives here and whether it's current. + +## Living reference + +Keep these up to date as the app changes. + +- [`architecture.md`](architecture.md) — tech stack, folder map, storage schema + (Dexie v7), deployment, and a walk through the major subsystems. +- [`cloud.md`](cloud.md) — the optional signed-in mode: Supabase setup, auth + providers, sync, cloud projects, collaboration, and the storage connectors + (Google Drive / OneDrive), with per-feature manual-verification matrices. + +## Working notes + +- [`refactor-audit.md`](refactor-audit.md) — a rolling health snapshot and + prioritised backlog of polish / refactor opportunities. + +## Revamp record (Verbalis 2.0) + +The six-milestone "Translation IDE" revamp that added tracked changes, comments, +accounts, real-time collaboration, roles, and extensions. **Done** — kept as the +build log, not a directive. + +- [`revamp/ROADMAP.md`](revamp/ROADMAP.md) — the canonical scope + locked + decisions (D1–D9). +- [`revamp/STATUS.md`](revamp/STATUS.md) — the phase-by-phase build log. +- [`revamp/prompts/`](revamp/prompts/) — the per-phase build prompts + (scaffolding; historical). + +## Historical / superseded + +Pre-build planning. Describes the _intended_ product; the shipped app is the +source of truth now. Kept for context. + +- [`history/vision/`](history/vision/) — the original product/UX/architecture + vision documents. +- [`history/roadmap-professional-features.md`](history/roadmap-professional-features.md) + — the pre-revamp "road to a professional CAT tool" plan (largely delivered). diff --git a/docs/architecture.md b/docs/architecture.md index d588438..278b7f4 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -6,20 +6,20 @@ VERBALIS is a local-first, browser-native CAT (Computer-Assisted Translation) to ## Tech Stack -| Layer | Choice | Notes | -|---|---|---| -| Framework | React 18 + TypeScript | Strict mode, full type safety | -| Bundler | Vite 6 | base: '/' for the custom-domain root (override with `BASE_PATH` for subdirectory builds) | -| Styling | Tailwind CSS 3 + shadcn/ui | Owned components, Radix primitives | -| State | Zustand (UI) + TanStack Query v5 (async) | No Redux boilerplate | -| Routing | React Router v6 HashRouter | GH Pages SPA compatibility | -| Storage | Dexie.js v4 (IndexedDB) | Promise API, live hooks, migrations | -| Parsing | unified + remark-parse + remark-gfm + sbd | AST-based, not regex | -| DOCX | mammoth | Only viable browser-side parser | -| Fuzzy search | Fuse.js (TM) + MiniSearch (terminology) | Different tools for different jobs | -| Workers | Comlink | Makes Web Worker calls look like async functions | -| PWA | vite-plugin-pwa (Workbox) | Offline support, installable | -| Fonts | geist npm package | Self-hosted, works fully offline | +| Layer | Choice | Notes | +| ------------ | ----------------------------------------- | ---------------------------------------------------------------------------------------- | +| Framework | React 18 + TypeScript | Strict mode, full type safety | +| Bundler | Vite 6 | base: '/' for the custom-domain root (override with `BASE_PATH` for subdirectory builds) | +| Styling | Tailwind CSS 3 + shadcn/ui | Owned components, Radix primitives | +| State | Zustand (UI) + TanStack Query v5 (async) | No Redux boilerplate | +| Routing | React Router v6 HashRouter | GH Pages SPA compatibility | +| Storage | Dexie.js v4 (IndexedDB) | Promise API, live hooks, migrations | +| Parsing | unified + remark-parse + remark-gfm + sbd | AST-based, not regex | +| DOCX | mammoth | Only viable browser-side parser | +| Fuzzy search | Fuse.js (TM) + MiniSearch (terminology) | Different tools for different jobs | +| Workers | Comlink | Makes Web Worker calls look like async functions | +| PWA | vite-plugin-pwa (Workbox) | Offline support, installable | +| Fonts | geist npm package | Self-hosted, works fully offline | ## Key Folder Map @@ -41,22 +41,22 @@ src/ ## Storage Schema (v7) -| Table | Indexes | Since | -|---|---|---| -| projects | id, name, updatedAt | v1 | -| segments | id, projectId, index, status, [projectId+status], [projectId+index] | v1 (compound idx v4) | -| tm | id, source, sourceLang, targetLang, projectId, corpusId, updatedAt | v1 | -| glossary | id, term, projectId, updatedAt | v1 | -| settings | &key | v2 | -| embeddings | id, tmId, model, [tmId+model] | v2 | -| corpusTerms | id, corpusId | v3 | -| corpusPacks | id | v3 | -| projectTemplates | projectId | v4 | -| versions | id, projectId, createdAt, [projectId+createdAt] | v5 | -| documents | id, projectId | v6 | -| blocks | id, documentId, projectId, [documentId+index] | v6 | -| assets | id, documentId, projectId | v6 | -| syncTombstones | [resource+rowId], resource, deletedAt | v7 | +| Table | Indexes | Since | +| ---------------- | ------------------------------------------------------------------- | -------------------- | +| projects | id, name, updatedAt | v1 | +| segments | id, projectId, index, status, [projectId+status], [projectId+index] | v1 (compound idx v4) | +| tm | id, source, sourceLang, targetLang, projectId, corpusId, updatedAt | v1 | +| glossary | id, term, projectId, updatedAt | v1 | +| settings | &key | v2 | +| embeddings | id, tmId, model, [tmId+model] | v2 | +| corpusTerms | id, corpusId | v3 | +| corpusPacks | id | v3 | +| projectTemplates | projectId | v4 | +| versions | id, projectId, createdAt, [projectId+createdAt] | v5 | +| documents | id, projectId | v6 | +| blocks | id, documentId, projectId, [documentId+index] | v6 | +| assets | id, documentId, projectId | v6 | +| syncTombstones | [resource+rowId], resource, deletedAt | v7 | Migrations are handled by Dexie's versioning system (`this.version(N).stores(...)`). Always increment, never modify existing version blocks. Notable steps: v3 adds the bundled-corpora tables + a `corpusId` index on `tm`; v4 adds compound segment indexes and moves the XLIFF template blob to its own table; v5 adds version snapshots; v6 adds the document/block model (backfilled from `sourceMeta.blockIndex`); v7 adds `updatedAt` indexes on `tm`/`glossary` and the `syncTombstones` table for the personal-resource cloud reconciler. @@ -67,6 +67,7 @@ Migrations are handled by Dexie's versioning system (`this.version(N).stores(... Static build → GitHub Actions → GitHub Pages at `https://verbalis.britx.me/`. Critical GH Pages constraints: + - `base: '/'` in vite.config.ts, overridable via the `BASE_PATH` env var (e.g. `BASE_PATH=/verbalis/ pnpm build`) for anyone forking this project into a path-based GitHub Pages deployment instead @@ -82,17 +83,17 @@ DOCX import uses `mammoth.convertToHtml`, then a small DOM walker (`src/core/seg ## Phase Roadmap -| Phase | Scope | -|---|---| -| 0 | Foundation — scaffold, CI/CD, PWA, app shell ✅ | -| 1 | TXT + MD import, segmentation, side-by-side editor ✅ | -| 2 | Translation Memory — store, exact/fuzzy match, TMX import/export ✅ | -| 3 | Terminology — glossary CRUD, CSV + TBX I/O, inline editor panel, Wiktionary adapter ✅ | -| 4 | DOCX import, command palette, review modes ✅ | -| 5 | PWA hardening, offline edge cases, update notification ✅ | -| 6 | AI integrations (Ollama, Claude, LibreTranslate), semantic TM ✅ | -| 7+ | Project-level exports, terminology extraction, collaborative TM | -| 8+ | Professional CAT features — rich editor, segment handling, versioning, LAN collaboration, document standards. See [`roadmap-professional-features.md`](roadmap-professional-features.md) | +| Phase | Scope | +| ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0 | Foundation — scaffold, CI/CD, PWA, app shell ✅ | +| 1 | TXT + MD import, segmentation, side-by-side editor ✅ | +| 2 | Translation Memory — store, exact/fuzzy match, TMX import/export ✅ | +| 3 | Terminology — glossary CRUD, CSV + TBX I/O, inline editor panel, Wiktionary adapter ✅ | +| 4 | DOCX import, command palette, review modes ✅ | +| 5 | PWA hardening, offline edge cases, update notification ✅ | +| 6 | AI integrations (Ollama, Claude, LibreTranslate), semantic TM ✅ | +| 7+ | Project-level exports, terminology extraction, collaborative TM | +| 8+ | Professional CAT features — rich editor, segment handling, versioning, LAN collaboration, document standards. See [`history/roadmap-professional-features.md`](history/roadmap-professional-features.md) | ## Phase 5 — PWA Layer diff --git a/docs/cloud.md b/docs/cloud.md index 3301120..09da39c 100644 --- a/docs/cloud.md +++ b/docs/cloud.md @@ -15,10 +15,10 @@ never talks to a real Supabase instance. The live Verbalis project is already provisioned: -| | | -| --- | --- | -| Project ref | `qutcuzlppbjbsymowavc` (region `us-east-2`, Postgres 17) | -| `VITE_SUPABASE_URL` | `https://qutcuzlppbjbsymowavc.supabase.co` | +| | | +| ------------------------ | ----------------------------------------------------------------------------------- | +| Project ref | `qutcuzlppbjbsymowavc` (region `us-east-2`, Postgres 17) | +| `VITE_SUPABASE_URL` | `https://qutcuzlppbjbsymowavc.supabase.co` | | `VITE_SUPABASE_ANON_KEY` | `sb_publishable_c8YdrT2-abKdW1Kl7FDKyA_v5OwqLdY` (publishable — safe in the bundle) | These two values are also in `.env.example` — copy it to `.env.local` for local @@ -35,8 +35,8 @@ production, the repository Variables in §1. Set these at build time (e.g. in the CI/deploy environment). Leaving `VITE_SUPABASE_URL` or `VITE_SUPABASE_ANON_KEY` unset keeps the app local-only. -**Production (GitHub Pages / `deploy.yml`)**: add them under *Settings → Secrets -and variables → Actions*. A repository **Variable** is the natural home (all +**Production (GitHub Pages / `deploy.yml`)**: add them under _Settings → Secrets +and variables → Actions_. A repository **Variable** is the natural home (all three are public — they end up in the client bundle), but `deploy.yml` accepts either a Variable or a Secret via a fallback chain, and also accepts the URL under the unprefixed name `SUPABASE_URL`: @@ -51,11 +51,11 @@ build; the `sb_secret_...` key must never reach the client bundle. **Local dev**: `cp .env.example .env.local` (git-ignored) and run `pnpm dev`. -| Variable | Required | Purpose | -| --- | --- | --- | -| `VITE_SUPABASE_URL` | yes (to enable cloud) | Supabase project URL, e.g. `https://xxxx.supabase.co` | -| `VITE_SUPABASE_ANON_KEY` | yes (to enable cloud) | The project's anon / publishable key (safe to ship in a client bundle) | -| `VITE_AUTH_PROVIDERS` | no | Comma-separated OAuth providers to show, using **Supabase provider ids**: `google,azure,apple`. Defaults to `google`. Providers not configured server-side are simply hidden. | +| Variable | Required | Purpose | +| ------------------------ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `VITE_SUPABASE_URL` | yes (to enable cloud) | Supabase project URL, e.g. `https://xxxx.supabase.co` | +| `VITE_SUPABASE_ANON_KEY` | yes (to enable cloud) | The project's anon / publishable key (safe to ship in a client bundle) | +| `VITE_AUTH_PROVIDERS` | no | Comma-separated OAuth providers to show, using **Supabase provider ids**: `google,azure,apple`. Defaults to `google`. Providers not configured server-side are simply hidden. | > Note on provider ids: Microsoft is `azure` (not `microsoft`) in Supabase Auth. @@ -92,7 +92,7 @@ and safe to re-run: allowlist lives in `src/storage/cloud/settingsSync.ts`). - `0004_personal_resources.sql` — the user's personal term bank + TM (`personal_glossary` / `personal_tm`, each `id, user_id, updated_at, deleted, - payload jsonb`) + owner-scoped RLS. A generic cursor-based reconciler +payload jsonb`) + owner-scoped RLS. A generic cursor-based reconciler (`src/storage/cloud/rowSync.ts`) syncs them with per-row LWW and soft-delete tombstones. Backs Phase 3.4; **bundled corpora never sync** (corpus-seeded TM carries a `corpusId` and is filtered out). @@ -105,32 +105,32 @@ and safe to re-run: `has_project_role` RLS helpers into a non-API `private` schema so they are not reachable as PostgREST RPCs (clears the database linter). Apply after `0005`. - `0007_compaction.sql` — the `claim_compaction(project_id, expected_seq, state, - up_to_id)` RPC (SECURITY DEFINER, membership-checked): optimistically installs +up_to_id)` RPC (SECURITY DEFINER, membership-checked): optimistically installs a fresh compacted `ydoc_state` snapshot (guarded by the `seq` generation) and prunes the subsumed `ydoc_updates` rows, so the append log stays bounded. Backs - Phase 4.3. Apply after `0006`. *(Numbering note: ROADMAP §4.3 sketched + Phase 4.3. Apply after `0006`. _(Numbering note: ROADMAP §4.3 sketched `0005_compaction.sql`, but `0005`/`0006` were taken by the 4.1 project migrations, so it ships as `0007`; and the RPC signature is widened from the sketch's `(project_id, expected_seq)` to also carry the new snapshot + the pruned-through id, so the whole compaction is one atomic, RLS-safe - transaction.)* + transaction.)_ - `0008_member_policies.sql` — member management: adds `profiles.email` (backfilled - + seeded on signup) and a co-member `profiles` SELECT policy (`private.shares_project`); - switches `project_members` update/delete to **project_manager-only** (insert still - allows the owner's publish self-seed); a `enforce_min_one_pm` trigger that blocks - removing or demoting a project's **last** manager; and the `invite_member(project_id, - email, role)` RPC (SECURITY DEFINER, PM-checked) that resolves the email to a user id - and upserts the membership (returns NULL when no account has that email — no - enumeration surface). Backs Phase 5.1. Apply after `0007`. *(Numbering note: ROADMAP - §5.1 sketched `0006_member_policies.sql`, but `0006`/`0007` were taken, so it ships as - `0008`; invite resolves the email inside the RPC rather than via a client-side profiles - lookup, keeping emails/ids server-side.)* + - seeded on signup) and a co-member `profiles` SELECT policy (`private.shares_project`); + switches `project_members` update/delete to **project_manager-only** (insert still + allows the owner's publish self-seed); a `enforce_min_one_pm` trigger that blocks + removing or demoting a project's **last** manager; and the `invite_member(project_id, +email, role)` RPC (SECURITY DEFINER, PM-checked) that resolves the email to a user id + and upserts the membership (returns NULL when no account has that email — no + enumeration surface). Backs Phase 5.1. Apply after `0007`. _(Numbering note: ROADMAP + §5.1 sketched `0006_member_policies.sql`, but `0006`/`0007` were taken, so it ships as + `0008`; invite resolves the email inside the RPC rather than via a client-side profiles + lookup, keeping emails/ids server-side.)_ - `0009_workflow.sql` — adds a `project_stage` enum + `projects.stage` (default `translation`) and `projects.deadline`, gating role-based editing (§5.2). Writes are project_manager-only through the existing `projects_update` policy — - no new policy. Backs Phase 5.2. Apply after `0008`. *(Numbering note: ROADMAP + no new policy. Backs Phase 5.2. Apply after `0008`. _(Numbering note: ROADMAP §5.2 sketched `0007_workflow.sql`, but `0007`/`0008` were taken, so it ships as - `0009`.)* + `0009`.)_ > Note: the linter's "Leaked Password Protection Disabled" warning is unrelated > to these migrations — it's an optional **Authentication → Policies** toggle @@ -153,7 +153,7 @@ In the Supabase dashboard under **Authentication → Providers**: ### Redirect URL allow-list (critical) Verbalis is a **hash-routed static site**, so we pin Supabase's **PKCE** flow: -the `?code=` lands in the query string *before* the `#/route` fragment, and a +the `?code=` lands in the query string _before_ the `#/route` fragment, and a pre-router bootstrap (`src/storage/cloud/authBootstrap.ts`) exchanges it and strips the query before React mounts. For this to work, add every origin you serve from to **Authentication → URL Configuration → Redirect URLs**: @@ -284,7 +284,7 @@ project card (`ManageMembersDialog` → `src/storage/cloud/members.ts`). Apply a second signed-in account: - [ ] **PM invites**: as the manager, open Members → invite the second account's - email as *translator*, then again as *revisor* → they appear in the roster + email as _translator_, then again as _revisor_ → they appear in the roster with the chosen role, and can open the project. - [ ] **Unknown email**: inviting an email with no Verbalis account yet shows the "no account uses that email" notice (the RPC returns NULL) — no row added. @@ -308,13 +308,13 @@ first. With a project_manager, a translator, and a revisor on one cloud project: - [ ] **PM sets the stage**: in Members, the manager moves the project through Translation → Review → Final; a non-PM sees the stage read-only. -- [ ] **Translator forced to suggest in review**: with the stage on *Review*, the +- [ ] **Translator forced to suggest in review**: with the stage on _Review_, the translator's edit-mode toggle is locked to **Suggesting** (disabled) and typing produces tracked suggestions, not direct edits. - [ ] **Accept/reject is reviewer-only**: the translator sees suggestions but **no** Accept/Reject (panel buttons hidden, hover card suppressed); the revisor and the PM see and can resolve them. -- [ ] **Final locks editing**: in *Final*, translator + revisor can't edit; only the +- [ ] **Final locks editing**: in _Final_, translator + revisor can't edit; only the PM can (e.g. to reopen by moving the stage back). - [ ] **Local-only unchanged**: a project with no cloud link behaves as "PM-of-self" — direct editing, suggesting, and accept/reject all available, @@ -336,7 +336,7 @@ verification burden. The access token lives in memory only (gone on reload). testing; add yourself as a test user). Add the scope `https://www.googleapis.com/auth/drive.file`. 4. **APIs & Services → Credentials → Create credentials → OAuth client ID → - Web application**. Under *Authorised JavaScript origins* add the exact + Web application**. Under _Authorised JavaScript origins_ add the exact origins the app runs on, e.g. `http://localhost:5173` and `https://verbalis.britx.me`. (No redirect URI is needed — GIS uses the token flow, not a redirect.) @@ -350,8 +350,8 @@ script is loaded and the connector code stays out of the initial bundle. With `VITE_GOOGLE_CLIENT_ID` set: -- [ ] **Addon listed**: the Add-ons page shows **Google Drive** under *Storage - connectors* with Network / API key / Files permission chips and a working +- [ ] **Addon listed**: the Add-ons page shows **Google Drive** under _Storage + connectors_ with Network / API key / Files permission chips and a working on/off toggle. - [ ] **Import from Drive**: in the Import dialog, **From Google Drive** opens the Google consent popup, then a file list; picking a `.docx`/`.txt`/`.md`/XLIFF @@ -396,8 +396,8 @@ never loaded and the connector code stays out of the initial bundle. With `VITE_MS_CLIENT_ID` set: -- [ ] **Addon listed**: the Add-ons page shows **OneDrive** under *Storage - connectors* with Network / API key / Files permission chips and a working +- [ ] **Addon listed**: the Add-ons page shows **OneDrive** under _Storage + connectors_ with Network / API key / Files permission chips and a working on/off toggle. - [ ] **Import from OneDrive**: in the Import dialog, **From OneDrive** opens the Microsoft login popup, then a file list; picking a diff --git a/docs/roadmap-professional-features.md b/docs/history/roadmap-professional-features.md similarity index 75% rename from docs/roadmap-professional-features.md rename to docs/history/roadmap-professional-features.md index 3678331..52ed9bd 100644 --- a/docs/roadmap-professional-features.md +++ b/docs/history/roadmap-professional-features.md @@ -14,17 +14,17 @@ below. memoQ is powerful and dense — a ribbon with ~120 commands, modal resource consoles, and a learning curve measured in weeks. Verbalis copies memoQ's -*capabilities*, not its *surface*. Every feature below is restated as "what +_capabilities_, not its _surface_. Every feature below is restated as "what memoQ does" → "the Verbalis‑native, simpler treatment" → "where it plugs into the current code". The plan is built on three decisions already made with the maintainer: -| Decision | Choice | -|---|---| -| LAN collaboration | **Tauri desktop peers** with mDNS auto‑discovery + CRDT sync — every install is a real peer, no cloud, no central server | -| Rich editor core | **Lexical** (Meta) replaces the plain `