viveworker 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/web/i18n.js CHANGED
@@ -106,6 +106,10 @@ const translations = {
106
106
  "detail.approvalRequested": "Approval requested.",
107
107
  "detail.planReady": "Plan is ready.",
108
108
  "detail.previousMessage": "Previous message",
109
+ "detail.interruptedTask": "Interrupted task",
110
+ "detail.imageAlt": ({ index }) => `Attached image ${index}`,
111
+ "detail.filesTitle": "Files",
112
+ "detail.turnAbortedNotice": "This task was interrupted.",
109
113
  "detail.macOnlyChoice": "Please answer this type of input on the Mac.",
110
114
  "detail.detailUnavailable": "Detail not available.",
111
115
  "detail.pageProgress": ({ page, totalPages }) => `Page ${page} / ${totalPages}`,
@@ -115,10 +119,11 @@ const translations = {
115
119
  "reply.fieldLabel": "Message",
116
120
  "reply.placeholder": "Ask Codex to continue, refine the result, or try a different approach.",
117
121
  "reply.imageLabel": "Image",
118
- "reply.imageAdd": "Add image",
122
+ "reply.imageAdd": "Add images",
123
+ "reply.imageAddMore": "Add more images",
119
124
  "reply.imageReplace": "Replace image",
120
125
  "reply.imageRemove": "Remove",
121
- "reply.imageHint": "Attach one image from your iPhone and send it together with your message.",
126
+ "reply.imageHint": "Attach up to {count} images from your iPhone and send them together with your message.",
122
127
  "reply.imageAttached": "Attached image",
123
128
  "reply.send": "Send to Codex",
124
129
  "reply.sendConfirm": "Send anyway",
@@ -163,6 +168,19 @@ const translations = {
163
168
  "timeline.allThreads": "All",
164
169
  "timeline.unknownThread": "Unknown thread",
165
170
  "timeline.filterLabel": "Thread filter",
171
+ "timeline.status.approved": "Approved",
172
+ "timeline.status.rejected": "Rejected",
173
+ "timeline.status.implemented": "Started",
174
+ "timeline.status.dismissed": "Dismissed",
175
+ "timeline.status.submitted": "Sent",
176
+ "timeline.kindFilterLabel": "Event filter",
177
+ "timeline.kindFilterButtonLabel": "Filter timeline events",
178
+ "timeline.kindFilter.all": "All events",
179
+ "timeline.kindFilter.messages": "Messages",
180
+ "timeline.kindFilter.approvals": "Approvals",
181
+ "timeline.kindFilter.plans": "Plans",
182
+ "timeline.kindFilter.choices": "Choices",
183
+ "timeline.kindFilter.completions": "Completed",
166
184
  "settings.intro": "Check pairing, language, install status, and Web Push health for this iPhone.",
167
185
  "settings.section.overview": "Quick setup",
168
186
  "settings.section.notifications": "Notifications",
@@ -293,9 +311,8 @@ const translations = {
293
311
  "error.completionReplyEmpty": "Enter a message before sending it.",
294
312
  "error.completionReplyImageInvalidType": "Choose an image file to attach.",
295
313
  "error.completionReplyImageTooLarge": "That image is too large to send from this reply screen.",
296
- "error.completionReplyImageLimit": "Attach one image at a time for now.",
314
+ "error.completionReplyImageLimit": "Attach up to {count} images at a time.",
297
315
  "error.completionReplyImageInvalidUpload": "That image could not be uploaded.",
298
- "error.completionReplyImageDisabled": "Image attachments are not available in this release.",
299
316
  "error.codexIpcNotConnected": "Codex desktop is not connected right now.",
300
317
  "error.approvalNotFound": "This approval is no longer available.",
301
318
  "error.approvalAlreadyHandled": "This approval was already handled.",
@@ -329,6 +346,7 @@ const translations = {
329
346
  "server.message.approvalNeededInCodex": "Approval needed in Codex.",
330
347
  "server.message.extraApprovals": ({ count }) => `Extra approvals: ${count}`,
331
348
  "server.message.taskFinished": "Task finished.",
349
+ "server.message.turnAborted": "This task was interrupted.",
332
350
  "server.message.planReady": "Plan is ready.",
333
351
  "server.message.choiceSubmitted": "The selected answers were sent to Codex.",
334
352
  "server.message.choiceSubmittedTest": "The test answers were received.",
@@ -559,6 +577,10 @@ const translations = {
559
577
  "detail.approvalRequested": "承認が必要です。",
560
578
  "detail.planReady": "プランの確認が必要です。",
561
579
  "detail.previousMessage": "ひとつ前のメッセージ",
580
+ "detail.interruptedTask": "中断されたタスク",
581
+ "detail.imageAlt": ({ index }) => `添付画像 ${index}`,
582
+ "detail.filesTitle": "関連ファイル",
583
+ "detail.turnAbortedNotice": "タスクを中断しました。",
562
584
  "detail.macOnlyChoice": "この種類の入力は Mac で回答してください。",
563
585
  "detail.detailUnavailable": "詳細は利用できません。",
564
586
  "detail.pageProgress": ({ page, totalPages }) => `${page} / ${totalPages} ページ`,
@@ -569,9 +591,10 @@ const translations = {
569
591
  "reply.placeholder": "続きを依頼したり、結果の修正や別案を Codex に伝えます。",
570
592
  "reply.imageLabel": "画像",
571
593
  "reply.imageAdd": "画像を追加",
594
+ "reply.imageAddMore": "さらに画像を追加",
572
595
  "reply.imageReplace": "画像を差し替え",
573
596
  "reply.imageRemove": "削除",
574
- "reply.imageHint": "iPhone の画像を 1 枚だけ添付して、メッセージと一緒に送れます。",
597
+ "reply.imageHint": "iPhone の画像を最大 {count} 枚まで添付して、メッセージと一緒に送れます。",
575
598
  "reply.imageAttached": "添付した画像",
576
599
  "reply.send": "Codex に送信",
577
600
  "reply.sendConfirm": "それでも送信",
@@ -616,6 +639,19 @@ const translations = {
616
639
  "timeline.allThreads": "すべて",
617
640
  "timeline.unknownThread": "不明なスレッド",
618
641
  "timeline.filterLabel": "スレッドフィルター",
642
+ "timeline.status.approved": "承認済み",
643
+ "timeline.status.rejected": "拒否済み",
644
+ "timeline.status.implemented": "実装開始",
645
+ "timeline.status.dismissed": "見送り",
646
+ "timeline.status.submitted": "送信済み",
647
+ "timeline.kindFilterLabel": "イベントフィルター",
648
+ "timeline.kindFilterButtonLabel": "タイムラインのイベントを絞り込む",
649
+ "timeline.kindFilter.all": "すべてのイベント",
650
+ "timeline.kindFilter.messages": "メッセージ",
651
+ "timeline.kindFilter.approvals": "承認",
652
+ "timeline.kindFilter.plans": "プラン",
653
+ "timeline.kindFilter.choices": "選択",
654
+ "timeline.kindFilter.completions": "完了",
619
655
  "settings.intro": "この iPhone のペアリング、言語、インストール状況、Web Push の状態を確認できます。",
620
656
  "settings.section.overview": "クイック確認",
621
657
  "settings.section.notifications": "通知",
@@ -746,9 +782,8 @@ const translations = {
746
782
  "error.completionReplyEmpty": "送信前にメッセージを入力してください。",
747
783
  "error.completionReplyImageInvalidType": "添付できるのは画像ファイルのみです。",
748
784
  "error.completionReplyImageTooLarge": "この画面から送るには画像が大きすぎます。",
749
- "error.completionReplyImageLimit": "いまは画像を 1 枚ずつ添付してください。",
785
+ "error.completionReplyImageLimit": "画像は最大 {count} 枚まで添付できます。",
750
786
  "error.completionReplyImageInvalidUpload": "この画像はアップロードできませんでした。",
751
- "error.completionReplyImageDisabled": "このリリースでは画像添付はまだ利用できません。",
752
787
  "error.codexIpcNotConnected": "いまは Codex desktop に接続できていません。",
753
788
  "error.approvalNotFound": "この承認はもう利用できません。",
754
789
  "error.approvalAlreadyHandled": "この承認はすでに処理済みです。",
@@ -782,6 +817,7 @@ const translations = {
782
817
  "server.message.approvalNeededInCodex": "Codex で承認が必要です。",
783
818
  "server.message.extraApprovals": ({ count }) => `追加の承認: ${count}`,
784
819
  "server.message.taskFinished": "タスクが完了しました。",
820
+ "server.message.turnAborted": "タスクを中断しました。",
785
821
  "server.message.planReady": "プランの確認が必要です。",
786
822
  "server.message.choiceSubmitted": "選択内容を Codex に送信しました。",
787
823
  "server.message.choiceSubmittedTest": "テスト回答を受け取りました。",
package/web/sw.js CHANGED
@@ -1,4 +1,6 @@
1
- const CACHE_NAME = "viveworker-v5";
1
+ const CACHE_NAME = "viveworker-v7";
2
+ const NOTIFICATION_INTENT_CACHE = "viveworker-notification-intent-v1";
3
+ const NOTIFICATION_INTENT_PATH = "/__viveworker_notification_intent__";
2
4
  const APP_ASSETS = ["/app.css", "/app.js", "/i18n.js"];
3
5
  const APP_ROUTES = new Set(["/", "/app", "/app/"]);
4
6
  const CACHED_PATHS = new Set(APP_ASSETS);
@@ -74,6 +76,7 @@ self.addEventListener("push", (event) => {
74
76
  });
75
77
 
76
78
  self.addEventListener("notificationclick", (event) => {
79
+ event.preventDefault?.();
77
80
  event.notification.close();
78
81
  const targetUrl = event.notification?.data?.url || "/app";
79
82
  event.waitUntil(openTargetWindow(targetUrl));
@@ -85,20 +88,26 @@ self.addEventListener("pushsubscriptionchange", (event) => {
85
88
 
86
89
  async function openTargetWindow(targetUrl) {
87
90
  const target = new URL(targetUrl, self.location.origin);
91
+ await persistNotificationIntent(target.toString());
88
92
  const clients = await self.clients.matchAll({
89
93
  type: "window",
90
94
  includeUncontrolled: true,
91
95
  });
96
+ broadcastTargetUrl(target.toString(), clients);
92
97
 
93
98
  const preferredClients = clients
94
99
  .slice()
95
- .sort((left, right) => scoreClient(right.url) - scoreClient(left.url));
100
+ .sort((left, right) => scoreClient(right) - scoreClient(left));
96
101
 
97
102
  for (const client of preferredClients) {
98
103
  if (typeof client.focus === "function") {
99
104
  if (typeof client.navigate === "function") {
100
- await client.navigate(target.toString());
105
+ await client.navigate(target.toString()).catch(() => {});
101
106
  }
107
+ client.postMessage({
108
+ type: "open-target-url",
109
+ url: target.toString(),
110
+ });
102
111
  await client.focus();
103
112
  return;
104
113
  }
@@ -109,9 +118,9 @@ async function openTargetWindow(targetUrl) {
109
118
  }
110
119
  }
111
120
 
112
- function scoreClient(urlString) {
121
+ function scoreClient(client) {
113
122
  try {
114
- const url = new URL(urlString);
123
+ const url = new URL(client?.url || "");
115
124
  let score = 0;
116
125
  if (APP_ROUTES.has(url.pathname)) {
117
126
  score += 20;
@@ -119,12 +128,50 @@ function scoreClient(urlString) {
119
128
  if (url.pathname === "/app" || url.pathname === "/app/") {
120
129
  score += 5;
121
130
  }
131
+ if (client?.focused) {
132
+ score += 4;
133
+ }
134
+ if (client?.visibilityState === "visible") {
135
+ score += 2;
136
+ }
122
137
  return score;
123
138
  } catch {
124
139
  return 0;
125
140
  }
126
141
  }
127
142
 
143
+ function broadcastTargetUrl(url, clients) {
144
+ for (const client of clients) {
145
+ client.postMessage({
146
+ type: "open-target-url",
147
+ url,
148
+ });
149
+ }
150
+ }
151
+
152
+ async function persistNotificationIntent(url) {
153
+ try {
154
+ const cache = await caches.open(NOTIFICATION_INTENT_CACHE);
155
+ const request = new Request(NOTIFICATION_INTENT_PATH);
156
+ const response = new Response(
157
+ JSON.stringify({
158
+ url,
159
+ nonce: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
160
+ createdAtMs: Date.now(),
161
+ }),
162
+ {
163
+ headers: {
164
+ "Content-Type": "application/json",
165
+ "Cache-Control": "no-store",
166
+ },
167
+ }
168
+ );
169
+ await cache.put(request, response);
170
+ } catch {
171
+ // Best-effort fallback for iOS warm-start notification routing.
172
+ }
173
+ }
174
+
128
175
  async function notifyClients(type) {
129
176
  const clients = await self.clients.matchAll({
130
177
  type: "window",