<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ollama-translations.git/src/popup, branch v0.1.1</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.
</subtitle>
<id>https://git.takashi-2521.com/ollama-translations.git/atom?h=v0.1.1</id>
<link rel='self' href='https://git.takashi-2521.com/ollama-translations.git/atom?h=v0.1.1'/>
<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: model select showed a value that was never saved</title>
<updated>2026-09-01T11:57:29+00:00</updated>
<author>
<name>tanaka takashi</name>
<email>isazaka@gmail.com</email>
</author>
<published>2026-09-01T11:57:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.takashi-2521.com/ollama-translations.git/commit/?id=5ba53d7b64b181134b9c594fd9881ec626eae791'/>
<id>urn:sha1:5ba53d7b64b181134b9c594fd9881ec626eae791</id>
<content type='text'>
Root cause of "設定値が不正です (モデルが設定されていません)" despite the
settings UI showing a selected model:
- fillModels(models, selected="") never set option.selected when no model
  was saved yet. The browser then auto-selects the first option visually,
  so the UI displayed e.g. "glm-5.3:cloud" while storage.model remained
  empty. Translation therefore failed with the no-model error until the
  user pressed Save, which materialized the UI value into storage.

Fixes
- popup.js/options.js fillModels: when no model is saved and the list is
  non-empty, explicitly select the first model so the UI value always
  matches what will be used
- popup.js: after the model list loads, persist the auto-selected model
  if storage still has none
- options.js loadSettings: same sync after loadModels, so opening the
  options page also heals the empty-model state

Results: 73 passed, 0 failed. dist rebuilt.
</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>
<entry>
<title>feat: Firefox selection translation extension with local Ollama (initial version)</title>
<updated>2026-09-01T11:10:20+00:00</updated>
<author>
<name>tanaka takashi</name>
<email>isazaka@gmail.com</email>
</author>
<published>2026-09-01T11:10:20+00:00</published>
<link rel='alternate' type='text/html' href='https://git.takashi-2521.com/ollama-translations.git/commit/?id=55e581bab9b600e7c070ec596d3109fc897d2d86'/>
<id>urn:sha1:55e581bab9b600e7c070ec596d3109fc897d2d86</id>
<content type='text'>
Implement the complete initial version of a Firefox WebExtension (Manifest V3)
that translates selected text on web pages using a local Ollama LLM and shows
the result in a small popover near the selection.

Specification alignment (Phase 0)
- Unified Specifications.md / Design.md / Security.md on the same approach:
  Ollama REST API + Fetch API + NDJSON streaming (no ollama-js SDK dependency)
- Fixed host_permissions to http://localhost/* and http://127.0.0.1/* because
  Firefox match patterns do not support port numbers (bug 1362809/1468162);
  the actual connection target is restricted to localhost/127.0.0.1 + any port
  by application-side validation (defense in depth, OLL-09)
- Clipboard: use navigator.clipboard.writeText() only inside the copy button
  click (user activation); no deprecated execCommand fallback

Implementation (zero dependencies, vanilla JS, no npm/Node required)
- manifest.json: MV3, background.scripts + type:module (event page, no
  service_worker), action popup, options_ui, content_scripts for http/https,
  permissions: storage + localhost host permissions only
- src/shared: constants (ports, limits, presets, error codes), message
  validation, settings schema (defaults / merge / sanitize / buildOptions /
  toApiThinking), system prompt builder (fixed prompt first + user prompt)
- src/background: message-router (runtime.Port streaming + runtime.onMessage
  for model list), translation-session (single active request, abort previous
  on new request, stale chunk suppression), ollama-client (fetch against
  /api/tags and /api/chat with buffering NDJSON line parser tolerant to chunk
  boundaries inside JSON objects, AbortController, host re-validation before
  every request, HTTP status to error-code mapping), settings-service
  (storage.local load/save with validation and fallback to defaults)
- src/content/content.js: single classic script (Firefox does not support ES
  modules in content scripts) with selection detection (selectionchange +
  mouseup + keyup, debounced), Shadow DOM action button, Shadow DOM popover
  with HIDDEN/LOADING/STREAMING/COMPLETED/ERROR state machine, port client
  with requestId-based stale chunk filtering, enabled-state sync via
  storage.onChanged; all dynamic text rendered via textContent only
- src/popup + src/options: full settings UI (enable toggle, Ollama host,
  model list from Ollama with saved-model warning, target language, thinking
  mode, temperature slider 0.0-2.0 step 0.1, context length presets
  4k/8k/16k/32k/128k/256k, seed random/fixed with integer value, user system
  prompt, advanced numeric options with steppers, stop sequences textarea)
- icons generated by tools/make-icons.ps1 (System.Drawing, no external tools)
- build.ps1 packages manifest.json + icons/ + src/ into dist zip, excluding
  tests/, doc/, ollama-js/ and tools/

Tests (71 passed, 0 failed)
- In-browser test runner (tests/test.html) with custom describe/it/assert
  framework; no npm, no Node.js
- Unit: constants sync between content mirror and shared module, Ollama host
  validation (localhost/127.0.0.1 + any port only; rejects external hosts,
  LAN IPs, https, credentials, paths), settings schema defaults and fallback,
  temperature 0.1-step validation, context length presets, seed modes,
  advanced option types, buildOptions omission of unset values and absence of
  tfs_z/mirostat fields, system prompt composition order, message validation
  with selection length boundary values
- Integration: NDJSON streaming across awkward byte boundaries, thinking
  chunk forwarding, malformed line tolerance, error payload mapping, think/
  options inclusion rules, model list, TranslationSession abort semantics,
  settings round-trip with storage.onChanged cache refresh, full port flow
  (started/chunk/completed), newer request aborts older with stale chunk
  discard, malformed message rejection, OLLAMA_UNAVAILABLE and
  INVALID_SETTINGS error paths, untrusted sender rejection

Security audit (Design.md section 24 checklist: all pass)
- No innerHTML/outerHTML/document.write/eval/new Function/localStorage
- No remote JS/CSS/fonts/CDN resources; no inline scripts in extension pages
- Ollama access limited to background; content scripts never fetch Ollama
- Background validates message type/length/requestId; single concurrent
  translation enforced
- Clipboard API only (no execCommand fallback)
- Minimal permissions: storage + localhost host permissions

Docs
- README.md with build/load/usage instructions, API list, permission rationale
- doc/Testing-Results.md with automated test results, security audit results,
  and a 14-item manual Firefox verification checklist (to be executed on a
  real Firefox + Ollama environment)
</content>
</entry>
</feed>
