dsh-mishu 0.0.1 → 0.5.0

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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +271 -0
  3. package/cordis.patch.yml +5 -0
  4. package/lib/client.js +1242 -0
  5. package/lib/core/ask.js +132 -0
  6. package/lib/core/board.js +200 -0
  7. package/lib/core/brief.js +208 -0
  8. package/lib/core/explain.js +71 -0
  9. package/lib/core/fold.js +89 -0
  10. package/lib/core/github/client.js +142 -0
  11. package/lib/core/github/facts.js +173 -0
  12. package/lib/core/github/rules.js +274 -0
  13. package/lib/core/github/sync.js +112 -0
  14. package/lib/core/i18n.js +54 -0
  15. package/lib/core/index.js +65 -0
  16. package/lib/core/mail/account.js +76 -0
  17. package/lib/core/mail/imap.js +98 -0
  18. package/lib/core/mail/rules.js +69 -0
  19. package/lib/core/mail/sync.js +69 -0
  20. package/lib/core/manual.js +157 -0
  21. package/lib/core/matters.js +54 -0
  22. package/lib/core/recur.js +73 -0
  23. package/lib/core/secrets.js +122 -0
  24. package/lib/core/settings.js +95 -0
  25. package/lib/core/store.js +322 -0
  26. package/lib/core/time.js +111 -0
  27. package/lib/dsh/api.js +264 -0
  28. package/lib/dsh/config.js +23 -0
  29. package/lib/dsh/plugin.js +167 -0
  30. package/lib/dsh/runtime.js +116 -0
  31. package/lib/dsh/tools.js +184 -0
  32. package/lib/index.js +11 -0
  33. package/lib/types/core/ask.d.ts +40 -0
  34. package/lib/types/core/board.d.ts +108 -0
  35. package/lib/types/core/brief.d.ts +33 -0
  36. package/lib/types/core/explain.d.ts +22 -0
  37. package/lib/types/core/fold.d.ts +28 -0
  38. package/lib/types/core/github/client.d.ts +150 -0
  39. package/lib/types/core/github/facts.d.ts +104 -0
  40. package/lib/types/core/github/rules.d.ts +39 -0
  41. package/lib/types/core/github/sync.d.ts +37 -0
  42. package/lib/types/core/i18n.d.ts +31 -0
  43. package/lib/types/core/index.d.ts +42 -0
  44. package/lib/types/core/mail/account.d.ts +72 -0
  45. package/lib/types/core/mail/imap.d.ts +44 -0
  46. package/lib/types/core/mail/rules.d.ts +12 -0
  47. package/lib/types/core/mail/sync.d.ts +30 -0
  48. package/lib/types/core/manual.d.ts +54 -0
  49. package/lib/types/core/matters.d.ts +32 -0
  50. package/lib/types/core/recur.d.ts +30 -0
  51. package/lib/types/core/secrets.d.ts +35 -0
  52. package/lib/types/core/settings.d.ts +37 -0
  53. package/lib/types/core/store.d.ts +143 -0
  54. package/lib/types/core/time.d.ts +37 -0
  55. package/lib/types/dsh/api.d.ts +20 -0
  56. package/lib/types/dsh/config.d.ts +25 -0
  57. package/lib/types/dsh/plugin.d.ts +13 -0
  58. package/lib/types/dsh/runtime.d.ts +38 -0
  59. package/lib/types/dsh/tools.d.ts +43 -0
  60. package/lib/types/index.d.ts +11 -0
  61. package/package.json +50 -4
  62. package/docs//350/260/203/347/240/224/344/270/216/350/256/276/350/256/241.md +0 -90
