Skip to content

Latest commit

 

History

History
119 lines (99 loc) · 5.85 KB

File metadata and controls

119 lines (99 loc) · 5.85 KB

DS Vector

A small, dependency-free web app to load images locally (no server, no upload), display them in a square letterboxed grid, and convert them. Built with pure JavaScript (native ES Modules), plain HTML and CSS — no framework, no build step, no bundler.

Golden rules

  • Pure JavaScript only. Native ES Modules (import/export). No TypeScript, no framework, no bundler, no transpilation, no npm runtime dependencies.
  • Code and comments in English. User-facing copy is never hard-coded — it lives in i18n locale files and is rendered through t(key). Supported locales: en-US (default) and pt-BR. A code string used for logic (e.g. a reason code like "memory") is English; a string shown on screen is a translation key resolved at runtime.
  • Everything runs client-side. Images are read from local memory via URL.createObjectURL; nothing is ever uploaded.
  • No new dependencies without an explicit request.

Running the app

Because it uses native ES Modules, it must be served over HTTP (file:// double-click is blocked by CORS for module imports):

python3 -m http.server 8000
# open http://localhost:8000

Architecture

Strict separation of responsibilities. Dependencies flow in one direction: controllers → services / views → core / config. Lower layers never import upward.

index.html                      Markup + element ids the UI binds to
styles.css                      All styling (see .claude/skills/styling.md)
app/
├── main.js                     Entry point; calls appController.init()
├── config/
│   └── constants.js            Accepted types + memory limits (single place to tune)
├── core/
│   └── imageStore.js           Single source of truth: in-memory image list,
│                               memory accounting, object-URL lifecycle (revoke)
├── i18n/                       Internationalization (no library)
│   ├── i18n.js                 Core: detect, t(key, params), get/setLocale,
│   │                           onChange, localStorage persistence
│   └── locales/
│       ├── en-us.js            Default locale (code, name, flag SVG, messages)
│       └── pt-br.js            Portuguese locale
├── services/                   Stateless business logic / specific tasks
│   ├── fileValidator.js        isAccepted / getExtension
│   ├── memoryBudget.js         computeBudgetBytes / measureDecodedBytes
│   ├── imageLoader.js          Sequential, budget-aware loading orchestration
│   └── imageProcessor.js       "Convert" action (image processing) — to implement
├── ui/                         View layer: DOM only, no business rules
│   ├── dom.js                  Centralized element refs + escapeHtml
│   ├── gridView.js             Render/remove/clear cards
│   ├── statusView.js           Counter, busy state, skipped-images notice
│   ├── i18nView.js             Applies translations to static markup
│   └── languageSwitcher.js     Custom flag dropdown to change locale at runtime
└── controllers/
    └── appController.js        Wires user events to services + views; the only
                                layer that knows about all the others

Layer responsibilities

Layer May import Must NOT do
controllers services, ui, core manipulate pixels; hold domain state
services config, core, other svcs touch the DOM; register UI events
core config touch the DOM; know about services/views
ui dom, pure utils hold business rules; know about the store
config contain logic

Where things go

  • New screen interaction → handler in controllers/appController.js.
  • New domain/processing task (e.g. the conversion logic) → services/ (put the Convert implementation in imageProcessor.js).
  • New visual piece / DOM change → a ui/*View.js module; keep it a pure function of its inputs.
  • New shared statecore/imageStore.js (keep it the single source of truth; it also owns URL.revokeObjectURL to prevent leaks).
  • New limit / accepted typeconfig/constants.js.
  • New user-facing text → add the key to both i18n/locales/*.js; render it via t(key) (dynamic strings) or a data-i18n* attribute (static markup). Never hard-code display text in views or HTML.
  • New language → add i18n/locales/<code>.js (with flag SVG) and register it in i18n/i18n.js; the switcher picks it up automatically.

Memory model (important)

The browser does not expose real free memory. We use an estimated budget (memoryBudget.js) and load sequentially, measuring each image's decoded cost (width × height × 4 bytes) plus its raw file size. When the running total would exceed the budget, loading stops and remaining files are reported as skipped. imageStore decrements the running total on remove/clear and is the only place that revokes object URLs.

Conventions

  • Module-per-responsibility; prefer named exports.
  • Comments explain why, not what; keep them in English.
  • No global variables; state lives in core/imageStore.js.

Skills

Project-specific Claude Code skills live under .claude/skills/<name>/SKILL.md and are auto-invoked by their description:

  • styling — CSS rules and conventions (tokens, BEM, dark theme, layout, a11y). Triggers on CSS / visual changes.
  • javascript — JS conventions (pure ESM, layering, naming, memory safety). Triggers on changes under app/.