1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
|
# Design.md
## 1. 設計概要
本拡張は、内容の翻訳接点をContent Script、権限の Ollama comunicación y administración de configuración を Background 内に分離する。Firefox内蔵PDF Viewerなどの content script 非対応ページおよび通常Webページの両方で、selectionベースの翻訳を context menuから実行できるようにする。
```text
Web Page / PDF Viewer
│
│ selection
▼
selection経由
│
├─ Content Script (通常ページのみ: 翻訳アイコン → Shadow DOM Popover)
▼ │
├─ menus API (任意ページ: PDF Viewer含むselection → context menu → Action Popup)
│ │
▼ ▼
Background
│ fetch / HTTP
▼
Ollama
│ streaming
▼
Background + TranslationSession
│ runtime.Port / popup state
▼
UI
├─ Content Script: Shadow DOM Popover
└─ Action Popup: 翻訳結果モード
```
設計原則:
- ページ由来データは信頼しない。
- Content Scriptは最小責務とする。
- Ollama通信はBackgroundのみ(Ollama REST API + Fetch API + NDJSON streaming)。
- UI文字列はHTMLとして評価しない。
- 翻訳要求は常に1-shot。
- 同時翻訳は1件に制限(新規要求で旧要求abort)。
- 設定値はUIだけでなくBackgroundでも検証する。
- Ollama接続先は初期版ではlocalhost / 127.0.0.1 + 任意ポートのみ許可する。
---
## 2. コンポーネント構成
| コンポーネント | 主責務 |
|---|---|
| `content/selection-controller` | Selection検出、Range位置取得 |
| `content/action-button` | 翻訳アイコン表示/非表示 |
| `content/translation-popover` | 翻訳結果UI、stream描画、copy/regenerate/close |
| `content/port-client` | Backgroundとの長期接続 |
| `background/message-router` | Content Scriptからのrequest受信・validation(popup state 共有) |
| `background/menu-controller` | context menu登録・selectionText取得・popup開き・selection経由翻訳 |
| `background/translation-session` | requestId、AbortController、active stream管理 |
| `background/ollama-client` | Ollama REST API呼び出し(fetch + NDJSON streaming) |
| `background/settings-service` | storage読書き、default、validation |
| `popup` | 設定UI + 翻訳結果モード(streaming表示、copy/regenerate、error表示) |
| `options` | 全設定UI |
| `shared/settings-schema` | 設定型、既定値、範囲 |
| `shared/messages` | runtime message型定義 |
| `shared/constants` | 最大選択長等 |
---
## 3. 推奨ディレクトリ構成
```text
/
├─ manifest.json
├─ package.json
├─ src/
│ ├─ background/
│ │ ├─ index.js
│ │ ├─ message-router.js
│ │ ├─ translation-session.js
│ │ ├─ ollama-client.js
│ │ └─ settings-service.js
│ ├─ content/
│ │ ├─ index.js
│ │ ├─ selection-controller.js
│ │ ├─ action-button.js
│ │ ├─ translation-popover.js
│ │ ├─ port-client.js
│ │ └─ content.css
│ ├─ popup/
│ │ ├─ popup.html
│ │ ├─ popup.js
│ │ └─ popup.css
│ ├─ options/
│ │ ├─ options.html
│ │ ├─ options.js
│ │ └─ options.css
│ └─ shared/
│ ├─ settings-schema.js
│ ├─ messages.js
│ ├─ prompt.js
│ └─ constants.js
├─ tests/
│ ├─ unit/
│ └─ integration/
└─ docs/
├─ Specifications.md
└─ Design.md
```
実際のビルド方式に応じて配置は調整してよいが、責務分離は維持する。
---
## 4. Manifest設計
概念例:
```json
{
"manifest_version": 3,
"name": "Selected Text Translator",
"version": "0.1.0",
"permissions": ["storage", "menus"],
"host_permissions": ["http://localhost/*", "http://127.0.0.1/*"],
"background": {
"scripts": ["background.js"],
"type": "module"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"],
"css": ["content.css"],
"run_at": "document_idle"
}
],
"action": {
"default_popup": "popup.html"
},
"options_ui": {
"page": "options.html",
"open_in_tab": true
}
}
```
### 4.1 Manifest実装ルール
- `background.service_worker` は使用しない。
- `browser_action` は使用しない。
- `scripting` 権限は初期版では付与しない。
- `tabs` 権限は付与しない。
- localhost以外のhost permissionは初期版では付与しない。
- `menus` permission は selection context の翻訳のためのみに使用する。
---
## 5. Content Script設計
### 5.1 Selection取得
標準DOM APIを使用する。
```js
const selection = window.getSelection();
const text = selection?.toString().trim();
if (!text) return;
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
```
### 5.2 検出イベント
初期実装候補:
- `selectionchange`: 選択状態更新用
- `mouseup`: 選択確定後の表示タイミング用
- `keyup`: Shift + Arrow等のキーボード選択対応用
過剰発火を避けるため、UI更新には軽いdebounceを適用してよい。
### 5.3 Action Button表示条件
```text
selection exists
AND trimmed text length > 0
AND text length <= MAX_SELECTION_LENGTH
AND extension enabled
```
### 5.4 位置計算
- `Range.getBoundingClientRect()` を基本とする。
- 選択が複数行の場合でも最後/全体矩形を基準に配置する。
- viewport端ではポップオーバーが画面外へ出ないよう補正する。
- scroll/resize時は再配置または非表示化する。
---
## 6. UI分離設計
### 6.1 Shadow DOM
ページCSSとの衝突を避けるため、Content Scriptが専用host要素を追加し、その内部にShadow DOMを作る。
```js
const host = document.createElement('div');
document.documentElement.append(host);
const shadow = host.attachShadow({ mode: 'open' });
```
`open/closed` は実装都合で選択可能だが、セキュリティ境界として依存しない。
### 6.2 DOM挿入ルール
禁止:
```js
element.innerHTML = translatedText;
shadowRoot.innerHTML = translatedText;
```
許可:
```js
element.textContent = translatedText;
```
静的テンプレートHTMLを自前固定文字列として構築する場合も、外部入力を文字列連結しない。
---
## 7. 翻訳ポップオーバー設計
### 7.1 状態
```text
HIDDEN
READY
LOADING
STREAMING
COMPLETED
ERROR
```
### 7.2 表示内容
| 要素 | 内容 |
|---|---|
| Header | 翻訳結果 |
| Meta | Model名 / Ollama |
| Body | streaming翻訳本文 |
| Copy | 完了/途中結果をclipboardへコピー |
| Regenerate | 同じ選択文字列で再要求 |
| Close | ポップオーバー非表示 |
### 7.3 UI挙動
- Icon押下直後にLOADING表示。
- 最初のchunk受信後STREAMINGへ遷移。
- 完了イベントでCOMPLETED。
- Abortされた旧requestのchunkはrequestId不一致なら無視。
- Error時は再生成可能な状態に戻す。
### 7.4 Action Popup 翻訳結果モード
Pdf viewerやcontext menu経由翻訳では Action Popup が翻訳結果を表示する。状態機械と表示内容はPopoverと同様。
状態:
```text
IDLE
STARTING
STREAMING
COMPLETED
ERROR
CANCELLED
```
* `starting`: 翻訳 request を backgroundへ送信し、まだ started/chunk を受け取っていない状態
* `streaming`: chunk受信中
* `completed`: chunk完了(最後のchunkはdone:trueと推定)
* `error`: ollamaエラーまたは request中止
* `cancelled`: ユーザー cancel(request abort または新規 request abort)
表示内容:
| 要素 | 内容 |
|---|---|
| Header | 翻訳結果 |
| TranslatedText | streaming翻訳本文 |
| Meta | Model名 / Ollama |
| Footer | コピー / 再生成 / 状態表示 / 「設定へ戻る」 |
* popupが streaming中に開かれた場合でも、その時点での translation textを表示し、以降のchunkを継続受信する。
* popupが閉じても requestを自動cancelしない。
* 新しい翻訳request開始時に古いrequestは abortされる(既存方針)。
* request sourceは backgroundで metadataとして識別してよい(例: `"content"` / `"context-menu"`)。ただし Ollama request生成処理は共通(prompt、settings、options生成)。
---
## 8. Runtime通信設計
Streamingには `browser.runtime.connect()` / `runtime.Port` を使用する。
### 8.1 Port名
```text
translation-stream
```
### 8.2 Content → Background message
#### translate
```js
{
type: 'translate',
requestId: 'uuid-or-random-id',
text: 'selected text'
}
```
#### cancel
```js
{
type: 'cancel',
requestId: '...'
}
```
### 8.3 Background → Content message
#### started
```js
{
type: 'started',
requestId: '...',
model: '...'
}
```
#### chunk
```js
{
type: 'chunk',
requestId: '...',
text: 'partial translation'
}
```
#### completed
```js
{
type: 'completed',
requestId: '...'
}
```
#### error
```js
{
type: 'error',
requestId: '...',
code: 'OLLAMA_UNAVAILABLE',
message: '...'
}
```
### 8.4 Validation
Backgroundは最低限以下を確認する。
- `type` が既知値か
- `requestId` がstringか
- `text` がstringか
- text lengthが上限以内か
- 現在のsender/portがContent Script由来か
---
## 9. TranslationSession設計
Backgroundではactive translationを原則1件だけ保持する。
```js
class TranslationSession {
requestId;
abortController;
sourceText;
startedAt;
}
```
新しい翻訳要求:
```text
translate(request B)
↓
active request Aあり?
├─ yes → Abort A
└─ no
↓
request Bをactiveに設定
↓
Ollama streaming開始
```
### 9.1 古いchunk対策
受信chunkごとにactive requestIdを確認し、不一致なら破棄する。
### 9.2 Translation state 共有(popup連携)
Backgroundは context menu / content script いずれの経由でも translation stateを保持し、popupが開いた時に最新状態を取得できるようにする。
```js
// module internal state (background)
translationState = {
requestId, // string
sourceText, // string (validated)
translatedText, // accumulated chunk text
status, // idle | starting | streaming | completed | error | cancelled
model, // settings.model when started
error, // error message (or null)
startedAt, // epoch ms
source // 'content' | 'context-menu'
}
```
* popupからの `GET_POPUP_STATE` runtime messageでこのstateを返す。
* `onChunk` ごとに state を更新(translatedText累積、status=streaming)。
* completed/error/cancelledでもstateを更新する。popupが開き直されたときに最終状態を表示できる。
* 新しい翻訳request開始時には state を初期化(古いrequestはabort)。
---
## 10. OllamaClient設計
### 10.1 API
Ollama REST APIをFetch APIで直接呼び出す。`ollama-js`等のサードパーティSDKは使用しない。
概念例:
```js
const response = await fetch(`${host}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: settings.model,
messages: [
{
role: 'system',
content: buildSystemPrompt(settings)
},
{
role: 'user',
content: selectedText
}
],
stream: true,
think: settings.thinking, // off時は省略
options: buildOptions(settings)
}),
signal: abortController.signal
});
// NDJSONストリームをバッファリングしつつ行単位でパースする
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
for (;;) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// bufferを行単位で分割し、完全な行のみJSON.parseする
// 末尾の不完全行は次回チャンクに持ち越す
}
```
### 10.2 System Prompt構築
```text
BUILTIN_SYSTEM_PROMPT
[User translation preferences]
USER_SYSTEM_PROMPT
```
固定promptは常に先頭に置く。
### 10.3 buildOptions
未設定値はrequest objectへ含めない。
例:
```js
{
temperature: 0.2,
num_ctx: 16384,
seed: 42,
top_k: 40,
top_p: 0.9,
min_p: 0.05,
typical_p: 1.0,
repeat_last_n: 64,
repeat_penalty: 1.1,
presence_penalty: 0.0,
frequency_penalty: 0.0,
num_keep: 4,
num_predict: -1,
stop: ['<END>']
}
```
Seed可変時は`seed`をoptionsへ含めない設計を優先する。
---
## 11. 設定スキーマ
推奨形:
```js
{
enabled: true,
ollamaHost: 'http://localhost:11434',
model: '',
targetLanguage: 'ja',
thinking: false,
temperature: 0.2,
contextLength: 16384,
seedMode: 'random',
seedValue: null,
userSystemPrompt: '',
advanced: {
numKeep: null,
numPredict: null,
topK: null,
topP: null,
minP: null,
typicalP: null,
repeatLastN: null,
repeatPenalty: null,
presencePenalty: null,
frequencyPenalty: null,
stop: []
}
}
```
---
## 12. 設定値validation
### 12.1 Basic
| enabled | boolean |
| ollamaHost | URL形式。**`http://localhost:<port>` または `http://127.0.0.1:<port>` のみ許可(schemeはhttp固定、外部ホスト・LAN IP・httpsは拒否)。必須制限** |
| model | non-empty string |
| targetLanguage | 定義済み値 |
| thinking | `false/true/low/medium/high` 相当 |
| temperature | 0.0〜2.0、0.1刻み |
| contextLength | `4096/8192/16384/32768/131072/262144` のいずれか |
| seedMode | `random/fixed` |
| seedValue | fixed時のみinteger |
| userSystemPrompt | string、最大長を設定 |
### 12.2 Advanced
推奨UI step:
| 項目 | step | validation方針 |
|---|---:|---|
| num_keep | 1 | integer |
| num_predict | 1 | integer、-1許容 |
| top_k | 1 | integer、0以上 |
| top_p | 0.05 | float |
| min_p | 0.01 | float |
| typical_p | 0.05 | float |
| repeat_last_n | 1 | integer、-1許容 |
| repeat_penalty | 0.05 | float |
| presence_penalty | 0.05 | float |
| frequency_penalty | 0.05 | float |
| stop | - | string[] |
Ollamaが明示的な最小/最大値を保証していない項目について、UI側で過度に狭い範囲を独自設定しない。型と明らかな不正値を中心に検証する。
---
## 13. Context Length UI設計
固定プリセット:
```js
const CONTEXT_PRESETS = [
4096,
8192,
16384,
32768,
131072,
262144
];
```
UI表示:
```text
4k ─ 8k ─ 16k ─ 32k ─ 128k ─ 256k
```
実装上はrange inputのindex値を使用してよい。
```text
0 => 4096
1 => 8192
2 => 16384
3 => 32768
4 => 131072
5 => 262144
```
モデル自身が対応する最大contextを超える場合、Ollama側でエラーまたは制約される可能性があるため、そのエラーをUIへ返す。
---
## 14. Temperature UI設計
```text
min=0.0
max=2.0
step=0.1
```
スライダー値は表示用数値と同期する。
```html
<input type="range" min="0" max="2" step="0.1">
```
---
## 15. Seed UI設計
```text
Seed
[ 可変 ] [ 固定 ]
固定選択時:
固定値 (整数) [ 42 ▲▼ ]
```
- random時: seed optionを送らない。
- fixed時: `seedValue` を整数として送る。
- Seed Valueはnumber input + spinnerでよい。
---
## 16. Advanced UI設計
数値項目は`input type="number"`相当のUIとする。
例:
```text
top_p
[ 0.90 ][▲]
[▼]
```
HTML native spinnerを使用できる場合は活用し、独自実装を増やさない。
stopは複数行textareaとし、1行1sequenceとして保存する。
---
## 17. SettingsService設計
### 17.1 保存
`browser.storage.local` を使用。
### 17.2 読み込み
1. default settings生成
2. storage値取得
3. merge
4. validation
5. 不正値はdefaultへフォールバック
### 17.3 設定変更通知
設定変更時に `browser.storage.onChanged` を使用し、Content Script / Background双方が必要な設定を更新できるようにする。
---
## 18. Security Design
### 18.1 Trust Boundary
```text
UNTRUSTED
Web Page / DOM / Selection
│
▼
Content Script
│ structured messages only
▼
PRIVILEGED
Background / Ollama access / storage
```
### 18.2 XSS対策
- 選択文字列をHTML化しない。
- LLM出力をHTML化しない。
- Markdown rendererを初期版では導入しない。
- DOM挿入は`textContent`中心。
### 18.3 Prompt Injection対策
翻訳用途ではページ本文を命令ではなくデータとして固定system promptで扱う。
ただしLLMの挙動を完全保証するセキュリティ境界としてPromptのみへ依存しない。
### 18.4 Remote Code禁止
- CDN script禁止。
- `eval`禁止。
- `new Function`禁止。
- remote import禁止。
- npm依存はbundleに含める。
### 18.5 Storage
- `storage.local` は暗号化ストレージではない前提。
- API key等の秘密情報は初期版では扱わない。
### 18.6 Message Validation
Content側のデータをBackgroundで再検証する。
### 18.7 Resource Abuse
- MAX_SELECTION_LENGTHを設ける。
- active translationは1件。
- 新規requestで旧request abort。
- 選択だけではrequestを送らない。
---
## 19. Error Code設計
| Code | 意味 |
|---|---|
| `INVALID_REQUEST` | message形式不正 |
| `TEXT_EMPTY` | 空文字 |
| `TEXT_TOO_LONG` | 選択長超過 |
| `OLLAMA_UNAVAILABLE` | Ollama接続不可 |
| `MODEL_NOT_FOUND` | モデルなし |
| `INVALID_SETTINGS` | 設定値不正 |
| `REQUEST_ABORTED` | 新規要求等で中断 |
| `STREAM_ERROR` | streaming中エラー |
| `UNKNOWN_ERROR` | その他 |
UIでは内部stack traceを表示しない。
---
## 20. Copy設計
Clipboard APIのみを使用する。`document.execCommand('copy')` はdeprecatedのため使用しない。
- コピーはポップオーバーのコピーボタンclick(user activation内)で発生するため、`navigator.clipboard.writeText()` をそのハンドラ内で直接呼び出す。
- 書き込み失敗時は「コピーできませんでした」等の簡潔なメッセージをポップオーバーへ表示するのみとし、deprecated APIへフォールバックしない。
- Firefox実機でclipboard動作を確認する。
コピー対象は現在表示中の翻訳プレーンテキストのみ。
---
## 21. モデル一覧取得
BackgroundのOllamaClientからモデル一覧取得APIを呼び、Settings UIへ返す。
要件:
- Ollama未起動時は空一覧 + 接続エラー表示。
- 取得成功時はselect候補へ反映。
- 保存済みmodelが一覧にない場合は警告表示。
---
## 22. 実装順序
### Phase 1: Skeleton
- Manifest V3
- Background
- Content Script
- Popup / Options
- build/test基盤
### Phase 2: Selection UI
- Selection検出
- Action button
- Shadow DOM
- Popover skeleton
### Phase 3: Messaging
- runtime.Port
- message schema
- validation
- requestId
### Phase 4: Ollama
- Ollama REST client(fetch + NDJSON streaming)
- model list
- chat stream
- Abort
### Phase 4.5: Context Menu 翻訳(PDF Viewer対応)
- menus permission 追加
- menu-controller.js(selection context menu 登録、selectionText取得、popup open)
- message-routerへ request 生成共通化(port / menu / popup 経由の request が同じ設定・prompt・options生成を利用)
- backgroundに translation state 共有(requestId/sourceText/translatedText/status/model/error)
- Action Popup 翻訳結果モード表示
### Phase 5: Settings
- storage.local
- Basic settings
- Advanced settings
- Context slider
- Temperature slider
- Seed modes
### Phase 6: Security Hardening
- textContent確認
- CSP確認
- message validation
- input length limits
- no remote code
- permissions audit
### Phase 7: Tests
- unit
- integration
- Firefox実機
### Phase 8: Final Verification
- lint/test/build
- temporary add-on install
- acceptance criteriaを全件確認
---
## 23. テスト設計
### 23.1 Unit
| 対象 | ケース |
|---|---|
| settings schema | default / valid / invalid |
| temperature | 0.1刻み / 範囲外 |
| contextLength | presetのみ許可 |
| seed | random / fixed / invalid integer |
| advanced options | null除外、数値反映 |
| prompt builder | fixed + user prompt結合 |
| message validation | malformed message拒否 |
| length limit | 境界値 |
| selectionText validation | context menu selectionText の型/trim/空/最大長 |
| popup state | status遷移、requestId/sourceText/translatedText/model/error更新 |
### 23.2 Integration
Mock Ollama serverまたはmock clientを使い以下を確認。
```text
Content request
→ Background validation
→ Ollama stream
→ chunk forwarding
→ complete
```
Context menu flow:
```text
menus selection (selectionText)
→ validation
→ translation request
→ mock Ollama stream
→ TranslationSession
→ popup result state更新
```
Abortテスト:
```text
request A start
request B start
A abort
B chunks only displayed
```
### 23.3 Firefox Manual/E2E
対象ページ:
- 単純HTML
- 長文記事
- 選択文字列にHTMLタグ表現を含むページ
- CSSが強く指定されたページ
- スクロール位置が深いページ
- Firefox内蔵PDF Viewer(context menu翻訳)
確認:
- 選択位置近傍UI
- viewport端補正
- scroll時挙動
- selection解除
- copy
- regenerate
- close
- settings persistence
- Ollama down/up
- context menu表示(通常ページ / PDF Viewer)
- context menu翻訳→popup streaming表示
- popup copy / regenerate
- 新規翻訳時の旧request cancel
- PDF上のHTML/script風文字列が実行されないこと
---
## 24. Firefox API監査チェックリスト
実装完了時に以下を確認する。
- [ ] Manifest V3
- [ ] `action` 使用
- [ ] `background.scripts` 使用
- [ ] `background.service_worker` 不使用
- [ ] `browser_action` 不使用
- [ ] `tabs.executeScript()` 不使用
- [ ] `runtime.getBackgroundPage()` 不使用
- [ ] `window.localStorage` 不使用
- [ ] `storage.local` 使用
- [ ] `runtime.Port` streaming通信
- [ ] Content ScriptからOllama fetchなし
- [ ] `eval` / `new Function`なし
- [ ] Remote JSなし
- [ ] LLM出力への`innerHTML`なし
- [ ] 不要permissionsなし
- [ ] iframe初期対応なし
- [ ] `menus` permission は selection context translationのみに使用
- [ ] PDF Viewer内部DOM / PDF.js内部オブジェクトへのアクセスなし
---
## 25. 完了時にLLMへ要求する成果物
実装担当LLMは最終的に以下を提示すること。
1. 実装済みソースコード一式
2. `manifest.json`
3. build手順
4. Firefox一時アドオンとしてのロード手順
5. Ollama起動/モデル準備手順
6. 単体テスト結果
7. 結合テスト結果
8. Firefox実機動作確認結果
9. 使用WebExtensions API一覧
10. 要求permissions一覧と理由
11. セキュリティチェック結果
12. 未解決事項・既知制限
テストを実行せず「実装したので動くはず」として完了扱いにしてはならない。実際に実行可能なテストは実行し、Firefox実機確認項目も可能な範囲で実行結果を記録する。
|