agim-cli 1.1.2 → 1.1.4

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.
@@ -5,6 +5,38 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Agim — Reminders</title>
7
7
  <script src="/_app.js"></script>
8
+ <script>
9
+ // i18n bootstrap — mirrors tasks.html. _app.js reads window.__lang.
10
+ (function () {
11
+ const LANGS = { en: 'en', zh: 'zh' };
12
+ const savedLang = localStorage.getItem('im-hub-lang');
13
+ const browserLang = navigator.language.startsWith('zh') ? 'zh' : 'en';
14
+ window.__lang = savedLang && LANGS[savedLang] ? savedLang : browserLang;
15
+ document.documentElement.lang = window.__lang === 'zh' ? 'zh-CN' : 'en';
16
+ const T = {
17
+ en: {
18
+ title: 'Agim — Reminders', h1: 'Reminders',
19
+ backToChat: 'Chat', toTasks: 'Tasks', toMemos: 'Memos', toSettings: 'Settings',
20
+ statusPending: 'Pending', statusFired: 'Fired', statusCancelled: 'Cancelled', statusFailed: 'Failed',
21
+ empty: 'No reminders matching this filter.',
22
+ searchPlaceholder: 'search…',
23
+ cancel: 'Cancel', snooze: 'Snooze', delete: 'Delete',
24
+ fireAt: 'Fires at', recur: 'Repeat',
25
+ },
26
+ zh: {
27
+ title: 'Agim — 提醒', h1: '提醒',
28
+ backToChat: '对话', toTasks: '任务', toMemos: '备忘', toSettings: '设置',
29
+ statusPending: '待发', statusFired: '已发', statusCancelled: '已取消', statusFailed: '失败',
30
+ empty: '当前过滤下暂无提醒。',
31
+ searchPlaceholder: '搜索…',
32
+ cancel: '取消', snooze: '推迟', delete: '删除',
33
+ fireAt: '触发时间', recur: '重复',
34
+ },
35
+ };
36
+ window.__t = T[window.__lang];
37
+ document.title = T[window.__lang].title;
38
+ })();
39
+ </script>
8
40
  <style>
