@x-otto/cli 0.0.1-alpha.5 → 0.0.1-alpha.51

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 (159) hide show
  1. package/README.md +72 -65
  2. package/bin/otto.js +6 -0
  3. package/dist/{auth-command-CXAB3_hs.js → auth-command-vFUyxV0G.js} +3 -3
  4. package/dist/auth-command-vFUyxV0G.js.map +1 -0
  5. package/dist/backend-handler-DUafuNWw.js +3 -0
  6. package/dist/backend-handler-DUafuNWw.js.map +1 -0
  7. package/dist/build-id-nYtU1lzB.js +3 -0
  8. package/dist/build-id-nYtU1lzB.js.map +1 -0
  9. package/dist/bundled-extensions/plugin-feedback/otto-plugin.json +24 -0
  10. package/dist/bundled-extensions/plugin-feedback/panels/feedback.tsx +348 -0
  11. package/dist/bundled-extensions/plugin-feedback/panels/panel-host-globals.d.ts +67 -0
  12. package/dist/bundled-extensions/plugin-feedback/panels-dist/feedback.mjs +257 -0
  13. package/dist/bundled-extensions/plugin-feedback/plugin-dist/meta.json +1 -0
  14. package/dist/bundled-extensions/plugin-feedback/plugin-dist/plugin.cjs +11304 -0
  15. package/dist/bundled-extensions/plugin-feedback/plugin.ts +198 -0
  16. package/dist/bundled-extensions/plugin-feedback/src/coding-adapter.ts +183 -0
  17. package/dist/bundled-extensions/plugin-feedback/src/coding-lister.ts +26 -0
  18. package/dist/bundled-extensions/plugin-feedback/src/github-lister.ts +230 -0
  19. package/dist/bundled-extensions/plugin-feedback/src/issue-aggregator.ts +109 -0
  20. package/dist/bundled-extensions/plugin-feedback/src/issue-submitter.ts +241 -0
  21. package/dist/bundled-extensions/plugin-feedback/src/platform-resolver.ts +76 -0
  22. package/dist/bundled-extensions/plugin-feedback/src/pulse-survey-sink.ts +114 -0
  23. package/dist/bundled-extensions/plugin-feedback/src/resolved-sink.ts +163 -0
  24. package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin-dist/meta.json +1 -1
  25. package/dist/bundled-extensions/plugin-otto-wire-protocols/plugin-dist/plugin.cjs +1019 -43188
  26. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/anthropic-messages.ts +6 -24
  27. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/openai-completions.ts +17 -10
  28. package/dist/bundled-extensions/plugin-otto-wire-protocols/src/openai-responses.ts +12 -17
  29. package/dist/bundled-extensions-C3n8jg20.js +3 -0
  30. package/dist/bundled-extensions-C3n8jg20.js.map +1 -0
  31. package/dist/command-dispatcher-BP4shfKu.js +18 -0
  32. package/dist/command-dispatcher-BP4shfKu.js.map +1 -0
  33. package/dist/config-migration-DatflIxs.js.map +1 -1
  34. package/dist/create-host-app-DFmHCULh.js +3 -0
  35. package/dist/create-host-app-DFmHCULh.js.map +1 -0
  36. package/dist/{daemon-base-YOWgYYN7.js → daemon-base-D015ew5y.js} +1 -1
  37. package/dist/{daemon-base-YOWgYYN7.js.map → daemon-base-D015ew5y.js.map} +1 -1
  38. package/dist/discover-with-bundled-B5d4q___.js +2 -0
  39. package/dist/discover-with-bundled-CF8qFP41.js +3 -0
  40. package/dist/discover-with-bundled-CF8qFP41.js.map +1 -0
  41. package/dist/doctor-Cy-XCOMb.js +36 -0
  42. package/dist/doctor-Cy-XCOMb.js.map +1 -0
  43. package/dist/{doctor-attach-_zeIJewx.js → doctor-attach-DEoE7eFx.js} +2 -2
  44. package/dist/{doctor-attach-_zeIJewx.js.map → doctor-attach-DEoE7eFx.js.map} +1 -1
  45. package/dist/{doctor-cdp-K_mIFRfA.js → doctor-cdp-Cj9IFpEP.js} +1 -1
  46. package/dist/{doctor-cdp-K_mIFRfA.js.map → doctor-cdp-Cj9IFpEP.js.map} +1 -1
  47. package/dist/{doctor-heap-Rr2nNvcw.js → doctor-heap-SIxiRvL8.js} +2 -2
  48. package/dist/{doctor-heap-Rr2nNvcw.js.map → doctor-heap-SIxiRvL8.js.map} +1 -1
  49. package/dist/{doctor-ps-OL3FGQYu.js → doctor-ps-DNVOF6yY.js} +2 -2
  50. package/dist/{doctor-ps-OL3FGQYu.js.map → doctor-ps-DNVOF6yY.js.map} +1 -1
  51. package/dist/export-trajectory-D-BcH0Wt.js +14 -0
  52. package/dist/export-trajectory-D-BcH0Wt.js.map +1 -0
  53. package/dist/extension-CoJqqktf.js +2 -0
  54. package/dist/extension-Dpn8gltG.js +179 -0
  55. package/dist/extension-Dpn8gltG.js.map +1 -0
  56. package/dist/feedback-BdfwxfUO.js +30 -0
  57. package/dist/feedback-BdfwxfUO.js.map +1 -0
  58. package/dist/feedback-backend-methods-DE2-fHID.js +3 -0
  59. package/dist/feedback-backend-methods-DE2-fHID.js.map +1 -0
  60. package/dist/{feishu-setup-D6vnuNLw.js → feishu-setup-DbAq_BRH.js} +2 -2
  61. package/dist/feishu-setup-DbAq_BRH.js.map +1 -0
  62. package/dist/ftue-doctor-recovery-CKTMnxM8.js +3 -0
  63. package/dist/ftue-doctor-recovery-CKTMnxM8.js.map +1 -0
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +11 -13
  66. package/dist/index.js.map +1 -1
  67. package/dist/install-CEPczuet.js +11 -0
  68. package/dist/install-CEPczuet.js.map +1 -0
  69. package/dist/install-CG6b6fox.js +2 -0
  70. package/dist/interactive-schedule-Vh4HVL9P.js +3 -0
  71. package/dist/interactive-schedule-Vh4HVL9P.js.map +1 -0
  72. package/dist/{mcp-DbTYT7hB.js → mcp-DjReRjuu.js} +1 -1
  73. package/dist/{mcp-DbTYT7hB.js.map → mcp-DjReRjuu.js.map} +1 -1
  74. package/dist/mcp-server-bGHngRCU.js +5 -0
  75. package/dist/mcp-server-bGHngRCU.js.map +1 -0
  76. package/dist/{migrate-boDXhEyV.js → migrate-BBi3Txu2.js} +1 -1
  77. package/dist/{migrate-boDXhEyV.js.map → migrate-BBi3Txu2.js.map} +1 -1
  78. package/dist/observe-BqLCpmFD.js +83 -0
  79. package/dist/observe-BqLCpmFD.js.map +1 -0
  80. package/dist/parse-cli-1sB4zM6w.js +3 -0
  81. package/dist/parse-cli-1sB4zM6w.js.map +1 -0
  82. package/dist/persistence-BvLRnpfi.js +85 -0
  83. package/dist/persistence-BvLRnpfi.js.map +1 -0
  84. package/dist/plugin-assembly-check-BntT5W6H.js +3 -0
  85. package/dist/plugin-assembly-check-BntT5W6H.js.map +1 -0
  86. package/dist/{proxy-evg7ZvoD.js → proxy-CFZDVIwI.js} +1 -1
  87. package/dist/proxy-CFZDVIwI.js.map +1 -0
  88. package/dist/proxy-CkSf8ziQ.js +2 -0
  89. package/dist/{ps-DKrluEuy.js → ps-BVKv6rAo.js} +1 -1
  90. package/dist/{ps-DKrluEuy.js.map → ps-BVKv6rAo.js.map} +1 -1
  91. package/dist/{registry-D0NONjLv.js → registry-BUvu-es1.js} +4 -4
  92. package/dist/registry-BUvu-es1.js.map +1 -0
  93. package/dist/registry-DYHR-ONu.js +2 -0
  94. package/dist/{run-attach-Ci5jYyCa.js → run-attach-X35puJ_n.js} +2 -2
  95. package/dist/{run-attach-Ci5jYyCa.js.map → run-attach-X35puJ_n.js.map} +1 -1
  96. package/dist/schedule-Ke0jwatM.js +5 -0
  97. package/dist/schedule-Ke0jwatM.js.map +1 -0
  98. package/dist/serve-VVbmjLAe.js +21 -0
  99. package/dist/serve-VVbmjLAe.js.map +1 -0
  100. package/dist/{service-client-BLNeugM9.js → service-client-cfUUP4EE.js} +1 -1
  101. package/dist/{service-client-BLNeugM9.js.map → service-client-cfUUP4EE.js.map} +1 -1
  102. package/dist/{version-C0qM-qJM.js → version-26SAxZuX.js} +2 -2
  103. package/dist/{version-C0qM-qJM.js.map → version-26SAxZuX.js.map} +1 -1
  104. package/package.json +28 -25
  105. package/dist/auth-command-CXAB3_hs.js.map +0 -1
  106. package/dist/build-id-BX4QHzM0.js +0 -3
  107. package/dist/build-id-BX4QHzM0.js.map +0 -1
  108. package/dist/bundled-extensions/plugin-skill-inductor/otto-plugin.json +0 -8
  109. package/dist/bundled-extensions/plugin-skill-inductor/plugin-dist/meta.json +0 -1
  110. package/dist/bundled-extensions/plugin-skill-inductor/plugin-dist/plugin.cjs +0 -52781
  111. package/dist/bundled-extensions/plugin-skill-inductor/plugin.ts +0 -77
  112. package/dist/bundled-extensions/plugin-skill-inductor/src/candidate-detector.ts +0 -210
  113. package/dist/bundled-extensions/plugin-skill-inductor/src/candidate-digest.ts +0 -146
  114. package/dist/bundled-extensions/plugin-skill-inductor/src/candidate-store.ts +0 -87
  115. package/dist/bundled-extensions/plugin-skill-inductor/src/episode-store.ts +0 -122
  116. package/dist/bundled-extensions/plugin-skill-inductor/src/episode.ts +0 -125
  117. package/dist/bundled-extensions/plugin-skill-inductor/src/extraction-prompt.ts +0 -97
  118. package/dist/bundled-extensions/plugin-skill-inductor/src/inductor-service.ts +0 -183
  119. package/dist/bundled-extensions/plugin-skill-inductor/src/similarity.ts +0 -58
  120. package/dist/command-dispatcher-BVGddstc.js +0 -13
  121. package/dist/command-dispatcher-BVGddstc.js.map +0 -1
  122. package/dist/debug-CiosFwme.js +0 -34
  123. package/dist/debug-CiosFwme.js.map +0 -1
  124. package/dist/discover-with-bundled-CznLjkkw.js +0 -3
  125. package/dist/discover-with-bundled-CznLjkkw.js.map +0 -1
  126. package/dist/doctor-C84-hhEr.js +0 -30
  127. package/dist/doctor-C84-hhEr.js.map +0 -1
  128. package/dist/extension-BKXIr86F.js +0 -170
  129. package/dist/extension-BKXIr86F.js.map +0 -1
  130. package/dist/extension-CnON6Xum.js +0 -2
  131. package/dist/extension-plugin-DB-bWOIf.js +0 -7
  132. package/dist/extension-plugin-DB-bWOIf.js.map +0 -1
  133. package/dist/extension-plugin-DoKKFxRC.js +0 -2
  134. package/dist/feedback-BES_6ebi.js +0 -21
  135. package/dist/feedback-BES_6ebi.js.map +0 -1
  136. package/dist/feedback-BRbLUn9u.js +0 -2
  137. package/dist/feishu-setup-D6vnuNLw.js.map +0 -1
  138. package/dist/install-DlVJRHRT.js +0 -11
  139. package/dist/install-DlVJRHRT.js.map +0 -1
  140. package/dist/install-WCcQ9k9M.js +0 -2
  141. package/dist/model-auth-B95mFA-O.js +0 -2
  142. package/dist/model-auth-BlaZzUUK.js +0 -3
  143. package/dist/model-auth-BlaZzUUK.js.map +0 -1
  144. package/dist/observe-CVg-MRM2.js +0 -32
  145. package/dist/observe-CVg-MRM2.js.map +0 -1
  146. package/dist/parse-cli-cYMjnAum.js +0 -3
  147. package/dist/parse-cli-cYMjnAum.js.map +0 -1
  148. package/dist/persistenced-CCwHu1Ww.js +0 -36
  149. package/dist/persistenced-CCwHu1Ww.js.map +0 -1
  150. package/dist/proxy-BvdBsB1O.js +0 -2
  151. package/dist/proxy-evg7ZvoD.js.map +0 -1
  152. package/dist/registry-B3nfN62q.js +0 -2
  153. package/dist/registry-D0NONjLv.js.map +0 -1
  154. package/dist/remote-persistence-server-B_hONoVR.js +0 -35
  155. package/dist/remote-persistence-server-B_hONoVR.js.map +0 -1
  156. package/dist/serve-pVR6UIT9.js +0 -21
  157. package/dist/serve-pVR6UIT9.js.map +0 -1
  158. package/dist/time-travel-ZozuPPI-.js +0 -21
  159. package/dist/time-travel-ZozuPPI-.js.map +0 -1
