<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ollama-translations.git/tests/ui-smoke-tests.js, branch main</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.
</subtitle>
<id>https://git.takashi-2521.com/ollama-translations.git/atom?h=main</id>
<link rel='self' href='https://git.takashi-2521.com/ollama-translations.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.takashi-2521.com/ollama-translations.git/'/>
<updated>2026-09-01T18:14:05+00:00</updated>
<entry>
<title>feat: PDF Viewer translation via context menu and two-mode Action Popup</title>
<updated>2026-09-01T18:14:05+00:00</updated>
<author>
<name>tanaka takashi</name>
<email>isazaka@gmail.com</email>
</author>
<published>2026-09-01T18:14:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.takashi-2521.com/ollama-translations.git/commit/?id=82bb55f66071b452e3bc7adeb09eafb5003de11f'/>
<id>urn:sha1:82bb55f66071b452e3bc7adeb09eafb5003de11f</id>
<content type='text'>
Add Firefox built-in PDF Viewer support using only public WebExtensions
APIs: a selection context menu ("選択したテキストを翻訳") starts a
translation and the toolbar Action Popup shows the streaming result.
No content-script injection into the privileged PDF Viewer, no PDF.js
internals, no separate windows, no custom viewer (Specifications.md §4.6).

Specification alignment (Phase 1)
- Specifications.md: new requirements PDF-01..04 (PDF selection
  translation), CTX-01..03 (context menu translation), POP-01..06
  (popup two modes); SEL-06 updated to route PDF through the menus API;
  section 13.4 adds a 14-item manual PDF verification checklist; section
  15 removes "Firefox内蔵PDF Viewerへの対応" from out-of-scope
- Design.md: architecture diagram with the menus path, menu-translation
  component, menus permission, popup translation-result state machine
  (idle/starting/streaming/completed/error/cancelled), shared translation
  state (§9.2), context-menu integration phase, test plan and API audit
  checklist items (menus permission scope, no PDF Viewer internals)
- Security.md: menus permission restricted to selection-context
  translation; PDF Viewer treated as privileged browser UI (no injection,
  no wrappedJSObject/cloneInto/exportFunction, no internal DOM access);
  menus.OnClickData.selectionText treated as untrusted input passing the
  same validation pipeline; popup result rendering stays textContent-only

Background (Phase 2)
- translation-service.js (new): the single translation pipeline shared by
  every entry point - selection validation (type/trim/empty/control
  chars/max length), prompt building, Ollama request, session/abort, and
  the shared translation state (requestId, sourceText, translatedText,
  status, model, error, startedAt, source). "source" is metadata only; a
  PDF-specific translation engine does not exist
- menu-translation.js (new): validates menus.OnClickData.selectionText
  and starts a sink-less translation observed via GET_POPUP_STATE
- message-router.js: routes port translations through the shared service;
  new runtime messages GET_POPUP_STATE and REGENERATE_LAST; popup-state
  port handler broadcasts live state (initial push + every chunk/status
  change) so an open popup shows streaming progress in real time
- index.js: registers the selection context menu (contexts: ["selection"]
  only, no menus.onShown) and calls browser.action.openPopup()
  synchronously inside the click user gesture, catching failures so a
  popup-open problem never breaks the translation itself
- manifest.json: permissions now ["storage", "menus"]
- settings-service.js: __resetCacheForTests hook for test isolation

Popup two-mode UI (Phase 3)
- popup.html/js/css: mode nav (設定 / 翻訳). Translation mode shows
  status, model, streaming text (textContent only), user-safe error, and
  copy (Clipboard API only, no execCommand fallback), regenerate (new
  requestId reusing the stored source text) and a settings shortcut.
  Closing the popup never cancels the request; reopening shows the
  latest text and continues receiving chunks live. Initial mode: active
  (starting/streaming) translation wins, otherwise the last tab the user
  picked is restored (storage.local.popupMode, default settings)

Fresh-install model auto-selection
- translation-service.js ensureModelConfigured(): when storage.model is
  empty, pick the first model from the running Ollama and persist it
  before translating, mirroring the popup/options auto-select. A user who
  never opened any settings UI can translate immediately; when Ollama is
  unreachable a clear OLLAMA_UNAVAILABLE message is shown

User-safe error details
- detailForUser(): strips URLs (no internal host names echoed to the UI),
  caps length at 120 chars, drops full JSON response bodies, and hides
  meaningless low-level network errors so the popover/popup shows the
  base message plus at most a short, safe detail

Tests: 85 passed, 0 failed
- selectionText validation (untrusted input pipeline)
- menu selection -&gt; translation -&gt; popup state streaming/completed
- empty selection rejected without starting a request
- streaming status transitions observed through GET_POPUP_STATE
- Ollama unreachable surfaces a user-safe error into popup state
- newer request replaces older state (abort semantics)
- regenerateLast reuses source text with a fresh requestId
- popup-state port receives live broadcasts; wrong port names ignored
- fresh-install auto-select persists the first Ollama model
- error detail sanitization (URL stripping, length cap, JSON drop)
- popup/options smoke tests updated for the two-mode markup
- setupRouter resets the shared settings cache for test isolation

Docs: README documents the two-mode popup and PDF flow; Testing-Results
records automated results and the manual PDF checklist (to be executed).

No git-side surprises: this commit was created explicitly on request
(requirement 18 of the PDF task had deferred commits until now).
</content>
</entry>
<entry>
<title>fix: options page never ran due to missing export; unify settings handling</title>
<updated>2026-09-01T11:38:17+00:00</updated>
<author>
<name>tanaka takashi</name>
<email>isazaka@gmail.com</email>
</author>
<published>2026-09-01T11:38:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.takashi-2521.com/ollama-translations.git/commit/?id=bb06e0e58dcb90eceede6f0b05743fdccc8f51af'/>
<id>urn:sha1:bb06e0e58dcb90eceede6f0b05743fdccc8f51af</id>
<content type='text'>
Root cause of the broken options page (audit finding):
- options.js imported CONTEXT_PRESETS from settings-schema.js, but that
  module never exported it. A named-import mismatch in ES modules is a
  SyntaxError, so the entire options page script silently never executed.
  This is why the settings page rendered its static HTML but was dead:
  no language options, no model list, no save handler.

Fixes
- options.js: import CONTEXT_PRESETS from shared/constants.js (where it
  is defined) instead of settings-schema.js
- options.js: save flow now merges the validated form over the stored
  settings (sanitize again) so fields saved by the popup in the meantime
  are preserved instead of being dropped
- popup.js: populate language options inside loadSettings and sanitize
  settings before every save, matching the options page behavior
- settings-schema.js: add normalizeOllamaHost() and use it in
  sanitizeSettings so trailing-slash variants are stored identically
- content.js: consolidate popover state - remove the duplicate outer
  lastSourceText and keep regenerate source inside the popover module;
  regenerate now calls portClient.translate directly

Test hardening (would have caught the original bug)
- tests/ui-smoke-tests.js: new smoke tests that install a fake browser
  API, run the real popup.js / options.js page scripts as ES modules
  against representative markup, and assert that language options are
  populated, the model select is filled from the mocked model list, and
  the stored host is reflected. The runner result elements are preserved
  across the sandboxed DOM swap
- test.html: include the smoke test module

Results: 73 passed, 0 failed (71 existing + 2 smoke). dist rebuilt.
</content>
</entry>
</feed>