9
41
  :root {
10
42
  --bg: #fafafa; --fg: #222; --muted: #666; --border: #e5e7eb;
@@ -86,20 +118,25 @@
86
118
  </head>
87
119
  <body>
88
120
  <header>
89
- <h1>🔔 Reminders</h1>
121
+ <h1>🔔 <span id="page-title"></span></h1>
122
+ <button id="theme-toggle" type="button" aria-label="Toggle color theme"></button>
123
+ <select id="langSelect" title="Language / 语言" style="margin-right:8px">
124
+ <option value="en">EN</option>
125
+ <option value="zh">中文</option>
126
+ </select>
90
127
  <nav>
91
- <a href="/">Chat</a>
92
- <a href="/tasks">Tasks</a>
93
- <a href="/memos">Memos</a>
94
- <a href="/settings">Settings</a>
128
+ <a href="/" id="lnk-chat"></a>
129
+ <a href="/tasks" id="lnk-tasks"></a>
130
+ <a href="/memos" id="lnk-memos"></a>
131
+ <a href="/settings" id="lnk-settings"></a>
95
132
  </nav>
96
133
  </header>
97
134
  <main>
98
135
  <div class="filters" id="filters">
99
- <button type="button" class="filter-btn active" data-status="pending">待发 (Pending)</button>
100
- <button type="button" class="filter-btn" data-status="fired">已发</button>
101
- <button type="button" class="filter-btn" data-status="cancelled">已取消</button>
102
- <button type="button" class="filter-btn" data-status="failed">失败</button>
136
+ <button type="button" class="filter-btn active" data-status="pending" id="btn-status-pending"></button>
137
+ <button type="button" class="filter-btn" data-status="fired" id="btn-status-fired"></button>
138
+ <button type="button" class="filter-btn" data-status="cancelled" id="btn-status-cancelled"></button>
139
+ <button type="button" class="filter-btn" data-status="failed" id="btn-status-failed"></button>
103
140
  </div>
104
141
  <div id="list"></div>
105
142
  </main>
@@ -107,11 +144,34 @@
107
144
 
108
145
  <script>
109
146
  (() => {
147
+ const T = window.__t;
110
148
  const $list = document.getElementById('list')
111
149
  const $toast = document.getElementById('toast')
112
150
  const $filters = document.getElementById('filters')
113
151
  let currentStatus = 'pending'
114
152
 
153
+ // i18n bind — header labels, status filter labels, lang switcher.
154
+ document.getElementById('page-title').textContent = T.h1;
155
+ document.getElementById('lnk-chat').textContent = T.backToChat;
156
+ document.getElementById('lnk-tasks').textContent = T.toTasks;
157
+ document.getElementById('lnk-memos').textContent = T.toMemos;
158
+ document.getElementById('lnk-settings').textContent = T.toSettings;
159
+ document.getElementById('btn-status-pending').textContent = T.statusPending;
160
+ document.getElementById('btn-status-fired').textContent = T.statusFired;
161
+ document.getElementById('btn-status-cancelled').textContent = T.statusCancelled;
162
+ document.getElementById('btn-status-failed').textContent = T.statusFailed;
163
+ if (window.imhub) imhub.theme.bindToggle(document.getElementById('theme-toggle'));
164
+ (function setupLangSwitcher() {
165
+ const sel = document.getElementById('langSelect');
166
+ if (!sel) return;
167
+ sel.value = window.__lang;
168
+ sel.addEventListener('change', () => {
169
+ if (sel.value === window.__lang) return;
170
+ localStorage.setItem('im-hub-lang', sel.value);
171
+ window.location.reload();
172
+ });
173
+ })();
174
+
115
175
  function toast(msg, isErr = false) {
116
176
  $toast.textContent = msg
117
177
  $toast.className = `toast${isErr ? ' error' : ''}`
@@ -119,17 +179,31 @@
119
179
  setTimeout(() => { $toast.style.display = 'none' }, 2500)
120
180
  }
121
181
 
122
- function fmtTime(iso) {
182
+ function fmtTime(s) {
183
+ if (!s) return '-'
184
+ // Normalize SQLite "YYYY-MM-DD HH:MM:SS" to RFC3339 — Safari/WebView
185
+ // reject the space-separated form (Invalid Date).
186
+ let iso = String(s).trim()
187
+ if (iso && !iso.includes('T')) iso = iso.replace(' ', 'T')
188
+ if (iso && !/[Z+]/.test(iso.slice(-6))) iso = iso + 'Z'
123
189
  const d = new Date(iso)
124
- if (Number.isNaN(d.getTime())) return iso
190
+ if (Number.isNaN(d.getTime())) return s
191
+ const isZh = window.__lang === 'zh'
125
192
  const now = new Date()
126
193
  const diffMs = d.getTime() - now.getTime()
127
194
  const absMin = Math.abs(Math.round(diffMs / 60000))
195
+ const past = diffMs <= 0
128
196
  let rel = ''
129
- if (Math.abs(diffMs) < 60_000) rel = '刚才'
130
- else if (absMin < 60) rel = `${(diffMs > 0 ? '' : '已过 ') + absMin} 分钟${diffMs > 0 ? '后' : ''}`
131
- else if (absMin < 1440) rel = `${(diffMs > 0 ? '' : '已过 ') + (absMin / 60).toFixed(1)} 小时${diffMs > 0 ? '' : ''}`
132
- else rel = `${(diffMs > 0 ? '' : '已过 ') + (absMin / 1440).toFixed(1)} 天${diffMs > 0 ? '' : ''}`
197
+ if (Math.abs(diffMs) < 60_000) rel = isZh ? '刚才' : 'just now'
198
+ else if (absMin < 60) rel = isZh
199
+ ? `${past ? '已过 ' : ''}${absMin} 分钟${past ? '' : ''}`
200
+ : `${absMin} min ${past ? 'ago' : 'from now'}`
201
+ else if (absMin < 1440) rel = isZh
202
+ ? `${past ? '已过 ' : ''}${(absMin / 60).toFixed(1)} 小时${past ? '' : '后'}`
203
+ : `${(absMin / 60).toFixed(1)} h ${past ? 'ago' : 'from now'}`
204
+ else rel = isZh
205
+ ? `${past ? '已过 ' : ''}${(absMin / 1440).toFixed(1)} 天${past ? '' : '后'}`
206
+ : `${(absMin / 1440).toFixed(1)} d ${past ? 'ago' : 'from now'}`
133
207
  return `${d.toLocaleString()} · ${rel}`
134
208
  }
135
209