From 370f1acb24fe7f596c826dffa4b541a620736500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henrique=20Ara=C3=BAjo?= Date: Fri, 23 Oct 2026 08:40:04 -0300 Subject: [PATCH 1/8] feat: LowLex v0.1, a light translation server Go backend (stdlib HTTP, pure-Go SQLite, argon2id) with the SvelteKit UI (CodeAtlas UI, LowBloat theme) embedded into one static binary for Linux, macOS and Windows, plus Dockerfile and compose.yaml. - First admin with a forced password change, or LOWLEX_ADMIN_PASSWORD - Global admin/user accounts; per-project manager, developer, reviewer, translator and viewer roles, with per-language limits - Translation editor, machine translation (Google, DeepL, Microsoft, LibreTranslate) and any OpenAI-compatible AI, single and batch - Import/export: JSON flat/nested, CSV, PO, Android, iOS, .properties, ZIP - Per-project API keys, activity log, en and pt-BR UI --- .agents/skills/codeatlas-dx/SKILL.md | 105 + .agents/skills/codeatlas-ui/ANTIPATTERNS.md | 166 + .agents/skills/codeatlas-ui/API.md | 5136 +++++++++++++++++ .agents/skills/codeatlas-ui/APPSHELL.md | 113 + .agents/skills/codeatlas-ui/COMPONENTS.md | 307 + .agents/skills/codeatlas-ui/DEMOS.md | 320 + .agents/skills/codeatlas-ui/LAYOUT.md | 205 + .agents/skills/codeatlas-ui/PAGE-TEMPLATES.md | 239 + .agents/skills/codeatlas-ui/SKILL.md | 163 + .agents/skills/codeatlas-writing/SKILL.md | 155 + .claude/launch.json | 11 + .claude/skills/codeatlas-dx/SKILL.md | 105 + .claude/skills/codeatlas-ui/ANTIPATTERNS.md | 166 + .claude/skills/codeatlas-ui/API.md | 5136 +++++++++++++++++ .claude/skills/codeatlas-ui/APPSHELL.md | 113 + .claude/skills/codeatlas-ui/COMPONENTS.md | 307 + .claude/skills/codeatlas-ui/DEMOS.md | 320 + .claude/skills/codeatlas-ui/LAYOUT.md | 205 + .claude/skills/codeatlas-ui/PAGE-TEMPLATES.md | 239 + .claude/skills/codeatlas-ui/SKILL.md | 163 + .claude/skills/codeatlas-writing/SKILL.md | 155 + .dockerignore | 9 + .env.example | 24 + .github/workflows/release.yml | 65 + .gitignore | 15 + .mcp.json | 9 + Dockerfile | 33 + README.md | 139 + assets/lowlex.svg | 1 + assets/screenshots/editor-desktop-light.png | Bin 0 -> 344888 bytes assets/screenshots/login-desktop.png | Bin 0 -> 119687 bytes .../screenshots/translations-desktop-dark.png | Bin 0 -> 354080 bytes .../translations-desktop-light.png | Bin 0 -> 346117 bytes .../screenshots/translations-phone-light.png | Bin 0 -> 132349 bytes cmd/lowlex/main.go | 164 + compose.yaml | 27 + go.mod | 20 + go.sum | 52 + internal/config/config.go | 98 + internal/formats/formats.go | 616 ++ internal/formats/formats_test.go | 59 + internal/mt/languages.go | 24 + internal/mt/mt.go | 450 ++ internal/secure/secure.go | 114 + internal/server/auth.go | 408 ++ internal/server/io.go | 635 ++ internal/server/keys.go | 485 ++ internal/server/projects.go | 603 ++ internal/server/providers.go | 500 ++ internal/server/server.go | 239 + internal/server/server_test.go | 254 + internal/server/users.go | 222 + internal/store/schema.sql | 110 + internal/store/store.go | 64 + scripts/build.sh | 39 + skills-lock.json | 23 + web/.gitignore | 24 + web/.npmrc | 1 + web/.vscode/extensions.json | 3 + web/bun.lock | 207 + web/embed.go | 22 + web/package.json | 33 + web/src/app.d.ts | 13 + web/src/app.html | 12 + web/src/error.html | 276 + web/src/hooks.server.ts | 9 + web/src/lib/api.ts | 199 + web/src/lib/app.css | 25 + web/src/lib/assets/lowlex.svg | 1 + web/src/lib/components/AuthPanel.svelte | 50 + .../components/BatchTranslateDialog.svelte | 91 + web/src/lib/components/Brand.svelte | 26 + web/src/lib/components/KeyEditor.svelte | 133 + web/src/lib/components/NewKeyDialog.svelte | 56 + .../lib/components/NewProjectDialog.svelte | 87 + .../lib/components/TranslationField.svelte | 225 + web/src/lib/i18n.svelte.ts | 87 + web/src/lib/index.ts | 1 + web/src/lib/locales/en.json | 380 ++ web/src/lib/locales/pt-BR.json | 380 ++ web/src/lib/password.ts | 12 + web/src/lib/project.svelte.ts | 49 + web/src/lib/session.svelte.ts | 73 + web/src/lib/states.ts | 17 + web/src/lib/theme.ts | 57 + web/src/routes/(app)/+layout.svelte | 108 + web/src/routes/(app)/account/+page.svelte | 109 + web/src/routes/(app)/admin/+page.svelte | 6 + .../routes/(app)/admin/providers/+page.svelte | 233 + web/src/routes/(app)/admin/users/+page.svelte | 211 + web/src/routes/(app)/p/[slug]/+layout.svelte | 73 + web/src/routes/(app)/p/[slug]/+page.svelte | 454 ++ .../(app)/p/[slug]/activity/+page.svelte | 52 + .../routes/(app)/p/[slug]/api/+page.svelte | 178 + .../(app)/p/[slug]/languages/+page.svelte | 168 + .../(app)/p/[slug]/members/+page.svelte | 179 + .../(app)/p/[slug]/settings/+page.svelte | 98 + .../(app)/p/[slug]/transfer/+page.svelte | 179 + web/src/routes/(app)/projects/+page.svelte | 104 + web/src/routes/+error.svelte | 6 + web/src/routes/+layout.svelte | 23 + web/src/routes/+layout.ts | 4 + web/src/routes/+page.svelte | 6 + web/src/routes/login/+page.svelte | 57 + web/src/routes/password/+page.svelte | 79 + web/static/favicon.svg | 1 + web/static/robots.txt | 2 + web/tsconfig.json | 8 + web/vite.config.ts | 26 + 109 files changed, 24243 insertions(+) create mode 100644 .agents/skills/codeatlas-dx/SKILL.md create mode 100644 .agents/skills/codeatlas-ui/ANTIPATTERNS.md create mode 100644 .agents/skills/codeatlas-ui/API.md create mode 100644 .agents/skills/codeatlas-ui/APPSHELL.md create mode 100644 .agents/skills/codeatlas-ui/COMPONENTS.md create mode 100644 .agents/skills/codeatlas-ui/DEMOS.md create mode 100644 .agents/skills/codeatlas-ui/LAYOUT.md create mode 100644 .agents/skills/codeatlas-ui/PAGE-TEMPLATES.md create mode 100644 .agents/skills/codeatlas-ui/SKILL.md create mode 100644 .agents/skills/codeatlas-writing/SKILL.md create mode 100644 .claude/launch.json create mode 100644 .claude/skills/codeatlas-dx/SKILL.md create mode 100644 .claude/skills/codeatlas-ui/ANTIPATTERNS.md create mode 100644 .claude/skills/codeatlas-ui/API.md create mode 100644 .claude/skills/codeatlas-ui/APPSHELL.md create mode 100644 .claude/skills/codeatlas-ui/COMPONENTS.md create mode 100644 .claude/skills/codeatlas-ui/DEMOS.md create mode 100644 .claude/skills/codeatlas-ui/LAYOUT.md create mode 100644 .claude/skills/codeatlas-ui/PAGE-TEMPLATES.md create mode 100644 .claude/skills/codeatlas-ui/SKILL.md create mode 100644 .claude/skills/codeatlas-writing/SKILL.md create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .mcp.json create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 assets/lowlex.svg create mode 100644 assets/screenshots/editor-desktop-light.png create mode 100644 assets/screenshots/login-desktop.png create mode 100644 assets/screenshots/translations-desktop-dark.png create mode 100644 assets/screenshots/translations-desktop-light.png create mode 100644 assets/screenshots/translations-phone-light.png create mode 100644 cmd/lowlex/main.go create mode 100644 compose.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/config/config.go create mode 100644 internal/formats/formats.go create mode 100644 internal/formats/formats_test.go create mode 100644 internal/mt/languages.go create mode 100644 internal/mt/mt.go create mode 100644 internal/secure/secure.go create mode 100644 internal/server/auth.go create mode 100644 internal/server/io.go create mode 100644 internal/server/keys.go create mode 100644 internal/server/projects.go create mode 100644 internal/server/providers.go create mode 100644 internal/server/server.go create mode 100644 internal/server/server_test.go create mode 100644 internal/server/users.go create mode 100644 internal/store/schema.sql create mode 100644 internal/store/store.go create mode 100644 scripts/build.sh create mode 100644 skills-lock.json create mode 100644 web/.gitignore create mode 100644 web/.npmrc create mode 100644 web/.vscode/extensions.json create mode 100644 web/bun.lock create mode 100644 web/embed.go create mode 100644 web/package.json create mode 100644 web/src/app.d.ts create mode 100644 web/src/app.html create mode 100644 web/src/error.html create mode 100644 web/src/hooks.server.ts create mode 100644 web/src/lib/api.ts create mode 100644 web/src/lib/app.css create mode 100644 web/src/lib/assets/lowlex.svg create mode 100644 web/src/lib/components/AuthPanel.svelte create mode 100644 web/src/lib/components/BatchTranslateDialog.svelte create mode 100644 web/src/lib/components/Brand.svelte create mode 100644 web/src/lib/components/KeyEditor.svelte create mode 100644 web/src/lib/components/NewKeyDialog.svelte create mode 100644 web/src/lib/components/NewProjectDialog.svelte create mode 100644 web/src/lib/components/TranslationField.svelte create mode 100644 web/src/lib/i18n.svelte.ts create mode 100644 web/src/lib/index.ts create mode 100644 web/src/lib/locales/en.json create mode 100644 web/src/lib/locales/pt-BR.json create mode 100644 web/src/lib/password.ts create mode 100644 web/src/lib/project.svelte.ts create mode 100644 web/src/lib/session.svelte.ts create mode 100644 web/src/lib/states.ts create mode 100644 web/src/lib/theme.ts create mode 100644 web/src/routes/(app)/+layout.svelte create mode 100644 web/src/routes/(app)/account/+page.svelte create mode 100644 web/src/routes/(app)/admin/+page.svelte create mode 100644 web/src/routes/(app)/admin/providers/+page.svelte create mode 100644 web/src/routes/(app)/admin/users/+page.svelte create mode 100644 web/src/routes/(app)/p/[slug]/+layout.svelte create mode 100644 web/src/routes/(app)/p/[slug]/+page.svelte create mode 100644 web/src/routes/(app)/p/[slug]/activity/+page.svelte create mode 100644 web/src/routes/(app)/p/[slug]/api/+page.svelte create mode 100644 web/src/routes/(app)/p/[slug]/languages/+page.svelte create mode 100644 web/src/routes/(app)/p/[slug]/members/+page.svelte create mode 100644 web/src/routes/(app)/p/[slug]/settings/+page.svelte create mode 100644 web/src/routes/(app)/p/[slug]/transfer/+page.svelte create mode 100644 web/src/routes/(app)/projects/+page.svelte create mode 100644 web/src/routes/+error.svelte create mode 100644 web/src/routes/+layout.svelte create mode 100644 web/src/routes/+layout.ts create mode 100644 web/src/routes/+page.svelte create mode 100644 web/src/routes/login/+page.svelte create mode 100644 web/src/routes/password/+page.svelte create mode 100644 web/static/favicon.svg create mode 100644 web/static/robots.txt create mode 100644 web/tsconfig.json create mode 100644 web/vite.config.ts diff --git a/.agents/skills/codeatlas-dx/SKILL.md b/.agents/skills/codeatlas-dx/SKILL.md new file mode 100644 index 0000000..a30e972 --- /dev/null +++ b/.agents/skills/codeatlas-dx/SKILL.md @@ -0,0 +1,105 @@ +--- +name: codeatlas-dx +description: "Use when creating files, components, or feature modules: DX-first clean code, file size limits, no god files." +alwaysApply: false +--- + +# DX-first development + +Write for the next developer (or AI). Files stay small, responsibilities stay clear, and shared logic lives in one place. + +## File size rules + +| File type | Max lines | Action at limit | +|---|---|---| +| Svelte component | 200 | Extract sub-components or composables | +| TypeScript module | 150 | Split by domain | +| Route `+page.svelte` | 300 | Move logic to `+page.ts` or a feature module | +| Store / composable | 80 | One state shape per file | + +If you're scrolling to find something, the file is too long. Split it. + +## Component structure + +``` +src/ + lib/ + components/ + UserCard/ + UserCard.svelte # UI only, no data fetching + UserCard.types.ts # Props type, not shared globally + features/ + users/ + users.store.svelte.ts # $state store for this feature + users.api.ts # fetch calls + users.utils.ts # formatters, validators + utils/ + format.ts # formatCurrency, formatDate (pure fns) + storage.ts # localStorage helpers +``` + +## Rules + +1. One component = one visual responsibility. If you're doing two things, split. +2. Logic used in 2+ places → extract to `utils/` or a composable. First use: inline. +3. Types live next to what they describe. No monolithic `types.ts`. +4. `$derived` for computed values. `$effect` only for side effects that can't be derived. +5. No `export let` in Svelte 5. Use `$props()`. +6. API calls belong in `*.api.ts`, not in components or stores. +7. Constants belong in `*.constants.ts` or top of the file that uses them. Not scattered. + +## Naming + +| Thing | Convention | Example | +|---|---|---| +| Component file | PascalCase | `UserCard.svelte` | +| Composable / store | camelCase + `.svelte.ts` | `users.store.svelte.ts` | +| API module | camelCase + `.api.ts` | `users.api.ts` | +| Utils | camelCase + `.ts` | `format.ts` | +| Types | PascalCase | `UserCardProps` | +| Boolean props | `is` / `has` prefix | `isLoading`, `hasError` | + +## Anti-patterns + +```svelte + + + + + + + +``` + +```ts +// ✗ Barrel that exports 80 things from one index.ts +export * from './components/UserCard'; +export * from './components/UserTable'; +export * from './stores/users'; +export * from './utils/format'; +// ...40 more lines + +// ✓ Import directly from the source file when building features +import { UserCard } from '$lib/components/UserCard/UserCard.svelte'; +import { formatCurrency } from '$lib/utils/format'; +// Only create barrels for published libraries, not internal feature modules +``` + +```ts +// ✗ Inline computation that belongs in a util +const formatted = `${value.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`; + +// ✓ +import { formatCurrency } from '$lib/utils/format'; +const formatted = formatCurrency(value); +``` + +## Before creating a new file + +Ask: +- Does this already exist in `@codeatlasdev/ui`? Use it. +- Does a util already do this? Import it. +- Is this logic used only once? Keep it inline. +- Will this be used in 3+ places? Extract it. diff --git a/.agents/skills/codeatlas-ui/ANTIPATTERNS.md b/.agents/skills/codeatlas-ui/ANTIPATTERNS.md new file mode 100644 index 0000000..c7a1744 --- /dev/null +++ b/.agents/skills/codeatlas-ui/ANTIPATTERNS.md @@ -0,0 +1,166 @@ +# Anti-patterns + +Explicit don'ts. Each one has a correct alternative. + +--- + +## Layout + +```svelte + +
+ + + +``` + +```svelte + +
+ +
+ + + + + +``` + +```svelte + +
+ + +
+``` + +```css +/* ✗ Tint in OKLCH: gray tokens have hue 0°, so blue drifts to purple/pink */ +background: color-mix(in oklch, var(--ca-brand-main) 15%, var(--ca-glass-tint)); + +/* ✓ Mix a color with a neutral (gray token, black, white, transparent) in OKLab */ +background: color-mix(in oklab, var(--ca-brand-main) 15%, var(--ca-glass-tint)); +``` + +--- + +## GlassCard + +```svelte + + + + + + + +
+ + +``` + +--- + +## AppShell + +```svelte + + + + + + + +``` + +--- + +## Imports + +```ts +// ✗ Internal path import +import Button from '../../../packages/ui/src/lib/components/Button/Button.svelte'; + +// ✓ +import { Button } from '@codeatlasdev/ui'; +``` + +```svelte + + + + + +``` + +--- + +## Components + +```svelte + + + + + + + + + + + + + +``` + +```svelte + +
+ + + +``` + +```svelte + +Active + + + +``` + +--- + +## Svelte 5 + +```svelte + +$: fullName = `${first} ${last}`; + + +const fullName = $derived(`${first} ${last}`); +``` + +```svelte + +