package/lib/client.js ADDED
@@ -0,0 +1,1242 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-mishu",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ "use strict";
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
+
25
+ // src/client/index.tsx
26
+ var index_exports = {};
27
+ __export(index_exports, {
28
+ PACKAGE_NAME: () => PACKAGE_NAME,
29
+ PANEL_ID: () => PANEL_ID,
30
+ apply: () => apply,
31
+ inject: () => inject
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/client/Board.tsx
36
+ var import_react3 = require("react");
37
+
38
+ // src/client/api.ts
39
+ async function post(path, body) {
40
+ try {
41
+ const res = await fetch(new URL(`/api/dsh-mishu${path}`, document.baseURI), {
42
+ method: "POST",
43
+ credentials: "same-origin",
44
+ headers: { "content-type": "application/json" },
45
+ body: JSON.stringify(body)
46
+ });
47
+ const value = await res.json().catch(() => null);
48
+ if (value === null) return { ok: false, message: `HTTP ${res.status}` };
49
+ return value;
50
+ } catch (error) {
51
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
52
+ }
53
+ }
54
+ var mishuApi = {
55
+ board: (lang) => post("/board", { lang }),
56
+ sync: (lang) => post("/sync", { lang }),
57
+ action: (lang, target, id, action, days) => post("/action", { lang, target, id, action, ...days === void 0 ? {} : { days } }),
58
+ detail: (lang, id) => post("/detail", { lang, id }),
59
+ preview: (lang, item) => post("/preview", { lang, item }),
60
+ add: (lang, item) => post("/add", { lang, item }),
61
+ ask: (lang, ask) => post("/ask", { lang, ask })
62
+ };
63
+ var settingsApi = {
64
+ load: (lang) => post("/settings", { lang }),
65
+ save: (lang, settings) => post("/settings/save", { lang, settings }),
66
+ testGithub: (lang) => post("/github/test", { lang }),
67
+ saveMail: (lang, account, password) => post("/mail/save", { lang, account, password }),
68
+ testMail: (lang, account, password) => post("/mail/test", { lang, account, password }),
69
+ deleteMail: (lang, id) => post("/mail/delete", { lang, id }),
70
+ syncMail: (lang, id) => post("/mail/sync", { lang, id })
71
+ };
72
+
73
+ // src/client/AskAI.tsx
74
+ var import_react = require("react");
75
+
76
+ // src/client/text.ts
77
+ function uiLang(locale) {
78
+ return (locale ?? "").toLowerCase().startsWith("zh") ? "zh" : "en";
79
+ }
80
+ var zh = {
81
+ title: "\u5C0F\u79D8\u4E66",
82
+ sync: "\u540C\u6B65",
83
+ syncing: "\u540C\u6B65\u4E2D\u2026",
84
+ loading: "\u6B63\u5728\u8BFB\u53D6\u2026",
85
+ handle: "\u8981\u4F60\u5904\u7406",
86
+ remember: "\u6211\u8BB0\u7740",
87
+ done: "\u66FF\u4F60\u529E\u4E86",
88
+ metricHandle: "\u8981\u4F60\u5904\u7406",
89
+ metricOverdue: "\u8FC7\u671F",
90
+ metricSoon: "7 \u5929\u5185\u5230\u671F",
91
+ metricClosed: "\u4ECA\u5929\u66FF\u4F60\u5173\u6389",
92
+ metricShadow: "\u5F85\u4F60\u6838\u5BF9",
93
+ shadowTag: "\u5F71\u5B50",
94
+ open: "\u6253\u5F00",
95
+ markDone: "\u529E\u5B8C\u4E86",
96
+ snooze: "\u660E\u5929\u518D\u8BF4",
97
+ dismiss: "\u5FFD\u7565",
98
+ unsnooze: "\u73B0\u5728\u5904\u7406",
99
+ reopen: "\u6062\u590D",
100
+ stop: "\u4E0D\u518D\u63D0\u9192",
101
+ emptyHandle: "\u6CA1\u6709\u8981\u4F60\u5904\u7406\u7684\u4E8B\u3002",
102
+ emptyRemember: "\u8FD8\u6CA1\u6709\u8981\u8BB0\u7740\u7684\u4E8B\u3002",
103
+ emptyDone: "\u6700\u8FD1 7 \u5929\u6CA1\u6709\u66FF\u4F60\u5173\u6389\u4EC0\u4E48\u3002",
104
+ doneHint: '\u5224\u9519\u4E86\u5C31\u70B9"\u6062\u590D"\uFF0C\u8FD9\u6761\u89C4\u5219\u4F1A\u8BB0\u4E0B\u8FD9\u6B21\u7EA0\u6B63\u3002',
105
+ shadow: (n) => `\u5F71\u5B50\u6A21\u5F0F\uFF1A\u5DF2\u5224\u5B9A\u7ED3\u675F\u7684 ${n} \u6761 GitHub \u901A\u77E5\uFF0C\u6253\u5F00\u81EA\u52A8\u6807\u5DF2\u8BFB\u540E\u4F1A\u88AB\u6807\u6210\u5DF2\u8BFB\u3002\u73B0\u5728\u4E00\u6761\u4E5F\u6CA1\u6807\u3002`,
106
+ dropDone: "\u653E\u5230\u8FD9\u91CC = \u529E\u5B8C\u4E86",
107
+ dropRemember: "\u653E\u5230\u8FD9\u91CC = \u660E\u5929\u518D\u63D0\u9192",
108
+ add: "\u4EA4\u4EE3\u4E00\u4EF6\u4E8B",
109
+ addTitle: "\u4E8B\u9879",
110
+ addTitlePlaceholder: "\u4EA4\u623F\u79DF",
111
+ addDue: "\u65E5\u671F",
112
+ addRepeat: "\u91CD\u590D",
113
+ addCategory: "\u7C7B\u522B",
114
+ addNote: "\u5907\u6CE8",
115
+ repeats: { none: "\u4E0D\u91CD\u590D", weekly: "\u6BCF\u5468", monthly: "\u6BCF\u6708", yearly: "\u6BCF\u5E74" },
116
+ categories: { other: "\u5176\u4ED6", bill: "\u8D26\u5355", document: "\u8BC1\u4EF6", birthday: "\u751F\u65E5\u7EAA\u5FF5\u65E5", renewal: "\u7EED\u8D39\u7EED\u4FDD" },
117
+ save: "\u8BB0\u4E0B",
118
+ cancel: "\u53D6\u6D88",
119
+ previewHint: "\u586B\u597D\u4E8B\u9879\u548C\u65E5\u671F\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u5C0F\u79D8\u4E66\u7406\u89E3\u7684\u5185\u5BB9\u3002",
120
+ chatHint: '\u4E5F\u53EF\u4EE5\u76F4\u63A5\u5728\u5BF9\u8BDD\u91CC\u8BF4\uFF0C\u6BD4\u5982"\u62A4\u7167 2027 \u5E74 3 \u6708 10 \u65E5\u5230\u671F"\u3002',
121
+ failed: "\u64CD\u4F5C\u6CA1\u6210\u529F\uFF1A",
122
+ what: "\u662F\u4EC0\u4E48",
123
+ why: "\u95EE\u9898\u5728\u54EA",
124
+ whyDone: "\u4E3A\u4EC0\u4E48\u5173\u6389",
125
+ whyKeep: "\u60C5\u51B5",
126
+ fix: "\u600E\u4E48\u529E",
127
+ did: "\u5C0F\u79D8\u4E66\u505A\u4E86\u4EC0\u4E48",
128
+ messages: (n) => `\u6765\u81EA\u8FD9 ${n} \u6761\u901A\u77E5`,
129
+ explainDone: "\u600E\u4E48\u56DE\u4E8B",
130
+ detail: "\u8BE6\u60C5",
131
+ hide: "\u6536\u8D77",
132
+ resize: "\u62D6\u52A8\u8C03\u6574\u5BBD\u5EA6\uFF0C\u53CC\u51FB\u6062\u590D\u7B49\u5BBD",
133
+ tabBoard: "\u770B\u677F",
134
+ tabSettings: "\u8BBE\u7F6E",
135
+ all: "\u5168\u90E8",
136
+ sourceGithub: "GitHub",
137
+ sourceMail: "\u90AE\u4EF6",
138
+ sourceManual: "\u4EA4\u4EE3",
139
+ sGeneral: "\u901A\u7528",
140
+ sLanguage: "\u8BED\u8A00",
141
+ sLanguageAuto: "\u8DDF\u968F\u7CFB\u7EDF",
142
+ sLead: "\u63D0\u524D\u591A\u5C11\u5929\u63D0\u9192\uFF08\u6309\u7C7B\u522B\uFF09",
143
+ sSave: "\u4FDD\u5B58",
144
+ sSaved: "\u5DF2\u4FDD\u5B58",
145
+ sGithub: "GitHub",
146
+ sGithubOn: "\u76EF\u7740 GitHub \u901A\u77E5",
147
+ sToken: "Token",
148
+ sTokenEnv: (name) => `\u4ECE\u73AF\u5883\u53D8\u91CF ${name} \u8BFB\u53D6`,
149
+ sTokenGh: "\u7528 GitHub CLI\uFF08gh\uFF09\u767B\u5F55\u7684\u8D26\u53F7\uFF0Ctoken \u5728\u7CFB\u7EDF\u94A5\u5319\u4E32\u91CC",
150
+ sTest: "\u6D4B\u8BD5\u8FDE\u63A5",
151
+ sTesting: "\u6B63\u5728\u6D4B\u8BD5\u2026",
152
+ sGithubOk: (login) => `\u8FDE\u4E0A\u4E86\uFF0C\u8D26\u53F7 ${login}`,
153
+ sMarkRead: "\u5728 GitHub \u4E0A\u6807\u5DF2\u8BFB",
154
+ sMarkOff: "\u5173\u95ED\uFF1A\u5B8C\u5168\u4E0D\u78B0 GitHub \u4E0A\u7684\u5DF2\u8BFB\u72B6\u6001",
155
+ sMarkShadow: "\u5F71\u5B50\u6A21\u5F0F\uFF1A\u53EA\u8BB0\u4E0B\u4F1A\u6807\u54EA\u4E9B\uFF0C\u4E0D\u771F\u6807\uFF08\u63A8\u8350\u5148\u7528\uFF09",
156
+ sMarkOn: "\u6253\u5F00\uFF1A\u628A\u5224\u5B9A\u7ED3\u675F\u7684\u901A\u77E5\u5728 GitHub \u4E0A\u6807\u6210\u5DF2\u8BFB",
157
+ sMarkConfirm: '\u6253\u5F00\u540E\uFF0C\u5C0F\u79D8\u4E66\u4F1A\u5728 GitHub \u4E0A\u628A\u5224\u5B9A\u5DF2\u7ECF\u7ED3\u675F\u7684\u901A\u77E5\u6807\u6210\u5DF2\u8BFB\uFF08\u8FD8\u5728"\u8981\u4F60\u5904\u7406"\u91CC\u7684\u6C38\u8FDC\u4E0D\u4F1A\u88AB\u6807\uFF09\u3002\u6807\u9519\u7684\u53EF\u4EE5\u5728 GitHub \u4E0A\u91CD\u65B0\u6807\u6210\u672A\u8BFB\u3002\u786E\u8BA4\u6253\u5F00\u5417\uFF1F',
158
+ sConfirm: "\u786E\u8BA4\u6253\u5F00",
159
+ sCancel: "\u53D6\u6D88",
160
+ sMail: "\u90AE\u7BB1",
161
+ sMailPrivacy: "\u53EA\u8BFB\uFF1A\u5C0F\u79D8\u4E66\u7528\u53EA\u8BFB\u65B9\u5F0F\u6253\u5F00\u6536\u4EF6\u7BB1\uFF0C\u53EA\u770B\u53D1\u4EF6\u4EBA\u3001\u6536\u4EF6\u4EBA\u3001\u6807\u9898\u548C\u51E0\u4E2A\u90AE\u4EF6\u5934\uFF0C\u4E0D\u8BFB\u6B63\u6587\uFF0C\u4E5F\u4E0D\u4F1A\u628A\u90AE\u4EF6\u6807\u6210\u5DF2\u8BFB\u3002",
162
+ sSecretsAt: (kind) => `\u5BC6\u7801\u4FDD\u5B58\u5728\uFF1A${kind}\uFF08\u52A0\u5BC6\u4FDD\u5B58\uFF0C\u4E0D\u5199\u8FDB\u4EFB\u4F55\u6587\u4EF6\uFF09`,
163
+ sSecretsNone: "\u8FD9\u53F0\u7535\u8111\u4E0A\u6CA1\u627E\u5230\u53EF\u7528\u7684\u7CFB\u7EDF\u94A5\u5319\u4E32\uFF0C\u6CA1\u6CD5\u4FDD\u5B58\u5BC6\u7801\u3002\u53EF\u4EE5\u6539\u7528\u73AF\u5883\u53D8\u91CF\uFF0C\u540D\u5B57\u89C1\u6BCF\u4E2A\u8D26\u6237\u4E0B\u9762\u3002",
164
+ secretKinds: { macos: "macOS \u94A5\u5319\u4E32", windows: "Windows \u51ED\u636E\u7BA1\u7406\u5668", linux: "Linux \u5BC6\u94A5\u73AF\uFF08Secret Service\uFF09", memory: "\u5185\u5B58\uFF08\u6D4B\u8BD5\u7528\uFF09" },
165
+ sNoAccounts: "\u8FD8\u6CA1\u6709\u6DFB\u52A0\u90AE\u7BB1\u3002",
166
+ sAddAccount: "+ \u6DFB\u52A0\u90AE\u7BB1",
167
+ sEdit: "\u7F16\u8F91",
168
+ sDelete: "\u5220\u9664",
169
+ sDeleteConfirm: "\u5220\u9664\u8FD9\u4E2A\u90AE\u7BB1\u8D26\u6237\uFF1F\u5B83\u7684\u5BC6\u7801\u4F1A\u4ECE\u94A5\u5319\u4E32\u91CC\u5220\u6389\uFF0C\u8FD8\u6CA1\u5904\u7406\u7684\u90AE\u4EF6\u4E8B\u9879\u4F1A\u88AB\u5173\u6389\u3002",
170
+ sCheckNow: "\u73B0\u5728\u6536\u4E00\u6B21",
171
+ sProvider: "\u90AE\u7BB1\u670D\u52A1\u5546",
172
+ sAddress: "\u90AE\u7BB1\u5730\u5740",
173
+ sLabel: "\u540D\u79F0\uFF08\u53EF\u9009\uFF09",
174
+ sUser: "\u7528\u6237\u540D\uFF08\u4E00\u822C\u5C31\u662F\u90AE\u7BB1\u5730\u5740\uFF09",
175
+ sPassword: "\u5BC6\u7801 / \u6388\u6743\u7801",
176
+ sPasswordKeep: "\u5DF2\u4FDD\u5B58\uFF1B\u4E0D\u6539\u5C31\u7559\u7A7A",
177
+ sHost: "IMAP \u670D\u52A1\u5668",
178
+ sPort: "\u7AEF\u53E3",
179
+ sSecure: "SSL/TLS",
180
+ sSince: "\u53EA\u770B\u6700\u8FD1\u51E0\u5929\u7684\u672A\u8BFB\u90AE\u4EF6",
181
+ sEnabled: "\u542F\u7528",
182
+ sMailOk: (n) => `\u8FDE\u4E0A\u4E86\uFF0C\u6700\u8FD1\u6709 ${n} \u5C01\u672A\u8BFB\u90AE\u4EF6`,
183
+ sEnvHint: (name) => `\u6CA1\u6709\u94A5\u5319\u4E32\u65F6\uFF0C\u53EF\u4EE5\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ${name}`,
184
+ sNeverChecked: "\u8FD8\u6CA1\u6536\u8FC7",
185
+ sFeishu: "\u98DE\u4E66",
186
+ sComing: "\u5373\u5C06\u63A5\u5165",
187
+ ai: "+AI",
188
+ aiTitle: "\u5728\u5BF9\u8BDD\u91CC\u95EE AI\uFF1A\u628A\u8FD9\u4EF6\u4E8B\u653E\u8FDB\u4E00\u4E2A\u65B0\u5BF9\u8BDD\u7684\u8F93\u5165\u6846\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u53D1\u9001\uFF0C\u4F60\u770B\u8FC7\u518D\u6309\u53D1\u9001",
189
+ aiOpened: "\u5DF2\u653E\u8FDB\u65B0\u5BF9\u8BDD\u7684\u8F93\u5165\u6846\uFF0C\u770B\u8FC7\u540E\u6309\u53D1\u9001\u5C31\u884C",
190
+ aiCopied: "\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F\uFF0C\u7C98\u8D34\u5230\u5BF9\u8BDD\u91CC\u5C31\u80FD\u95EE",
191
+ aiFailed: "\u6CA1\u80FD\u6253\u5F00\u5BF9\u8BDD\uFF1A"
192
+ };
193
+ var en = {
194
+ title: "Secretary",
195
+ sync: "Sync",
196
+ syncing: "Syncing\u2026",
197
+ loading: "Loading\u2026",
198
+ handle: "To handle",
199
+ remember: "Keeping for you",
200
+ done: "Done for you",
201
+ metricHandle: "To handle",
202
+ metricOverdue: "overdue",
203
+ metricSoon: "Due in 7 days",
204
+ metricClosed: "Closed today",
205
+ metricShadow: "To review",
206
+ shadowTag: "shadow",
207
+ open: "Open",
208
+ markDone: "Done",
209
+ snooze: "Tomorrow",
210
+ dismiss: "Dismiss",
211
+ unsnooze: "Handle now",
212
+ reopen: "Reopen",
213
+ stop: "Stop",
214
+ emptyHandle: "Nothing needs you right now.",
215
+ emptyRemember: "Nothing to keep yet.",
216
+ emptyDone: "Nothing closed in the last 7 days.",
217
+ doneHint: "If one is wrong, reopen it; the rule records the correction.",
218
+ shadow: (n) => `Shadow mode: ${n} resolved GitHub notifications would be marked read once you turn it on. None has been.`,
219
+ dropDone: "Drop here to mark done",
220
+ dropRemember: "Drop here to snooze until tomorrow",
221
+ add: "Tell me something",
222
+ addTitle: "What",
223
+ addTitlePlaceholder: "Pay rent",
224
+ addDue: "Date",
225
+ addRepeat: "Repeat",
226
+ addCategory: "Category",
227
+ addNote: "Note",
228
+ repeats: { none: "Once", weekly: "Weekly", monthly: "Monthly", yearly: "Yearly" },
229
+ categories: { other: "Other", bill: "Bill", document: "Document", birthday: "Birthday", renewal: "Renewal" },
230
+ save: "Save",
231
+ cancel: "Cancel",
232
+ previewHint: "Fill in what and when to see what the secretary understood.",
233
+ chatHint: 'You can also say it in the chat, e.g. "my passport expires on 2027-03-10".',
234
+ failed: "That did not work: ",
235
+ what: "What",
236
+ why: "The problem",
237
+ whyDone: "Why it was closed",
238
+ whyKeep: "Status",
239
+ fix: "What to do",
240
+ did: "What the secretary did",
241
+ messages: (n) => `From these ${n} notifications`,
242
+ explainDone: "What happened",
243
+ detail: "Details",
244
+ hide: "Hide",
245
+ resize: "Drag to resize; double-click to make them equal",
246
+ tabBoard: "Board",
247
+ tabSettings: "Settings",
248
+ all: "All",
249
+ sourceGithub: "GitHub",
250
+ sourceMail: "Mail",
251
+ sourceManual: "Noted",
252
+ sGeneral: "General",
253
+ sLanguage: "Language",
254
+ sLanguageAuto: "Follow the system",
255
+ sLead: "Days of warning, by category",
256
+ sSave: "Save",
257
+ sSaved: "Saved",
258
+ sGithub: "GitHub",
259
+ sGithubOn: "Watch GitHub notifications",
260
+ sToken: "Token",
261
+ sTokenEnv: (name) => `Read from the environment variable ${name}`,
262
+ sTokenGh: "The account the GitHub CLI (gh) is signed in to; the token stays in the system keychain",
263
+ sTest: "Test connection",
264
+ sTesting: "Testing\u2026",
265
+ sGithubOk: (login) => `Connected as ${login}`,
266
+ sMarkRead: "Mark read on GitHub",
267
+ sMarkOff: "Off: never touch read state on GitHub",
268
+ sMarkShadow: "Shadow: only record what would be marked (start here)",
269
+ sMarkOn: "On: mark resolved notifications read on GitHub",
270
+ sMarkConfirm: 'Once on, the secretary marks notifications it judged resolved as read on GitHub (anything still under "To handle" is never marked). You can mark them unread again on GitHub. Turn it on?',
271
+ sConfirm: "Turn on",
272
+ sCancel: "Cancel",
273
+ sMail: "Mail",
274
+ sMailPrivacy: "Read-only: the secretary opens your inbox read-only and looks at sender, recipients, subject and a few headers only. It never reads bodies and never marks mail read.",
275
+ sSecretsAt: (kind) => `Passwords are kept in: ${kind} (encrypted, never written to a file)`,
276
+ sSecretsNone: "No usable system keychain on this computer, so passwords cannot be saved. Use the environment variable shown under each account instead.",
277
+ secretKinds: { macos: "the macOS Keychain", windows: "Windows Credential Manager", linux: "the Linux keyring (Secret Service)", memory: "memory (tests)" },
278
+ sNoAccounts: "No mail account yet.",
279
+ sAddAccount: "+ Add a mail account",
280
+ sEdit: "Edit",
281
+ sDelete: "Delete",
282
+ sDeleteConfirm: "Delete this mail account? Its password is removed from the keychain and its open mail items are closed.",
283
+ sCheckNow: "Check now",
284
+ sProvider: "Provider",
285
+ sAddress: "Email address",
286
+ sLabel: "Name (optional)",
287
+ sUser: "User name (usually the address)",
288
+ sPassword: "Password / app password",
289
+ sPasswordKeep: "Saved; leave empty to keep it",
290
+ sHost: "IMAP server",
291
+ sPort: "Port",
292
+ sSecure: "SSL/TLS",
293
+ sSince: "Only look at unread mail from the last \u2026 days",
294
+ sEnabled: "Enabled",
295
+ sMailOk: (n) => `Connected; ${n} unread messages recently`,
296
+ sEnvHint: (name) => `Without a keychain, set the environment variable ${name}`,
297
+ sNeverChecked: "Not checked yet",
298
+ sFeishu: "Feishu / Lark",
299
+ sComing: "Coming soon",
300
+ ai: "+AI",
301
+ aiTitle: "Ask AI in a chat: puts this into a new chat's input box, unsent; read it, then press send",
302
+ aiOpened: "Put into a new chat's input box; read it and press send",
303
+ aiCopied: "Copied; paste it into a chat to ask",
304
+ aiFailed: "Could not open a chat: "
305
+ };
306
+ var TEXT = { zh, en };
307
+
308
+ // src/client/AskAI.tsx
309
+ var import_jsx_runtime = require("react/jsx-runtime");
310
+ async function copy(text) {
311
+ try {
312
+ await navigator.clipboard.writeText(text);
313
+ return true;
314
+ } catch {
315
+ return false;
316
+ }
317
+ }
318
+ function AskAI({ topic, lang, locale, openChat: openChat2, compact = false }) {
319
+ const t = TEXT[lang];
320
+ const [note, setNote] = (0, import_react.useState)();
321
+ const [busy, setBusy] = (0, import_react.useState)(false);
322
+ const ask = async () => {
323
+ setBusy(true);
324
+ const result = await mishuApi.ask(locale(), topic);
325
+ setBusy(false);
326
+ if (!result.ok) {
327
+ setNote({ ok: false, text: `${t.aiFailed}${result.message}` });
328
+ return;
329
+ }
330
+ if (openChat2(result.prompt)) {
331
+ setNote({ ok: true, text: t.aiOpened });
332
+ } else {
333
+ setNote(await copy(result.prompt) ? { ok: true, text: t.aiCopied } : { ok: false, text: `${t.aiFailed}clipboard` });
334
+ }
335
+ setTimeout(() => setNote(void 0), 6e3);
336
+ };
337
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "mishu-ai-wrap", children: [
338
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
339
+ "button",
340
+ {
341
+ type: "button",
342
+ className: `mishu-ai ${compact ? "compact" : ""}`,
343
+ title: t.aiTitle,
344
+ "aria-label": t.aiTitle,
345
+ disabled: busy,
346
+ onClick: (event) => {
347
+ event.stopPropagation();
348
+ void ask();
349
+ },
350
+ children: t.ai
351
+ }
352
+ ),
353
+ note === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `mishu-ai-note ${note.ok ? "" : "mishu-danger"}`, role: "status", children: note.text })
354
+ ] });
355
+ }
356
+
357
+ // src/client/Settings.tsx
358
+ var import_react2 = require("react");
359
+ var import_jsx_runtime2 = require("react/jsx-runtime");
360
+ var CATEGORIES = ["document", "bill", "birthday", "renewal", "other"];
361
+ function MishuSettings({ lang, locale, openChat: openChat2 }) {
362
+ const t = TEXT[lang];
363
+ const [view, setView] = (0, import_react2.useState)();
364
+ const [error, setError] = (0, import_react2.useState)();
365
+ const [note, setNote] = (0, import_react2.useState)();
366
+ const [lead, setLead] = (0, import_react2.useState)({});
367
+ const [githubTest, setGithubTest] = (0, import_react2.useState)();
368
+ const [confirmOn, setConfirmOn] = (0, import_react2.useState)(false);
369
+ const [editing, setEditing] = (0, import_react2.useState)();
370
+ const take = (result, saved) => {
371
+ if (result.ok) {
372
+ setView(result.view);
373
+ setLead(result.view.settings.leadDays);
374
+ setError(void 0);
375
+ setNote(saved);
376
+ } else {
377
+ setError(result.message);
378
+ }
379
+ };
380
+ (0, import_react2.useEffect)(() => {
381
+ void settingsApi.load(locale()).then((r) => take(r));
382
+ }, [locale]);
383
+ if (view === void 0) {
384
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-settings", children: error === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-empty", children: t.loading }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-error", children: [
385
+ t.failed,
386
+ error
387
+ ] }) });
388
+ }
389
+ const save = async (settings) => take(await settingsApi.save(locale(), settings), t.sSaved);
390
+ const token = view.github.token;
391
+ const secretKind = t.secretKinds[view.secrets.kind] ?? view.secrets.kind;
392
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-settings", children: [
393
+ error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-error", children: [
394
+ t.failed,
395
+ error
396
+ ] }),
397
+ note === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-ok", children: note }),
398
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "mishu-panel", children: [
399
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t.sGeneral }),
400
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
401
+ t.sLanguage,
402
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: view.settings.language, onChange: (e) => {
403
+ void save({ language: e.target.value });
404
+ }, children: [
405
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "auto", children: t.sLanguageAuto }),
406
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "zh", children: "\u4E2D\u6587" }),
407
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "en", children: "English" })
408
+ ] })
409
+ ] }),
410
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-field", children: t.sLead }),
411
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-grid5", children: CATEGORIES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
412
+ t.categories[c],
413
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "number", min: 0, max: 366, value: lead[c] ?? 0, onChange: (e) => setLead({ ...lead, [c]: Number(e.target.value) }) })
414
+ ] }, c)) }),
415
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn primary", onClick: () => {
416
+ void save({ leadDays: lead });
417
+ }, children: t.sSave })
418
+ ] }),
419
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "mishu-panel", children: [
420
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t.sGithub }),
421
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-check", children: [
422
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: view.settings.githubEnabled, onChange: (e) => {
423
+ void save({ githubEnabled: e.target.checked });
424
+ } }),
425
+ t.sGithubOn
426
+ ] }),
427
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-row-explain", children: [
428
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "mishu-row-label", children: t.sToken }),
429
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: token.found ? "" : "mishu-danger", children: token.found ? token.from === "env" ? t.sTokenEnv(view.github.tokenEnv) : t.sTokenGh : token.text })
430
+ ] }),
431
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-actions", children: [
432
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => {
433
+ setGithubTest(t.sTesting);
434
+ void settingsApi.testGithub(locale()).then((r) => setGithubTest(r.ok ? t.sGithubOk(r.login) : `${t.failed}${r.message}`));
435
+ }, children: t.sTest }),
436
+ githubTest === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "mishu-hint", children: githubTest })
437
+ ] }),
438
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
439
+ t.sMarkRead,
440
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: view.settings.markRead, onChange: (e) => {
441
+ const value = e.target.value;
442
+ if (value === "on") setConfirmOn(true);
443
+ else void save({ markRead: value });
444
+ }, children: [
445
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "off", children: t.sMarkOff }),
446
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "shadow", children: t.sMarkShadow }),
447
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "on", children: t.sMarkOn })
448
+ ] })
449
+ ] }),
450
+ confirmOn ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-banner", children: [
451
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: t.sMarkConfirm }),
452
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-actions", children: [
453
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn primary", onClick: () => {
454
+ setConfirmOn(false);
455
+ void save({ markRead: "on" });
456
+ }, children: t.sConfirm }),
457
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => setConfirmOn(false), children: t.sCancel })
458
+ ] })
459
+ ] }) : null
460
+ ] }),
461
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "mishu-panel", children: [
462
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t.sMail }),
463
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-hint", children: t.sMailPrivacy }),
464
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: view.secrets.available ? "mishu-hint" : "mishu-hint mishu-warn", children: view.secrets.available ? t.sSecretsAt(secretKind) : t.sSecretsNone }),
465
+ view.accounts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-empty", children: t.sNoAccounts }) : view.accounts.map((account) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
466
+ AccountRow,
467
+ {
468
+ account,
469
+ lang,
470
+ locale,
471
+ openChat: openChat2,
472
+ onEdit: () => setEditing({ ...account }),
473
+ onView: (next) => take(next)
474
+ },
475
+ account.id
476
+ )),
477
+ editing === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn mishu-add-toggle", onClick: () => setEditing({ preset: "qq", sinceDays: 14, enabled: true }), children: t.sAddAccount }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AccountForm, { draft: editing, view, lang, locale, openChat: openChat2, onDone: (next) => {
478
+ setEditing(void 0);
479
+ if (next !== void 0) take({ ok: true, view: next }, t.sSaved);
480
+ } })
481
+ ] }),
482
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "mishu-panel", children: [
483
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t.sFeishu }),
484
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mishu-hint", children: t.sComing })
485
+ ] })
486
+ ] });
487
+ }
488
+ function AccountRow({ account, lang, locale, openChat: openChat2, onEdit, onView }) {
489
+ const t = TEXT[lang];
490
+ const [busy, setBusy] = (0, import_react2.useState)(false);
491
+ const report = account.report;
492
+ const status = report === null ? t.sNeverChecked : report.error !== void 0 ? report.error[lang] : lang === "zh" ? `${new Date(report.at).toLocaleString()} \u6536\u4FE1\uFF0C${report.messages} \u5C01\u672A\u8BFB` : `checked ${new Date(report.at).toLocaleString()}, ${report.messages} unread`;
493
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-card", children: [
494
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-card-title", children: [
495
+ account.label,
496
+ account.enabled ? "" : ` (${lang === "zh" ? "\u5DF2\u505C\u7528" : "disabled"})`
497
+ ] }),
498
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-card-meta", children: [
499
+ account.address,
500
+ " \xB7 ",
501
+ account.host,
502
+ ":",
503
+ account.port
504
+ ] }),
505
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `mishu-card-note ${report?.error === void 0 ? "" : "mishu-danger"}`, children: [
506
+ status,
507
+ report?.error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AskAI, { topic: { kind: "mail-error", accountId: account.id }, lang, locale, openChat: openChat2, compact: true })
508
+ ] }),
509
+ account.hasPassword ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mishu-card-note mishu-warn", children: t.sEnvHint(account.envName) }),
510
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-actions", children: [
511
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", disabled: busy, onClick: () => {
512
+ setBusy(true);
513
+ void settingsApi.syncMail(locale(), account.id).then((r) => {
514
+ setBusy(false);
515
+ onView(r);
516
+ });
517
+ }, children: busy ? t.sTesting : t.sCheckNow }),
518
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: onEdit, children: t.sEdit }),
519
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", onClick: () => {
520
+ if (window.confirm(t.sDeleteConfirm)) void settingsApi.deleteMail(locale(), account.id).then(onView);
521
+ }, children: t.sDelete })
522
+ ] })
523
+ ] });
524
+ }
525
+ function AccountForm({ draft, view, lang, locale, openChat: openChat2, onDone }) {
526
+ const t = TEXT[lang];
527
+ const [account, setAccount] = (0, import_react2.useState)(draft);
528
+ const [password, setPassword] = (0, import_react2.useState)("");
529
+ const [message, setMessage] = (0, import_react2.useState)();
530
+ const [busy, setBusy] = (0, import_react2.useState)(false);
531
+ const preset = view.presets.find((p) => p.id === account.preset) ?? view.presets[view.presets.length - 1];
532
+ const custom = preset.id === "custom";
533
+ const existing = draft.id !== void 0;
534
+ const set = (patch) => {
535
+ setAccount({ ...account, ...patch });
536
+ setMessage(void 0);
537
+ };
538
+ const test = async () => {
539
+ setBusy(true);
540
+ setMessage({ ok: true, text: t.sTesting });
541
+ const r = await settingsApi.testMail(locale(), account, password);
542
+ setBusy(false);
543
+ setMessage(r.ok ? { ok: true, text: t.sMailOk(r.unread) } : { ok: false, text: r.message });
544
+ };
545
+ const save = async () => {
546
+ setBusy(true);
547
+ const r = await settingsApi.saveMail(locale(), account, password);
548
+ setBusy(false);
549
+ if (r.ok) {
550
+ setPassword("");
551
+ onDone(r.view);
552
+ } else {
553
+ setMessage({ ok: false, text: r.message });
554
+ }
555
+ };
556
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-add", children: [
557
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
558
+ t.sProvider,
559
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("select", { value: account.preset, onChange: (e) => set({ preset: e.target.value }), children: view.presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: p.id, children: p.label }, p.id)) })
560
+ ] }),
561
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: preset.supported ? "mishu-hint" : "mishu-hint mishu-danger", children: [
562
+ preset.howTo,
563
+ preset.supported ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AskAI, { topic: { kind: "mail-setup", preset: preset.id }, lang, locale, openChat: openChat2, compact: true }) : null
564
+ ] }),
565
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
566
+ t.sAddress,
567
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "email", value: account.address ?? "", autoComplete: "off", onChange: (e) => set({ address: e.target.value }) })
568
+ ] }),
569
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-row", children: [
570
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
571
+ t.sLabel,
572
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { value: account.label ?? "", onChange: (e) => set({ label: e.target.value }) })
573
+ ] }),
574
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
575
+ t.sUser,
576
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { value: account.user ?? "", placeholder: account.address ?? "", autoComplete: "off", onChange: (e) => set({ user: e.target.value }) })
577
+ ] })
578
+ ] }),
579
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
580
+ t.sPassword,
581
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "password", value: password, autoComplete: "new-password", placeholder: existing ? t.sPasswordKeep : "", onChange: (e) => {
582
+ setPassword(e.target.value);
583
+ setMessage(void 0);
584
+ } })
585
+ ] }),
586
+ custom ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-row", children: [
587
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
588
+ t.sHost,
589
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { value: account.host ?? "", onChange: (e) => set({ host: e.target.value }) })
590
+ ] }),
591
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
592
+ t.sPort,
593
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "number", value: account.port ?? 993, onChange: (e) => set({ port: Number(e.target.value) }) })
594
+ ] })
595
+ ] }) : null,
596
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-row", children: [
597
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-field", children: [
598
+ t.sSince,
599
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "number", min: 1, max: 90, value: account.sinceDays ?? 14, onChange: (e) => set({ sinceDays: Number(e.target.value) }) })
600
+ ] }),
601
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "mishu-check", children: [
602
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: account.enabled !== false, onChange: (e) => set({ enabled: e.target.checked }) }),
603
+ t.sEnabled
604
+ ] })
605
+ ] }),
606
+ message === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: `mishu-preview ${message.ok ? "" : "mishu-danger"}`, children: message.text }),
607
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mishu-actions", children: [
608
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", disabled: busy || !preset.supported, onClick: () => {
609
+ void test();
610
+ }, children: t.sTest }),
611
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn primary", disabled: busy || !preset.supported, onClick: () => {
612
+ void save();
613
+ }, children: t.sSave }),
614
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => onDone(), children: t.sCancel })
615
+ ] })
616
+ ] });
617
+ }
618
+
619
+ // src/client/Board.tsx
620
+ var import_jsx_runtime3 = require("react/jsx-runtime");
621
+ var SOURCE_KEY = "dsh-mishu.source";
622
+ function loadSource() {
623
+ try {
624
+ const value = localStorage.getItem(SOURCE_KEY);
625
+ if (value === "github" || value === "mail" || value === "manual") return value;
626
+ } catch {
627
+ }
628
+ return "all";
629
+ }
630
+ var REFRESH_MS = 6e4;
631
+ var WIDTHS_KEY = "dsh-mishu.columns";
632
+ var EQUAL = [1, 1, 1];
633
+ var MIN_COLUMN_PX = 220;
634
+ function dropAction(card, from, to) {
635
+ if (card.target !== "matter" || from === to) return void 0;
636
+ if (to === "done") return "done";
637
+ if (to === "remember" && from === "handle") return "snooze";
638
+ if (to === "handle" && from === "remember") return "unsnooze";
639
+ return void 0;
640
+ }
641
+ function loadWidths() {
642
+ try {
643
+ const value = JSON.parse(localStorage.getItem(WIDTHS_KEY) ?? "null");
644
+ if (Array.isArray(value) && value.length === 3 && value.every((v2) => typeof v2 === "number" && v2 > 0)) return value;
645
+ } catch {
646
+ }
647
+ return EQUAL;
648
+ }
649
+ function saveWidths(widths) {
650
+ try {
651
+ localStorage.setItem(WIDTHS_KEY, JSON.stringify(widths));
652
+ } catch {
653
+ }
654
+ }
655
+ function MishuBoard({ locale, openChat: openChat2 }) {
656
+ const [board, setBoard] = (0, import_react3.useState)();
657
+ const [error, setError] = (0, import_react3.useState)();
658
+ const [busy, setBusy] = (0, import_react3.useState)(false);
659
+ const [syncing, setSyncing] = (0, import_react3.useState)(false);
660
+ const [dropOn, setDropOn] = (0, import_react3.useState)();
661
+ const [widths, setWidths] = (0, import_react3.useState)(loadWidths);
662
+ const [tab, setTab] = (0, import_react3.useState)("board");
663
+ const [source, setSourceState] = (0, import_react3.useState)(loadSource);
664
+ const setSource = (next) => {
665
+ setSourceState(next);
666
+ try {
667
+ localStorage.setItem(SOURCE_KEY, next);
668
+ } catch {
669
+ }
670
+ };
671
+ const dragged = (0, import_react3.useRef)(void 0);
672
+ const columnsRef = (0, import_react3.useRef)(null);
673
+ const lang = board?.lang ?? uiLang(locale());
674
+ const t = TEXT[lang];
675
+ const take = (0, import_react3.useCallback)((result) => {
676
+ if (result.ok) {
677
+ setBoard(result.board);
678
+ setError(void 0);
679
+ } else {
680
+ setError(result.message);
681
+ }
682
+ }, []);
683
+ const load = (0, import_react3.useCallback)(() => mishuApi.board(locale()).then(take), [locale, take]);
684
+ (0, import_react3.useEffect)(() => {
685
+ void load();
686
+ const timer = setInterval(() => {
687
+ void load();
688
+ }, REFRESH_MS);
689
+ const onFocus = () => {
690
+ void load();
691
+ };
692
+ window.addEventListener("focus", onFocus);
693
+ return () => {
694
+ clearInterval(timer);
695
+ window.removeEventListener("focus", onFocus);
696
+ };
697
+ }, [load]);
698
+ const act = async (card2, action) => {
699
+ if (action === "open") {
700
+ if (card2.url !== null) window.open(card2.url, "_blank", "noopener");
701
+ return;
702
+ }
703
+ setBusy(true);
704
+ take(await mishuApi.action(locale(), card2.target, card2.id, action));
705
+ setBusy(false);
706
+ };
707
+ const sync = async () => {
708
+ setSyncing(true);
709
+ take(await mishuApi.sync(locale()));
710
+ setSyncing(false);
711
+ };
712
+ const startResize = (index) => (event) => {
713
+ const container = columnsRef.current;
714
+ if (container === null) return;
715
+ event.preventDefault();
716
+ const handle = event.currentTarget;
717
+ handle.setPointerCapture(event.pointerId);
718
+ const startX = event.clientX;
719
+ const start = widths;
720
+ const total = start[0] + start[1] + start[2];
721
+ const usable = container.getBoundingClientRect().width - 2 * 10;
722
+ const minFr = MIN_COLUMN_PX / usable * total;
723
+ let latest = start;
724
+ const move = (e) => {
725
+ const delta = (e.clientX - startX) / usable * total;
726
+ const pair = start[index] + start[index + 1];
727
+ const left = Math.min(pair - minFr, Math.max(minFr, start[index] + delta));
728
+ const next = [...start];
729
+ next[index] = left;
730
+ next[index + 1] = pair - left;
731
+ latest = next;
732
+ setWidths(next);
733
+ };
734
+ const up = () => {
735
+ handle.removeEventListener("pointermove", move);
736
+ handle.removeEventListener("pointerup", up);
737
+ handle.removeEventListener("pointercancel", up);
738
+ saveWidths(latest);
739
+ };
740
+ handle.addEventListener("pointermove", move);
741
+ handle.addEventListener("pointerup", up);
742
+ handle.addEventListener("pointercancel", up);
743
+ };
744
+ const resetWidths = () => {
745
+ setWidths(EQUAL);
746
+ saveWidths(EQUAL);
747
+ };
748
+ const dropProps = (column) => ({
749
+ onDragOver: (event) => {
750
+ const drag = dragged.current;
751
+ if (drag === void 0 || dropAction(drag.card, drag.from, column) === void 0) return;
752
+ event.preventDefault();
753
+ setDropOn(column);
754
+ },
755
+ onDragLeave: () => setDropOn((current) => current === column ? void 0 : current),
756
+ onDrop: (event) => {
757
+ event.preventDefault();
758
+ setDropOn(void 0);
759
+ const drag = dragged.current;
760
+ dragged.current = void 0;
761
+ const action = drag === void 0 ? void 0 : dropAction(drag.card, drag.from, column);
762
+ if (drag !== void 0 && action !== void 0) void act(drag.card, action);
763
+ }
764
+ });
765
+ const label = {
766
+ open: t.open,
767
+ done: t.markDone,
768
+ snooze: t.snooze,
769
+ dismiss: t.dismiss,
770
+ unsnooze: t.unsnooze,
771
+ reopen: t.reopen,
772
+ stop: t.stop
773
+ };
774
+ const card = (item, column) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
775
+ Card,
776
+ {
777
+ item,
778
+ column,
779
+ lang,
780
+ locale,
781
+ busy,
782
+ label,
783
+ openChat: openChat2,
784
+ onAct: (action) => {
785
+ void act(item, action);
786
+ },
787
+ onDragStart: () => {
788
+ dragged.current = { card: item, from: column };
789
+ },
790
+ onDragEnd: () => {
791
+ dragged.current = void 0;
792
+ setDropOn(void 0);
793
+ }
794
+ },
795
+ `${item.target}-${item.id}`
796
+ );
797
+ if (board === void 0) {
798
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-page", children: [
799
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-head", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { children: t.title }) }),
800
+ error === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mishu-empty", children: t.loading }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-error", children: [
801
+ t.failed,
802
+ error
803
+ ] })
804
+ ] });
805
+ }
806
+ const { metrics } = board;
807
+ const keep = (card2) => source === "all" || card2.source === source;
808
+ const columns = {
809
+ handle: board.columns.handle.filter(keep),
810
+ remember: board.columns.remember.filter(keep),
811
+ done: (() => {
812
+ const groups = board.columns.done.groups.map((g) => ({ ...g, items: g.items.filter(keep) })).filter((g) => g.items.length > 0);
813
+ return { total: groups.reduce((n, g) => n + g.items.length, 0), groups };
814
+ })()
815
+ };
816
+ const everything = [...board.columns.handle, ...board.columns.remember, ...board.columns.done.groups.flatMap((g) => g.items)];
817
+ const count = (s) => everything.filter((c) => s === "all" || c.source === s).length;
818
+ const sources = [
819
+ { id: "all", label: t.all },
820
+ { id: "github", label: t.sourceGithub },
821
+ { id: "mail", label: t.sourceMail },
822
+ { id: "manual", label: t.sourceManual }
823
+ ];
824
+ const template = `minmax(0, ${widths[0]}fr) 10px minmax(0, ${widths[1]}fr) 10px minmax(0, ${widths[2]}fr)`;
825
+ const divider = (index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
826
+ "div",
827
+ {
828
+ className: "mishu-divider",
829
+ role: "separator",
830
+ "aria-orientation": "vertical",
831
+ title: t.resize,
832
+ onPointerDown: startResize(index),
833
+ onDoubleClick: resetWidths
834
+ }
835
+ );
836
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-page", "data-mishu-board": "", children: [
837
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-head", children: [
838
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-title", children: [
839
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { children: t.title }),
840
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-tabs", role: "tablist", children: [
841
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === "board", className: tab === "board" ? "active" : "", onClick: () => setTab("board"), children: t.tabBoard }),
842
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === "settings", className: tab === "settings" ? "active" : "", onClick: () => setTab("settings"), children: t.tabSettings })
843
+ ] })
844
+ ] }),
845
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-sources", children: [
846
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
847
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: `mishu-dot ${board.sources.github.state}` }),
848
+ board.sources.github.text,
849
+ board.sources.github.state === "error" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskAI, { topic: { kind: "github-error" }, lang, locale, openChat: openChat2, compact: true }) : null
850
+ ] }),
851
+ board.sources.mail.map((m) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
852
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: `mishu-dot ${m.state}` }),
853
+ m.text,
854
+ m.state === "error" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskAI, { topic: { kind: "mail-error", accountId: m.id }, lang, locale, openChat: openChat2, compact: true }) : null
855
+ ] }, m.id)),
856
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn", disabled: syncing, onClick: () => {
857
+ void sync();
858
+ }, children: syncing ? t.syncing : t.sync })
859
+ ] })
860
+ ] }),
861
+ tab === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MishuSettings, { lang, locale, openChat: openChat2 }) : null,
862
+ tab === "settings" ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
863
+ error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-error", children: [
864
+ t.failed,
865
+ error
866
+ ] }),
867
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "mishu-summary", children: [
868
+ board.summary,
869
+ " ",
870
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskAI, { topic: { kind: "summary" }, lang, locale, openChat: openChat2, compact: true })
871
+ ] }),
872
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metrics", children: [
873
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric", children: [
874
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-label", children: t.metricHandle }),
875
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric-value", children: [
876
+ metrics.handle,
877
+ metrics.overdue > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("small", { className: "mishu-danger", children: [
878
+ metrics.overdue,
879
+ " ",
880
+ t.metricOverdue
881
+ ] }) : null
882
+ ] })
883
+ ] }),
884
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric", children: [
885
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-label", children: t.metricSoon }),
886
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-value", children: metrics.dueSoon })
887
+ ] }),
888
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric", children: [
889
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-label", children: t.metricClosed }),
890
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-value", children: metrics.closedToday })
891
+ ] }),
892
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric", children: [
893
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-metric-label", children: t.metricShadow }),
894
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-metric-value", children: [
895
+ metrics.shadow,
896
+ board.markRead === "shadow" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { className: "mishu-warn", children: t.shadowTag }) : null
897
+ ] })
898
+ ] })
899
+ ] }),
900
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-filters", role: "group", children: sources.map((s) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", "aria-pressed": source === s.id, className: `mishu-chip ${source === s.id ? "active" : ""}`, onClick: () => setSource(s.id), children: [
901
+ s.label,
902
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: count(s.id) })
903
+ ] }, s.id)) }),
904
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-columns", ref: columnsRef, style: { ["--mishu-cols"]: template }, children: [
905
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: `mishu-col ${dropOn === "handle" ? "drop" : ""}`, ...dropProps("handle"), "aria-label": t.handle, children: [
906
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-col-head", children: [
907
+ t.handle,
908
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: columns.handle.length })
909
+ ] }),
910
+ columns.handle.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-empty", children: t.emptyHandle }) : columns.handle.map((item) => card(item, "handle"))
911
+ ] }),
912
+ divider(0),
913
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: `mishu-col ${dropOn === "remember" ? "drop" : ""}`, ...dropProps("remember"), "aria-label": t.remember, title: t.dropRemember, children: [
914
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-col-head", children: [
915
+ t.remember,
916
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: columns.remember.length })
917
+ ] }),
918
+ columns.remember.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-empty", children: t.emptyRemember }) : columns.remember.map((item) => card(item, "remember")),
919
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AddForm, { lang, locale, onAdded: (next) => setBoard(next) })
920
+ ] }),
921
+ divider(1),
922
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: `mishu-col ${dropOn === "done" ? "drop" : ""}`, ...dropProps("done"), "aria-label": t.done, title: t.dropDone, children: [
923
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-col-head", children: [
924
+ t.done,
925
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: columns.done.total })
926
+ ] }),
927
+ columns.done.groups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-empty", children: t.emptyDone }) : columns.done.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("details", { className: "mishu-group", children: [
928
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("summary", { children: [
929
+ group.label,
930
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: group.items.length })
931
+ ] }),
932
+ group.items.map((item) => card(item, "done"))
933
+ ] }, group.reason)),
934
+ columns.done.total > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mishu-hint", children: t.doneHint }) : null,
935
+ metrics.shadow > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-banner", children: t.shadow(metrics.shadow) }) : null
936
+ ] })
937
+ ] })
938
+ ] })
939
+ ] });
940
+ }
941
+ function Card({ item, column, lang, locale, busy, label, openChat: openChat2, onAct, onDragStart, onDragEnd }) {
942
+ const t = TEXT[lang];
943
+ const [open, setOpen] = (0, import_react3.useState)(false);
944
+ const [detail, setDetail] = (0, import_react3.useState)();
945
+ const explain = item.explain;
946
+ const compact = column === "done";
947
+ const toggle = () => {
948
+ const next = !open;
949
+ setOpen(next);
950
+ if (next && item.target === "matter") {
951
+ void mishuApi.detail(locale(), item.id).then((r) => setDetail(r.ok ? r.detail : { error: r.message }));
952
+ }
953
+ };
954
+ const row = (name, text, className = "") => text === null || text === void 0 || text === "" ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `mishu-row-explain ${className}`, children: [
955
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mishu-row-label", children: name }),
956
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: text })
957
+ ] });
958
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
959
+ "div",
960
+ {
961
+ className: `mishu-card ${item.tone === "normal" ? "" : item.tone}`,
962
+ draggable: item.target === "matter" && column !== "done",
963
+ onDragStart: (event) => {
964
+ event.dataTransfer.effectAllowed = "move";
965
+ onDragStart();
966
+ },
967
+ onDragEnd,
968
+ children: [
969
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-card-title", children: item.title }),
970
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-card-meta", children: item.meta }),
971
+ !compact && explain !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-explain", children: [
972
+ row(t.what, explain.what),
973
+ row(column === "remember" ? t.whyKeep : t.why, explain.why),
974
+ row(t.fix, explain.fix, "mishu-fix")
975
+ ] }) : null,
976
+ !compact && explain === null && item.note !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-card-note", children: item.note }) : null,
977
+ open ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-detail", children: detail === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-empty", children: t.loading }) : "error" in detail ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-danger", children: detail.error }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
978
+ compact && detail.explain !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
979
+ row(t.what, detail.explain.what),
980
+ row(t.whyDone, detail.explain.why)
981
+ ] }) : null,
982
+ detail.did.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-row-explain", children: [
983
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mishu-row-label", children: t.did }),
984
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: detail.did.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: line }, i)) })
985
+ ] }) : null,
986
+ detail.messages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-messages", children: [
987
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mishu-row-label", children: t.messages(detail.messages.length) }),
988
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { children: detail.messages.map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
989
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: m.at }),
990
+ " ",
991
+ m.title
992
+ ] }, i)) })
993
+ ] }) : null
994
+ ] }) }) : null,
995
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-actions", children: [
996
+ column === "handle" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskAI, { topic: item.target === "series" ? { kind: "series", id: item.id } : { kind: "matter", id: item.id }, lang, locale, openChat: openChat2 }) : null,
997
+ item.actions.map((action, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
998
+ "button",
999
+ {
1000
+ type: "button",
1001
+ className: `mishu-btn ${index === 0 ? "primary" : ""}`,
1002
+ disabled: busy && action !== "open",
1003
+ onClick: () => onAct(action),
1004
+ children: action === "open" && explain?.link !== void 0 ? explain.link.label : label[action]
1005
+ },
1006
+ action
1007
+ )),
1008
+ item.target === "matter" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn mishu-btn-quiet", "aria-expanded": open, onClick: toggle, children: open ? t.hide : compact ? t.explainDone : t.detail }) : null,
1009
+ column === "handle" ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskAI, { topic: item.target === "series" ? { kind: "series", id: item.id } : { kind: "matter", id: item.id }, lang, locale, openChat: openChat2, compact: true })
1010
+ ] })
1011
+ ]
1012
+ }
1013
+ );
1014
+ }
1015
+ var EMPTY = { title: "", due: "", repeat: "none", category: "other", note: "" };
1016
+ function AddForm({ lang, locale, onAdded }) {
1017
+ const t = TEXT[lang];
1018
+ const [open, setOpen] = (0, import_react3.useState)(false);
1019
+ const [item, setItem] = (0, import_react3.useState)(EMPTY);
1020
+ const [preview, setPreview] = (0, import_react3.useState)();
1021
+ const [saving, setSaving] = (0, import_react3.useState)(false);
1022
+ (0, import_react3.useEffect)(() => {
1023
+ if (!open || item.title.trim() === "" || item.due === "") {
1024
+ setPreview(void 0);
1025
+ return;
1026
+ }
1027
+ const timer = setTimeout(() => {
1028
+ void mishuApi.preview(locale(), item).then((r) => setPreview(r.ok ? { ok: true, text: r.text } : { ok: false, text: r.message }));
1029
+ }, 250);
1030
+ return () => clearTimeout(timer);
1031
+ }, [open, item, locale]);
1032
+ if (!open) {
1033
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", className: "mishu-btn mishu-add-toggle", onClick: () => setOpen(true), children: [
1034
+ "+ ",
1035
+ t.add
1036
+ ] });
1037
+ }
1038
+ const set = (key) => (event) => setItem({ ...item, [key]: event.target.value });
1039
+ const save = async () => {
1040
+ setSaving(true);
1041
+ const result = await mishuApi.add(locale(), item);
1042
+ setSaving(false);
1043
+ if (result.ok) {
1044
+ onAdded(result.board);
1045
+ setItem(EMPTY);
1046
+ setOpen(false);
1047
+ } else {
1048
+ setPreview({ ok: false, text: result.message });
1049
+ }
1050
+ };
1051
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-add", children: [
1052
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
1053
+ t.addTitle,
1054
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { value: item.title, placeholder: t.addTitlePlaceholder, onChange: set("title") })
1055
+ ] }),
1056
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-row", children: [
1057
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
1058
+ t.addDue,
1059
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "date", value: item.due, onChange: set("due") })
1060
+ ] }),
1061
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
1062
+ t.addRepeat,
1063
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { value: item.repeat, onChange: set("repeat"), children: Object.entries(t.repeats).map(([value, text]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value, children: text }, value)) })
1064
+ ] })
1065
+ ] }),
1066
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-row", children: [
1067
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
1068
+ t.addCategory,
1069
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { value: item.category, onChange: set("category"), children: Object.entries(t.categories).map(([value, text]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value, children: text }, value)) })
1070
+ ] }),
1071
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "mishu-field", children: [
1072
+ t.addNote,
1073
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { value: item.note, onChange: set("note") })
1074
+ ] })
1075
+ ] }),
1076
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `mishu-preview ${preview?.ok === false ? "mishu-danger" : ""}`, children: preview?.text ?? t.previewHint }),
1077
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mishu-actions", children: [
1078
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn primary", disabled: saving || preview?.ok !== true, onClick: () => {
1079
+ void save();
1080
+ }, children: t.save }),
1081
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mishu-btn", onClick: () => {
1082
+ setOpen(false);
1083
+ setItem(EMPTY);
1084
+ }, children: t.cancel })
1085
+ ] }),
1086
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mishu-hint", children: t.chatHint })
1087
+ ] });
1088
+ }
1089
+
1090
+ // src/client/styles.ts
1091
+ var v = (name, fallback) => `var(--dsw-alias-${name}, ${fallback})`;
1092
+ var TEXT2 = v("label-primary", "CanvasText");
1093
+ var MUTED = v("label-secondary", "GrayText");
1094
+ var FAINT = v("label-tertiary", "GrayText");
1095
+ var LINE = v("border-l2", "rgba(127,127,127,.22)");
1096
+ var LINE_STRONG = v("border-l3", "rgba(127,127,127,.35)");
1097
+ var LAYER1 = v("bg-layer-1", "rgba(127,127,127,.06)");
1098
+ var LAYER2 = v("bg-layer-2", "rgba(127,127,127,.10)");
1099
+ var HOVER = v("interactive-bg-hover", "rgba(127,127,127,.12)");
1100
+ var DANGER = v("state-error-primary", "#d14343");
1101
+ var WARN = v("state-warn-primary", "#b7791f");
1102
+ var OK = v("state-success-primary", "#2f855a");
1103
+ var ACCENT = v("brand-primary", "#4d6bfe");
1104
+ var STYLES = `
1105
+ .mishu-page{box-sizing:border-box;height:100%;overflow:auto;padding:20px 24px 32px;color:${TEXT2};font-size:13px;line-height:1.5}
1106
+ .mishu-page *{box-sizing:border-box}
1107
+ .mishu-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:8px}
1108
+ .mishu-head h1{font-size:18px;font-weight:600;margin:0}
1109
+ .mishu-sources{display:flex;align-items:center;gap:10px;color:${MUTED};font-size:12px;flex-wrap:wrap}
1110
+ .mishu-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:4px;vertical-align:1px;background:${FAINT}}
1111
+ .mishu-dot.ok{background:${OK}}.mishu-dot.error{background:${DANGER}}
1112
+ .mishu-summary{font-size:15px;margin:4px 0 14px;max-width:72ch}
1113
+ .mishu-metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-bottom:16px}
1114
+ .mishu-metric{background:${LAYER1};border-radius:10px;padding:10px 12px}
1115
+ .mishu-metric-label{font-size:12px;color:${MUTED}}
1116
+ .mishu-metric-value{font-size:22px;font-weight:600;line-height:1.3}
1117
+ .mishu-metric-value small{font-size:12px;font-weight:400;margin-left:6px}
1118
+ .mishu-danger{color:${DANGER}}.mishu-warn{color:${WARN}}
1119
+ .mishu-columns{display:grid;grid-template-columns:var(--mishu-cols,minmax(0,1fr) 10px minmax(0,1fr) 10px minmax(0,1fr));gap:0;align-items:stretch}
1120
+ .mishu-divider{cursor:col-resize;position:relative;touch-action:none}
1121
+ .mishu-divider::after{content:'';position:absolute;top:8px;bottom:8px;left:4px;width:2px;border-radius:1px;background:transparent;transition:background .12s}
1122
+ .mishu-divider:hover::after,.mishu-divider:active::after{background:${ACCENT}}
1123
+ .mishu-col{background:${LAYER1};border-radius:12px;padding:10px;min-height:120px;min-width:0;border:1px solid transparent;transition:border-color .12s;align-self:start}
1124
+ .mishu-col.drop{border-color:${ACCENT};border-style:dashed}
1125
+ .mishu-col-head{display:flex;justify-content:space-between;align-items:baseline;font-weight:600;margin:2px 4px 8px}
1126
+ .mishu-col-head span{color:${MUTED};font-weight:400}
1127
+ .mishu-empty{color:${FAINT};padding:8px 4px}
1128
+ .mishu-card{background:${LAYER2};border:1px solid ${LINE};border-radius:8px;padding:8px 10px;margin-bottom:8px}
1129
+ .mishu-card[draggable=true]{cursor:grab}
1130
+ .mishu-card.danger{border-color:${DANGER}}
1131
+ .mishu-card-title{font-weight:600;word-break:break-word}
1132
+ .mishu-card-meta{font-size:12px;color:${MUTED};margin-top:2px}
1133
+ .mishu-card.danger .mishu-card-meta{color:${DANGER}}
1134
+ .mishu-card.warn .mishu-card-meta{color:${WARN}}
1135
+ .mishu-card-note{font-size:12px;color:${MUTED};margin-top:4px;word-break:break-word}
1136
+ .mishu-actions{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
1137
+ .mishu-explain{margin-top:6px;display:flex;flex-direction:column;gap:4px}
1138
+ .mishu-row-explain{display:grid;grid-template-columns:auto minmax(0,1fr);gap:8px;font-size:12px;line-height:1.55;word-break:break-word}
1139
+ .mishu-row-label{color:${FAINT};white-space:nowrap;font-size:12px}
1140
+ .mishu-fix span:last-child{color:${TEXT2}}
1141
+ .mishu-detail{margin-top:8px;padding-top:8px;border-top:1px dashed ${LINE};display:flex;flex-direction:column;gap:6px}
1142
+ .mishu-messages ul{margin:4px 0 0;padding-left:16px;font-size:12px;color:${MUTED}}
1143
+ .mishu-messages li span{color:${FAINT};margin-right:4px}
1144
+ .mishu-btn-quiet{border-color:transparent;color:${MUTED}}
1145
+ .mishu-btn{font:inherit;font-size:12px;color:${TEXT2};background:transparent;border:1px solid ${LINE_STRONG};border-radius:6px;padding:2px 9px;cursor:pointer}
1146
+ .mishu-btn:hover{background:${HOVER}}
1147
+ .mishu-btn:disabled{opacity:.5;cursor:default}
1148
+ .mishu-btn.primary{border-color:${ACCENT};color:${ACCENT}}
1149
+ .mishu-group{border-top:1px solid ${LINE};padding:6px 4px}
1150
+ .mishu-group summary{cursor:pointer;display:flex;justify-content:space-between;list-style:none}
1151
+ .mishu-group summary::-webkit-details-marker{display:none}
1152
+ .mishu-group summary span{color:${MUTED}}
1153
+ .mishu-group .mishu-card{margin-top:6px}
1154
+ .mishu-hint{font-size:12px;color:${FAINT};margin:6px 4px 0}
1155
+ .mishu-banner{margin-top:10px;padding:8px 10px;border-radius:8px;border:1px solid ${WARN};color:${WARN};font-size:12px}
1156
+ .mishu-error{margin-bottom:12px;padding:8px 10px;border-radius:8px;border:1px solid ${DANGER};color:${DANGER};font-size:12px}
1157
+ .mishu-add{border:1px dashed ${LINE_STRONG};border-radius:8px;padding:8px;margin-top:4px}
1158
+ .mishu-add-toggle{width:100%;text-align:left;border-style:dashed}
1159
+ .mishu-field{display:flex;flex-direction:column;gap:2px;margin-bottom:6px;font-size:12px;color:${MUTED}}
1160
+ .mishu-field input,.mishu-field select{font:inherit;font-size:13px;color:${TEXT2};background:${LAYER2};border:1px solid ${LINE_STRONG};border-radius:6px;padding:4px 6px;width:100%}
1161
+ .mishu-row{display:grid;grid-template-columns:1fr 1fr;gap:6px}
1162
+ .mishu-preview{white-space:pre-wrap;font-size:12px;background:${LAYER2};border-radius:6px;padding:6px 8px;margin:4px 0 6px;color:${TEXT2}}
1163
+ .mishu-title{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
1164
+ .mishu-tabs{display:flex;gap:2px;background:${LAYER1};border-radius:8px;padding:2px}
1165
+ .mishu-tabs button{font:inherit;font-size:13px;color:${MUTED};background:transparent;border:0;border-radius:6px;padding:3px 12px;cursor:pointer}
1166
+ .mishu-tabs button.active{background:${LAYER2};color:${TEXT2}}
1167
+ .mishu-filters{display:flex;gap:6px;flex-wrap:wrap;margin:0 0 10px}
1168
+ .mishu-chip{font:inherit;font-size:12px;color:${MUTED};background:transparent;border:1px solid ${LINE_STRONG};border-radius:999px;padding:2px 10px;cursor:pointer;display:inline-flex;gap:6px;align-items:center}
1169
+ .mishu-chip span{color:${FAINT}}
1170
+ .mishu-chip.active{border-color:${ACCENT};color:${ACCENT}}
1171
+ .mishu-chip.active span{color:${ACCENT}}
1172
+ .mishu-settings{display:flex;flex-direction:column;gap:12px;max-width:760px}
1173
+ .mishu-panel{background:${LAYER1};border-radius:12px;padding:12px 14px}
1174
+ .mishu-panel h2{font-size:14px;font-weight:600;margin:0 0 8px}
1175
+ .mishu-grid5{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:6px;margin-bottom:8px}
1176
+ .mishu-check{display:flex;align-items:center;gap:6px;font-size:13px;margin:4px 0 8px;color:${TEXT2}}
1177
+ .mishu-ok{padding:6px 10px;border-radius:8px;border:1px solid ${OK};color:${OK};font-size:12px}
1178
+ .mishu-ai-wrap{display:inline-flex;align-items:center;gap:6px;vertical-align:middle;flex-wrap:wrap}
1179
+ .mishu-ai{font:inherit;font-size:12px;font-weight:700;letter-spacing:.02em;color:#fff;border:0;border-radius:999px;padding:3px 11px;cursor:pointer;background:linear-gradient(120deg,#6d5dfc 0%,#c850c0 50%,#ff8a3d 100%);background-size:160% 100%;transition:background-position .3s,transform .1s}
1180
+ .mishu-ai:hover{background-position:100% 0}
1181
+ .mishu-ai:active{transform:scale(.97)}
1182
+ .mishu-ai:disabled{opacity:.6;cursor:default}
1183
+ .mishu-ai.compact{font-size:11px;padding:1px 8px;margin-left:4px}
1184
+ .mishu-ai:focus-visible{outline:2px solid ${ACCENT};outline-offset:2px}
1185
+ .mishu-ai-note{font-size:11px;color:${MUTED}}
1186
+ @media (max-width:820px){.mishu-columns{grid-template-columns:1fr;gap:12px}.mishu-divider{display:none}.mishu-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.mishu-grid5{grid-template-columns:repeat(2,minmax(0,1fr))}}
1187
+ `;
1188
+
1189
+ // src/client/index.tsx
1190
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1191
+ var PACKAGE_NAME = "dsh-mishu";
1192
+ var inject = ["slots", "locale"];
1193
+ var PANEL_ID = "dsh-mishu";
1194
+ var NS = "dsh-mishu";
1195
+ var openChat;
1196
+ function MishuIcon({ size }) {
1197
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1198
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "5", y: "4", width: "14", height: "17", rx: "2" }),
1199
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M9 4h6v3H9z" }),
1200
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M9 12l2 2 4-4" }),
1201
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M9 17h6" })
1202
+ ] });
1203
+ }
1204
+ function apply(ctx) {
1205
+ ctx.effect(() => ctx.locale.register(NS, { zh: { panel: "\u5C0F\u79D8\u4E66" }, en: { panel: "Secretary" } }), "dsh-mishu: dictionaries");
1206
+ ctx.effect(() => {
1207
+ const style = document.createElement("style");
1208
+ style.dataset.dshPlugin = PACKAGE_NAME;
1209
+ style.textContent = STYLES;
1210
+ document.head.append(style);
1211
+ return () => {
1212
+ style.remove();
1213
+ };
1214
+ }, "dsh-mishu: styles");
1215
+ const t = ctx.locale.bind(NS);
1216
+ const locale = () => ctx.locale.getSnapshot().active;
1217
+ ctx.inject(["uiWorkspace"], (workspaceCtx) => {
1218
+ workspaceCtx.effect(() => {
1219
+ openChat = (prompt) => workspaceCtx.uiWorkspace.startSession(void 0, { prompt, clearPreviousDraft: true });
1220
+ return () => {
1221
+ openChat = void 0;
1222
+ };
1223
+ }, "dsh-mishu: +AI opens a chat");
1224
+ });
1225
+ ctx.slots.inject("main", () => ctx.slots.register({
1226
+ name: "main",
1227
+ key: PANEL_ID,
1228
+ locale: NS,
1229
+ inject: () => ({ locale, openChat: (prompt) => openChat === void 0 ? false : (openChat(prompt), true) })
1230
+ }, MishuBoard));
1231
+ ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
1232
+ name: "sidebar.panellist",
1233
+ id: PANEL_ID,
1234
+ order: 30,
1235
+ locale: NS,
1236
+ label: () => t("panel")
1237
+ }, MishuIcon));
1238
+ }
1239
+
1240
+ return module.exports;
1241
+ }
1242
+ });