@@ -0,0 +1,67 @@
1
+ /**
2
+ * panel-host-globals.d.ts — 插件面板宿主桥接全局类型声明(RFC-353 S12)。
3
+ *
4
+ * `panel-compiler.ts` 用 esbuild banner 把宿主运行时(`getTheme`/`getFigures`/
5
+ * `usePanelBackend`/`usePanelFocusClaimed`/`requestPanelClose`/`setPanelHint`/`t`)
6
+ * 注入为面板产物的全局引用(非常规 import,见 `packages/coding/src/plugin/panel-compiler.ts`
7
+ * 头注释)。此前每个面板 `.tsx` 文件各自手写一份 `declare function ...`(逐文件重复),
8
+ * 是漂移风险源——2026-08-11 review 实测发现 schedule.tsx 三处 `backend.call<TResult>()`
9
+ * 调用漏传 `TParams` 泛型参数(`call<TParams, TResult>` 签名下单参数会被错误绑定到
10
+ * `TParams`),typecheck 覆盖打开后才暴露(本文件与该修复同批落地)。
11
+ *
12
+ * 本文件用 `declare global` 环境声明(ambient declaration)替代逐文件重复:TS 编译器
13
+ * 按 tsconfig `include` 把本文件纳入同一程序,`declare global` 块内的声明对整个程序
14
+ * 全局可见,面板 `.tsx` 文件无需 import 即可直接使用这些函数名(与
15
+ * `packages/coding/src/global.d.ts` 声明 `globalThis.React` 同一模式)。
16
+ *
17
+ * 本文件是纯类型声明(zero JS 产出),且从不被任何 `.tsx` 文件 import——不会被
18
+ * esbuild 打包,只参与 tsc 类型检查程序。
19
+ */
20
+ export interface PanelTheme {
21
+ text: string | undefined
22
+ inactive: string
23
+ secondary: string
24
+ success: string
25
+ error: string
26
+ warning: string
27
+ special: string
28
+ selection: string
29
+ focusBorder: string
30
+ panelBorder: string
31
+ }
32
+
33
+ export interface PanelFigures {
34
+ check: string
35
+ cross: string
36
+ pointer: string
37
+ bullet: string
38
+ }
39
+
40
+ export type PanelBackendStatus = 'connecting' | 'ready' | 'crashed' | 'disabled'
41
+
42
+ export interface PanelBackendHandle<TState = unknown> {
43
+ /**
44
+ * `TParams` 必须显式指定(或从传入的 `params` 字面量推断)——本函数签名是
45
+ * `<TParams, TResult>`(不是 `<TResult>`)。调用方若只想指定返回类型,须写
46
+ * `call<Record<string, unknown>, TResult>(...)` 或让 TS 从 params 参数推断 TParams,
47
+ * 不能省略只传一个类型参数(会被错误绑定到 TParams 而非 TResult)。
48
+ */
49
+ call: <TParams extends Record<string, unknown>, TResult = unknown>(
50
+ method: string,
51
+ params: TParams,
52
+ opts?: { operationId?: string },
53
+ ) => Promise<TResult>
54
+ cancel: (operationId: string) => void
55
+ state: TState | undefined
56
+ status: PanelBackendStatus
57
+ }
58
+
59
+ declare global {
60
+ function getTheme(): PanelTheme
61
+ function getFigures(): PanelFigures
62
+ function usePanelFocusClaimed(): boolean
63
+ function requestPanelClose(): void
64
+ function setPanelHint(hint: string | Array<[string, string]> | undefined): void
65
+ function t(key: string, opts?: Record<string, unknown>): string
66
+ function usePanelBackend<TState = unknown>(): PanelBackendHandle<TState>
67
+ }
@@ -0,0 +1,257 @@
1
+ const { usePluginData } = globalThis.__ottoPanelRuntime || {};const { claim: claimPanelFocus, release: releasePanelFocus, usePanelFocusClaimed, requestClose: requestPanelClose, setHint: setPanelHint } = globalThis.__ottoPanelFocusBridge || { claim(){}, release(){}, usePanelFocusClaimed: () => true, requestClose(){}, setHint(){} };const { getTheme, getFigures } = globalThis.__ottoPanelTheme || { getTheme: () => ({ text: undefined, inactive: '#9498B8', secondary: '#C8CBDB', success: '#7EE095', error: '#FF9592', warning: '#FFC966', special: '#DD78C9', selection: '#4D7CFF', focusBorder: '#4D7CFF', panelBorder: '#9498B8' }), getFigures: () => ({ check: '✓', cross: '✗', pointer: '▶', bullet: '●' }) };const { SPACING, LAYOUT } = globalThis.__ottoPanelSpacing || { SPACING: { none: 0, tight: 1, section: 2, loose: 4 }, LAYOUT: { contentInset: 4, borderInset: 2 } };const { t: t, i18next: panelI18next } = globalThis.__ottoPanelI18n || { t: (k) => k, i18next: { language: 'en' } };const { usePanelBackend } = globalThis.__ottoPanelBackendRuntime || { usePanelBackend: () => ({ call: () => Promise.reject(new Error('otto panel: backend runtime not installed')), cancel(){}, state: undefined, status: 'disabled' }) };
2
+
3
+ // ../../../../../tmp/otto-panel-shims-xkXuT3/react-shim.js
4
+ var R = globalThis.React;
5
+ if (!R) throw new Error("otto panel: globalThis.React \u672A\u6CE8\u5165\uFF08TUI \u987B\u5728\u52A0\u8F7D\u9762\u677F\u524D import panel-globals\uFF09");
6
+ var react_shim_default = R;
7
+ var useState = R.useState;
8
+ var useEffect = R.useEffect;
9
+ var useCallback = R.useCallback;
10
+ var useMemo = R.useMemo;
11
+ var useRef = R.useRef;
12
+ var useContext = R.useContext;
13
+ var useReducer = R.useReducer;
14
+ var useLayoutEffect = R.useLayoutEffect;
15
+ var createElement = R.createElement;
16
+ var Fragment = R.Fragment;
17
+ var memo = R.memo;
18
+ var forwardRef = R.forwardRef;
19
+ var createContext = R.createContext;
20
+ var Children = R.Children;
21
+ var cloneElement = R.cloneElement;
22
+ var isValidElement = R.isValidElement;
23
+
24
+ // ../../../../../tmp/otto-panel-shims-xkXuT3/ink-shim.js
25
+ var I = globalThis.__ottoPanelInk;
26
+ if (!I) throw new Error("otto panel: globalThis.__ottoPanelInk \u672A\u6CE8\u5165\uFF08TUI \u987B\u5728\u52A0\u8F7D\u9762\u677F\u524D import panel-globals\uFF09");
27
+ var Box = I.Box;
28
+ var Text = I.Text;
29
+ var Newline = I.Newline;
30
+ var Spacer = I.Spacer;
31
+ var Static = I.Static;
32
+ var Transform = I.Transform;
33
+ var useApp = I.useApp;
34
+ var useInput = I.useInput;
35
+ var useStdin = I.useStdin;
36
+ var useStdout = I.useStdout;
37
+ var useStderr = I.useStderr;
38
+ var useFocus = I.useFocus;
39
+ var useFocusManager = I.useFocusManager;
40
+ var measureElement = I.measureElement;
41
+ var render = I.render;
42
+
43
+ // ../../../../../tmp/otto-panel-shims-xkXuT3/tui-shim.js
44
+ var T = globalThis.__ottoPanelTui;
45
+ if (!T) throw new Error("otto panel: globalThis.__ottoPanelTui \u672A\u6CE8\u5165\uFF08TUI \u987B\u5728\u52A0\u8F7D\u9762\u677F\u524D import panel-globals\uFF09");
46
+ var Tabs = T.Tabs;
47
+ var DataList = T.DataList;
48
+ var Button = T.Button;
49
+ var GroupDivider = T.GroupDivider;
50
+ var ScrollIndicatorAbove = T.ScrollIndicatorAbove;
51
+ var ScrollIndicatorBelow = T.ScrollIndicatorBelow;
52
+ var Tabable = T.Tabable;
53
+ var useTabable = T.useTabable;
54
+ var TabScope = T.TabScope;
55
+
56
+ // extensions/plugin-feedback/panels/feedback.tsx
57
+ var POLL_MS = 15e3;
58
+ var sourceBadge = (source, theme) => {
59
+ const label = source === "github" ? "GH" : "COD";
60
+ const color = source === "github" ? theme.success : theme.warning;
61
+ return /* @__PURE__ */ react_shim_default.createElement(Text, { key: "badge", color, bold: true }, "[", label, "]");
62
+ };
63
+ function formatDate(iso) {
64
+ if (!iso) return "";
65
+ try {
66
+ return new Date(iso).toLocaleDateString();
67
+ } catch {
68
+ return iso;
69
+ }
70
+ }
71
+ function rowId(row) {
72
+ return `${row.source}:${row.platformIssueId}`;
73
+ }
74
+ function FeedbackPanel() {
75
+ const backend = usePanelBackend();
76
+ const [view, setView] = useState("list");
77
+ const [selectedIndex, setSelectedIndex] = useState(0);
78
+ const [filterMode, setFilterMode] = useState("all");
79
+ const [listResult, setListResult] = useState(void 0);
80
+ const [resolvedMarks, setResolvedMarks] = useState([]);
81
+ const [platform, setPlatform] = useState("github");
82
+ const [loading, setLoading] = useState(true);
83
+ const [formTitle, setFormTitle] = useState("");
84
+ const [formRepro, setFormRepro] = useState("");
85
+ const [formExpected, setFormExpected] = useState("");
86
+ const [formActual, setFormActual] = useState("");
87
+ const [formFocus, setFormFocus] = useState(0);
88
+ const [formMsg, setFormMsg] = useState(null);
89
+ const [formMsgTone, setFormMsgTone] = useState(null);
90
+ const [submitting, setSubmitting] = useState(false);
91
+ const refresh = useCallback(async () => {
92
+ setLoading(true);
93
+ try {
94
+ const [listResult2, resolvedResult, platformResult] = await Promise.all([
95
+ backend.call("feedback.listIssues", { state: "all", limitPerSource: 20 }),
96
+ backend.call("feedback.listResolvedMarks", {}),
97
+ backend.call("feedback.resolvePlatform", {})
98
+ ]);
99
+ setListResult(listResult2);
100
+ setResolvedMarks(resolvedResult.marks ?? []);
101
+ setPlatform(platformResult.platform);
102
+ } catch {
103
+ } finally {
104
+ setLoading(false);
105
+ }
106
+ }, [backend]);
107
+ useEffect(() => {
108
+ const timer = setInterval(refresh, POLL_MS);
109
+ refresh();
110
+ return () => clearInterval(timer);
111
+ }, [refresh]);
112
+ const allRows = useMemo(() => listResult?.items ?? [], [listResult]);
113
+ const resolvedSet = useMemo(() => {
114
+ const s = /* @__PURE__ */ new Set();
115
+ for (const m of resolvedMarks) s.add(`${m.source}:${m.platformIssueId}`);
116
+ return s;
117
+ }, [resolvedMarks]);
118
+ const filteredRows = useMemo(() => {
119
+ return allRows.filter((row) => {
120
+ const isResolved = resolvedSet.has(rowId(row));
121
+ if (filterMode === "resolved") return isResolved;
122
+ if (filterMode === "open") return !isResolved;
123
+ return true;
124
+ });
125
+ }, [allRows, resolvedSet, filterMode]);
126
+ useEffect(() => {
127
+ setSelectedIndex((i) => filteredRows.length === 0 ? 0 : Math.min(i, filteredRows.length - 1));
128
+ }, [filteredRows]);
129
+ const markResolved = useCallback(async (row) => {
130
+ await backend.call("feedback.markResolved", {
131
+ source: row.source,
132
+ platformIssueId: row.platformIssueId,
133
+ url: row.url,
134
+ title: row.title
135
+ });
136
+ refresh();
137
+ }, [backend, refresh]);
138
+ const unmarkResolved = useCallback(async (row) => {
139
+ await backend.call("feedback.unmarkResolved", {
140
+ source: row.source,
141
+ platformIssueId: row.platformIssueId
142
+ });
143
+ refresh();
144
+ }, [backend, refresh]);
145
+ const submitForm = useCallback(async () => {
146
+ if (submitting) return;
147
+ if (!formTitle.trim()) {
148
+ setFormMsg(t("feedbackPanel.formRequired"));
149
+ return;
150
+ }
151
+ setSubmitting(true);
152
+ setFormMsg(t("feedbackPanel.formSubmitting"));
153
+ setFormMsgTone(null);
154
+ try {
155
+ const resp = await backend.call("feedback.submitIssue", {
156
+ title: formTitle,
157
+ reproduce: formRepro || void 0,
158
+ expected: formExpected || void 0,
159
+ actual: formActual || void 0
160
+ });
161
+ if (resp?.ok && resp.url) {
162
+ setFormMsg(t("feedbackPanel.formSuccess", { url: resp.url }));
163
+ setFormMsgTone("success");
164
+ setView("list");
165
+ } else if (resp?.error === "repo-unresolved") {
166
+ setFormMsg(t("feedbackPanel.formRepoUnresolved"));
167
+ setFormMsgTone("error");
168
+ } else {
169
+ setFormMsg(t("feedbackPanel.formNoChannel"));
170
+ setFormMsgTone("error");
171
+ }
172
+ } catch {
173
+ setFormMsg(t("feedbackPanel.formNoChannel"));
174
+ setFormMsgTone("error");
175
+ } finally {
176
+ setSubmitting(false);
177
+ }
178
+ }, [backend, formTitle, formRepro, formExpected, formActual, submitting]);
179
+ useInput((input, key) => {
180
+ if (view === "new") {
181
+ if (key.escape || input === "q") {
182
+ setView("list");
183
+ setFormMsg(null);
184
+ return;
185
+ }
186
+ if (key.tab) {
187
+ setFormFocus((f) => (f + 1) % 4);
188
+ return;
189
+ }
190
+ if (key.return) {
191
+ void submitForm();
192
+ return;
193
+ }
194
+ if (key.backspace || key.delete) {
195
+ if (formFocus === 0) setFormTitle((v) => v.slice(0, -1));
196
+ else if (formFocus === 1) setFormRepro((v) => v.slice(0, -1));
197
+ else if (formFocus === 2) setFormExpected((v) => v.slice(0, -1));
198
+ else if (formFocus === 3) setFormActual((v) => v.slice(0, -1));
199
+ return;
200
+ }
201
+ if (input && input.length === 1) {
202
+ if (formFocus === 0) setFormTitle((v) => v + input);
203
+ else if (formFocus === 1) setFormRepro((v) => v + input);
204
+ else if (formFocus === 2) setFormExpected((v) => v + input);
205
+ else if (formFocus === 3) setFormActual((v) => v + input);
206
+ }
207
+ return;
208
+ }
209
+ if (key.downArrow || input === "j") {
210
+ setSelectedIndex((i) => filteredRows.length === 0 ? 0 : Math.min(i + 1, filteredRows.length - 1));
211
+ }
212
+ if (key.upArrow || input === "k") {
213
+ setSelectedIndex((i) => Math.max(i - 1, 0));
214
+ }
215
+ if (input === "n") setView("new");
216
+ if (input === "r") refresh();
217
+ if (input === "f") setFilterMode((v) => v === "all" ? "open" : v === "open" ? "resolved" : "all");
218
+ if (input === "m" && filteredRows[selectedIndex]) {
219
+ const row = filteredRows[selectedIndex];
220
+ const isResolved = resolvedSet.has(rowId(row));
221
+ if (isResolved) unmarkResolved(row);
222
+ else markResolved(row);
223
+ }
224
+ if (input === "x" && filteredRows[selectedIndex]) {
225
+ unmarkResolved(filteredRows[selectedIndex]);
226
+ }
227
+ if (input === "o" && filteredRows[selectedIndex]) {
228
+ void backend.call("feedback.openExternal", { url: filteredRows[selectedIndex].url });
229
+ }
230
+ if (input === "q" || key.escape) requestPanelClose();
231
+ });
232
+ const theme = getTheme();
233
+ const figures = getFigures();
234
+ useEffect(() => {
235
+ setPanelHint([
236
+ ["n", t("feedbackPanel.hintNew")],
237
+ ["m", t("feedbackPanel.hintMark")],
238
+ ["x", t("feedbackPanel.hintUnmark")],
239
+ ["o", t("feedbackPanel.hintOpen")],
240
+ ["r", t("feedbackPanel.hintRefresh")],
241
+ ["f", t("feedbackPanel.hintFilter")],
242
+ ["q", t("feedbackPanel.hintClose")]
243
+ ]);
244
+ }, []);
245
+ const formRow = (label, value, focused, placeholder) => /* @__PURE__ */ react_shim_default.createElement(Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: !focused }, focused ? figures.pointer : " ", " ", label), /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: !value && !focused }, value || placeholder), /* @__PURE__ */ react_shim_default.createElement(Text, { color: theme.selection }, value || (focused ? "_" : "")));
246
+ if (view === "new") {
247
+ return /* @__PURE__ */ react_shim_default.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ react_shim_default.createElement(Text, { bold: true }, t("feedbackPanel.newTitle")), /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: true }, t("feedbackPanel.newSubtitle", { platform })), formRow(t("feedbackPanel.formTitle"), formTitle, formFocus === 0, ""), formRow(t("feedbackPanel.formRepro"), formRepro, formFocus === 1, t("feedbackPanel.formRepro")), formRow(t("feedbackPanel.formExpected"), formExpected, formFocus === 2, t("feedbackPanel.formExpected")), formRow(t("feedbackPanel.formActual"), formActual, formFocus === 3, t("feedbackPanel.formActual")), formMsg && /* @__PURE__ */ react_shim_default.createElement(Text, { color: formMsgTone === "success" ? theme.success : theme.warning }, formMsg), /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: true }, t("feedbackPanel.formHint")));
248
+ }
249
+ return /* @__PURE__ */ react_shim_default.createElement(Box, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ react_shim_default.createElement(Box, { justifyContent: "space-between" }, /* @__PURE__ */ react_shim_default.createElement(Text, { bold: true }, t("feedbackPanel.title")), /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: true }, t("feedbackPanel.platform", { platform }))), loading && /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: true }, t("feedbackPanel.loading")), listResult?.unavailable && Object.entries(listResult.unavailable).map(([source, reason]) => /* @__PURE__ */ react_shim_default.createElement(Box, { key: source }, /* @__PURE__ */ react_shim_default.createElement(Text, { color: theme.warning }, source, ": ", reason))), /* @__PURE__ */ react_shim_default.createElement(Box, { flexDirection: "column" }, filteredRows.length === 0 && !loading && /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: true }, t("feedbackPanel.empty")), filteredRows.map((row, i) => {
250
+ const isSelected = i === selectedIndex;
251
+ const isResolved = resolvedSet.has(rowId(row));
252
+ return /* @__PURE__ */ react_shim_default.createElement(Box, { key: rowId(row), gap: 1 }, isSelected && /* @__PURE__ */ react_shim_default.createElement(Text, { color: theme.selection }, figures.pointer), sourceBadge(row.source, theme), /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: isResolved }, isResolved ? `${figures.check} ` : "", row.title), /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: true }, formatDate(row.updatedAt ?? row.createdAt)), /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: true }, row.state));
253
+ })), /* @__PURE__ */ react_shim_default.createElement(GroupDivider, { label: t("feedbackPanel.legend") }), /* @__PURE__ */ react_shim_default.createElement(Box, { gap: 2 }, /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: true }, t("feedbackPanel.filter", { filter: filterMode })), /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: true }, t("feedbackPanel.total", { count: String(filteredRows.length) }))), /* @__PURE__ */ react_shim_default.createElement(Text, { dimColor: true }, t("feedbackPanel.resolvedNote")));
254
+ }
255
+ export {
256
+ FeedbackPanel as default
257
+ };
@@ -0,0 +1 @@
1
+ {"apiVersion":1,"compilerVersion":"0.1.0-alpha.6","sourceHash":"9b098c146a255a15c3f2959595ba5729a2da6d666882a4fb8795e4abc1256b39"}