dsh-connect 0.8.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +236 -18
- package/README.zh.md +185 -18
- package/client/client.js +517 -84
- package/client/client.js.map +4 -4
- package/client/locale.mjs +227 -0
- package/client/panel-state.mjs +54 -0
- package/client/settings-client.mjs +282 -61
- package/docs/images/settings-advanced-en.png +0 -0
- package/docs/images/settings-advanced-zh.png +0 -0
- package/docs/images/settings-defaults-en.png +0 -0
- package/docs/images/settings-defaults-zh.png +0 -0
- package/docs/images/settings-overview-en.png +0 -0
- package/docs/images/settings-overview-zh.png +0 -0
- package/lib/binding.d.ts.map +1 -1
- package/lib/binding.js +2 -2
- package/lib/binding.js.map +1 -1
- package/lib/channels/dingtalk/index.d.ts +49 -49
- package/lib/channels/dingtalk/index.d.ts.map +1 -1
- package/lib/channels/dingtalk/message.d.ts.map +1 -1
- package/lib/channels/dingtalk/message.js +7 -0
- package/lib/channels/dingtalk/message.js.map +1 -1
- package/lib/channels/feishu/adapter.d.ts +14 -13
- package/lib/channels/feishu/adapter.d.ts.map +1 -1
- package/lib/channels/feishu/adapter.js +122 -56
- package/lib/channels/feishu/adapter.js.map +1 -1
- package/lib/channels/feishu/i18n.d.ts +2 -0
- package/lib/channels/feishu/i18n.d.ts.map +1 -1
- package/lib/channels/feishu/i18n.js +2 -0
- package/lib/channels/feishu/i18n.js.map +1 -1
- package/lib/channels/feishu/index.d.ts +48 -27
- package/lib/channels/feishu/index.d.ts.map +1 -1
- package/lib/channels/feishu/index.js +32 -6
- package/lib/channels/feishu/index.js.map +1 -1
- package/lib/channels/telegram/adapter.d.ts.map +1 -1
- package/lib/channels/telegram/adapter.js +7 -1
- package/lib/channels/telegram/adapter.js.map +1 -1
- package/lib/channels/telegram/index.d.ts +13 -13
- package/lib/channels/telegram/index.d.ts.map +1 -1
- package/lib/channels/web/adapter.d.ts +3 -1
- package/lib/channels/web/adapter.d.ts.map +1 -1
- package/lib/channels/web/adapter.js +3 -1
- package/lib/channels/web/adapter.js.map +1 -1
- package/lib/channels/web/index.d.ts +5 -5
- package/lib/channels/web/index.d.ts.map +1 -1
- package/lib/chat-key.d.ts +29 -0
- package/lib/chat-key.d.ts.map +1 -0
- package/lib/chat-key.js +38 -0
- package/lib/chat-key.js.map +1 -0
- package/lib/index.d.ts +57 -63
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +330 -55
- package/lib/index.js.map +1 -1
- package/lib/interaction.d.ts +90 -50
- package/lib/interaction.d.ts.map +1 -1
- package/lib/interaction.js +233 -272
- package/lib/interaction.js.map +1 -1
- package/lib/retry.d.ts.map +1 -1
- package/lib/retry.js +7 -1
- package/lib/retry.js.map +1 -1
- package/lib/runner.d.ts +54 -0
- package/lib/runner.d.ts.map +1 -1
- package/lib/runner.js +158 -23
- package/lib/runner.js.map +1 -1
- package/lib/scheduler.d.ts.map +1 -1
- package/lib/scheduler.js +2 -2
- package/lib/scheduler.js.map +1 -1
- package/lib/service.d.ts +16 -0
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +46 -4
- package/lib/service.js.map +1 -1
- package/lib/settings/channel-runtime.d.ts +75 -0
- package/lib/settings/channel-runtime.d.ts.map +1 -0
- package/lib/settings/channel-runtime.js +165 -0
- package/lib/settings/channel-runtime.js.map +1 -0
- package/lib/settings/channels.d.ts +11 -0
- package/lib/settings/channels.d.ts.map +1 -1
- package/lib/settings/channels.js +31 -0
- package/lib/settings/channels.js.map +1 -1
- package/lib/settings/credential-store.d.ts +52 -6
- package/lib/settings/credential-store.d.ts.map +1 -1
- package/lib/settings/credential-store.js +92 -18
- package/lib/settings/credential-store.js.map +1 -1
- package/lib/settings/legacy-import.d.ts +190 -0
- package/lib/settings/legacy-import.d.ts.map +1 -0
- package/lib/settings/legacy-import.js +373 -0
- package/lib/settings/legacy-import.js.map +1 -0
- package/lib/settings/namespace.d.ts +263 -0
- package/lib/settings/namespace.d.ts.map +1 -0
- package/lib/settings/namespace.js +287 -0
- package/lib/settings/namespace.js.map +1 -0
- package/lib/settings/secret-disclosure.d.ts +50 -0
- package/lib/settings/secret-disclosure.d.ts.map +1 -0
- package/lib/settings/secret-disclosure.js +132 -0
- package/lib/settings/secret-disclosure.js.map +1 -0
- package/lib/settings/settings-model.d.ts +17 -0
- package/lib/settings/settings-model.d.ts.map +1 -1
- package/lib/settings/settings-model.js +18 -1
- package/lib/settings/settings-model.js.map +1 -1
- package/lib/settings/settings-rpc.d.ts +71 -3
- package/lib/settings/settings-rpc.d.ts.map +1 -1
- package/lib/settings/settings-rpc.js +167 -24
- package/lib/settings/settings-rpc.js.map +1 -1
- package/lib/settings/settings-service.d.ts +22 -1
- package/lib/settings/settings-service.d.ts.map +1 -1
- package/lib/settings/settings-service.js +145 -8
- package/lib/settings/settings-service.js.map +1 -1
- package/lib/state-dir.d.ts +28 -0
- package/lib/state-dir.d.ts.map +1 -0
- package/lib/state-dir.js +33 -0
- package/lib/state-dir.js.map +1 -0
- package/lib/stream.d.ts.map +1 -1
- package/lib/stream.js +3 -2
- package/lib/stream.js.map +1 -1
- package/lib/types.d.ts +14 -1
- package/lib/types.d.ts.map +1 -1
- package/package.json +13 -10
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which channel cards the settings pane shows open, and which fields hide behind
|
|
3
|
+
* each card's "advanced" fold.
|
|
4
|
+
*
|
|
5
|
+
* Dependency-free and separate from the React component for the same reason
|
|
6
|
+
* `locale.mjs` is: these rules are the interesting part, they are pure, and a
|
|
7
|
+
* test can pin them without a DOM. The bundle test in particular cannot — it
|
|
8
|
+
* renders the component with pre-supplied hook values, so it can never observe
|
|
9
|
+
* what the component *initializes* to. Only this module can answer that.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Fields that are set once and then left alone, grouped by channel.
|
|
14
|
+
*
|
|
15
|
+
* These are what makes the pane tall: with every channel expanded the pane
|
|
16
|
+
* renders 20 field blocks, each a label + input + preview + hint. Folding the
|
|
17
|
+
* rarely-touched ones behind a second-level disclosure keeps the common case —
|
|
18
|
+
* credentials plus the handful of behavioural switches — on one screen.
|
|
19
|
+
* `test/panel-state.test.mjs` asserts every key here really exists in
|
|
20
|
+
* `CHANNEL_CONFIG_FIELDS`, because a typo would silently drop a field from the
|
|
21
|
+
* UI rather than fail anything.
|
|
22
|
+
*/
|
|
23
|
+
export const ADVANCED_KEYS = {
|
|
24
|
+
feishu: ['webhookPort', 'webhookPath'],
|
|
25
|
+
telegram: ['pollingTimeoutSeconds', 'baseUrl'],
|
|
26
|
+
dingtalk: ['defaultAt'],
|
|
27
|
+
web: ['pollIntervalMs'],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** True when `key` is one of `channel`'s low-frequency fields. */
|
|
31
|
+
export function isAdvanced(channel, key) {
|
|
32
|
+
return (ADVANCED_KEYS[channel] ?? []).includes(key);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The channels whose card starts open: the enabled ones, or — when nothing is
|
|
37
|
+
* enabled at all — the first channel, so the pane never opens as a wall of
|
|
38
|
+
* collapsed headers with no contents in sight.
|
|
39
|
+
*
|
|
40
|
+
* Unknown names are filtered out rather than trusted: `enabled` comes from the
|
|
41
|
+
* host and would otherwise open a card that does not exist.
|
|
42
|
+
*/
|
|
43
|
+
export function initialOpenChannels(enabled, all) {
|
|
44
|
+
const open = (enabled ?? []).filter((ch) => all.includes(ch));
|
|
45
|
+
return new Set(open.length > 0 ? open : all.slice(0, 1));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Toggle one key in a Set, returning a new Set (never mutating the argument). */
|
|
49
|
+
export function toggleInSet(set, key) {
|
|
50
|
+
const next = new Set(set);
|
|
51
|
+
if (next.has(key)) next.delete(key);
|
|
52
|
+
else next.add(key);
|
|
53
|
+
return next;
|
|
54
|
+
}
|
|
@@ -8,46 +8,93 @@
|
|
|
8
8
|
* `settings-model.js` / `rpc-client.js` helpers; this component is a thin
|
|
9
9
|
* renderer. Build the lib first (`pnpm --filter dsh-connect build`), then
|
|
10
10
|
* bundle this in a `dsh web` shell (Vite).
|
|
11
|
+
*
|
|
12
|
+
* The pane is an accordion, not a flat list. Four channels with every field
|
|
13
|
+
* visible is ~2000px of content in a ~690px scroller, so the channel cards
|
|
14
|
+
* collapse, rarely-touched fields hide behind a second-level "advanced" fold,
|
|
15
|
+
* and a tab strip at the top jumps to (and opens) a channel. The rules for
|
|
16
|
+
* which cards start open live in `panel-state.mjs` — pure, and testable without
|
|
17
|
+
* a DOM, which matters because the bundle test's React stub can only supply a
|
|
18
|
+
* component's hook values, never observe what it initializes them to.
|
|
11
19
|
*/
|
|
12
20
|
import * as React from 'react';
|
|
13
21
|
|
|
14
22
|
import { SETTINGS_RPC_CHANNEL } from '../lib/settings/settings-rpc.js';
|
|
15
23
|
import { loadSettings, saveSettings, saveCredentials } from '../lib/settings/rpc-client.js';
|
|
16
24
|
import { snapshotToForm, buildConfigSave, buildCredentialSaves, CHANNEL_SECRET_FIELDS, CHANNEL_CONFIG_FIELDS, CHANNEL_DEFAULT_FIELDS, coerceConfigValue } from '../lib/settings/settings-model.js';
|
|
25
|
+
// Shared with the host, so the pane and the masking it displays can never
|
|
26
|
+
// disagree about which keys are confidential.
|
|
27
|
+
import { isMaskedSecret } from '../lib/settings/secret-disclosure.js';
|
|
28
|
+
import { LOCALES, tr, optionalText } from './locale.mjs';
|
|
29
|
+
import { initialOpenChannels, toggleInSet, isAdvanced } from './panel-state.mjs';
|
|
17
30
|
|
|
18
31
|
export const name = 'dsh-connect-settings';
|
|
19
32
|
export const inject = ['slots', 'connection', 'locale'];
|
|
20
33
|
export const NS = 'dsh-connect';
|
|
21
34
|
|
|
22
|
-
export const locale =
|
|
23
|
-
zh: { title: '连接设置', channels: '渠道', save: '保存', saved: '已保存', error: '保存失败', loading: '加载中', defaults: '公共默认(channelDefaults)', statePath: '设置文件', secret: '密钥', config: '配置', reachable: '已连接凭据', unreachable: '未配置凭据' },
|
|
24
|
-
en: { title: 'Connection Settings', channels: 'Channels', save: 'Save', saved: 'Saved', error: 'Save failed', loading: 'Loading', defaults: 'Defaults (channelDefaults)', statePath: 'Settings file', secret: 'Secret', config: 'Config', reachable: 'Credential set', unreachable: 'Credential missing' },
|
|
25
|
-
};
|
|
35
|
+
export const locale = LOCALES;
|
|
26
36
|
|
|
27
37
|
const h = React.createElement;
|
|
28
38
|
|
|
39
|
+
/** The channels the pane knows about, in the order the cards are rendered. */
|
|
40
|
+
const ALL_CHANNELS = Object.keys(CHANNEL_SECRET_FIELDS);
|
|
41
|
+
|
|
29
42
|
// Self-contained scoped stylesheet for the pane (injected once). The host's own
|
|
30
43
|
// settings panes use per-feature CSS modules we can't import into this separate
|
|
31
44
|
// bundle, so we ship our own consistent, theme-aware styles instead. Everything
|
|
32
45
|
// is scoped under `.dsh-connect-settings` so nothing leaks into the shell.
|
|
46
|
+
//
|
|
47
|
+
// Two things here are load-bearing rather than cosmetic:
|
|
48
|
+
//
|
|
49
|
+
// 1. The `box-sizing` reset. Neither this bundle nor the host shell ships a
|
|
50
|
+
// global one, so under the default `content-box` a `.ds-input` with
|
|
51
|
+
// `width:100%` is ~20px (padding + border) *wider* than the grid column it
|
|
52
|
+
// sits in, and paints over the neighbouring column's text. That overflow is
|
|
53
|
+
// what "文字跟窗口重叠" actually was.
|
|
54
|
+
// 2. The host's theme tokens, with our old hex values as fallbacks. The host
|
|
55
|
+
// puts `--dsw-alias-*` on `body` / `body[data-ds-dark-theme]`, so inheriting
|
|
56
|
+
// them follows the user's in-app theme. A `prefers-color-scheme` block — what
|
|
57
|
+
// used to be here — ignores that choice entirely and repaints the pane black
|
|
58
|
+
// on a light shell for anyone whose OS is dark. Do not bring it back.
|
|
33
59
|
const STYLE = `
|
|
34
|
-
.dsh-connect-settings
|
|
35
|
-
|
|
36
|
-
.dsh-connect-settings .ds-card{border:1px solid var(--ds-border);border-radius:10px;background:var(--ds-bg);padding:12px 14px
|
|
60
|
+
.dsh-connect-settings,.dsh-connect-settings *,.dsh-connect-settings *::before,.dsh-connect-settings *::after{box-sizing:border-box}
|
|
61
|
+
.dsh-connect-settings{--ds-bg:var(--dsw-alias-bg-layer-3,#ffffff);--ds-bg-sub:var(--dsw-alias-bg-layer-1,#f6f7f9);--ds-text:var(--dsw-alias-label-primary,#1f2329);--ds-muted:var(--dsw-alias-label-tertiary,#646a73);--ds-border:var(--dsw-alias-border-l2,#e2e4e8);--ds-border-2:var(--dsw-alias-border-l3,#c8cbd0);--ds-accent:var(--dsw-alias-state-business-primary,#3b82f6);--ds-hover:var(--dsw-alias-interactive-bg-hover,#2631480f);display:flex;flex-direction:column;gap:12px;max-width:760px;color:var(--ds-text);font:13px/1.6 -apple-system,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif}
|
|
62
|
+
.dsh-connect-settings .ds-card{display:flex;flex-direction:column;gap:10px;border:1px solid var(--ds-border);border-radius:10px;background:var(--ds-bg);padding:12px 14px}
|
|
37
63
|
.dsh-connect-settings .ds-card-title{margin:0;font-size:13px;font-weight:600}
|
|
38
|
-
.dsh-connect-settings .ds-
|
|
39
|
-
.dsh-connect-settings .ds-
|
|
40
|
-
.dsh-connect-settings .ds-
|
|
41
|
-
.dsh-connect-settings .ds-
|
|
42
|
-
.dsh-connect-settings .ds-
|
|
43
|
-
.dsh-connect-settings .ds-
|
|
44
|
-
.dsh-connect-settings .ds-
|
|
45
|
-
.dsh-connect-settings .ds-
|
|
46
|
-
.dsh-connect-settings .ds-
|
|
64
|
+
.dsh-connect-settings .ds-note{margin:0;font-size:11px;line-height:1.5;color:var(--ds-muted)}
|
|
65
|
+
.dsh-connect-settings .ds-tabs{display:flex;flex-wrap:wrap;gap:6px}
|
|
66
|
+
.dsh-connect-settings .ds-tab{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border:1px solid var(--ds-border);border-radius:14px;background:transparent;color:var(--ds-text);font:inherit;font-size:12px;cursor:pointer}
|
|
67
|
+
.dsh-connect-settings .ds-tab:hover{background:var(--ds-hover)}
|
|
68
|
+
.dsh-connect-settings .ds-tab[aria-expanded=true]{border-color:var(--ds-accent);color:var(--ds-accent)}
|
|
69
|
+
.dsh-connect-settings .ds-dot{width:6px;height:6px;border-radius:50%;background:var(--ds-border-2);flex:none}
|
|
70
|
+
.dsh-connect-settings .ds-dot[data-on="1"]{background:var(--ds-accent)}
|
|
71
|
+
.dsh-connect-settings .ds-channel{border:1px solid var(--ds-border);border-radius:8px;background:var(--ds-bg-sub)}
|
|
72
|
+
.dsh-connect-settings .ds-channel-head{display:flex;align-items:center;gap:8px;padding:5px 10px}
|
|
73
|
+
.dsh-connect-settings .ds-enable{display:flex;align-items:center;flex:none}
|
|
74
|
+
.dsh-connect-settings .ds-channel-toggle{flex:1;min-width:0;display:flex;align-items:center;gap:8px;padding:5px 6px;border:0;border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;font-weight:600;text-align:left;cursor:pointer}
|
|
75
|
+
.dsh-connect-settings .ds-channel-toggle:hover{background:var(--ds-hover);color:var(--ds-accent)}
|
|
76
|
+
.dsh-connect-settings .ds-channel-name{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
77
|
+
.dsh-connect-settings .ds-channel-body{display:flex;flex-direction:column;gap:8px;padding:0 10px 10px}
|
|
78
|
+
.dsh-connect-settings .ds-chevron{width:7px;height:7px;margin:-3px 4px 0 auto;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(45deg);transition:transform .15s ease;flex:none}
|
|
79
|
+
.dsh-connect-settings [aria-expanded=true]>.ds-chevron,.dsh-connect-settings [aria-expanded=true] .ds-chevron{transform:rotate(-135deg);margin-top:2px}
|
|
80
|
+
.dsh-connect-settings .ds-badge{flex:none;font-size:11px;font-weight:500;padding:1px 8px;border-radius:99px;background:var(--ds-bg);color:var(--ds-muted);border:1px solid var(--ds-border-2)}
|
|
81
|
+
.dsh-connect-settings .ds-fields{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px 14px}
|
|
82
|
+
.dsh-connect-settings .ds-field{display:flex;flex-direction:column;gap:4px;min-width:0}
|
|
83
|
+
.dsh-connect-settings .ds-control{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--ds-muted)}
|
|
84
|
+
.dsh-connect-settings .ds-control.ds-check-field{flex-direction:row;align-items:center;gap:6px}
|
|
85
|
+
.dsh-connect-settings .ds-hint{margin:0;font-size:11px;line-height:1.5;color:var(--ds-muted)}
|
|
86
|
+
.dsh-connect-settings .ds-preview{margin:0;font-size:11px;line-height:1.5;color:var(--ds-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;word-break:break-all}
|
|
87
|
+
.dsh-connect-settings .ds-preview b{font-weight:500;font-family:inherit;opacity:.75}
|
|
88
|
+
.dsh-connect-settings .ds-check{flex:none;width:16px;height:16px;accent-color:var(--ds-accent)}
|
|
89
|
+
.dsh-connect-settings .ds-input{height:30px;width:100%;min-width:0;padding:0 9px;border:1px solid var(--ds-border-2);border-radius:6px;background:var(--ds-bg);color:var(--ds-text);font:inherit}
|
|
47
90
|
.dsh-connect-settings select.ds-input{cursor:pointer}
|
|
48
|
-
.dsh-connect-settings .ds-input:focus{outline:none;border-color:var(--ds-accent);box-shadow:0 0 0 2px var(--ds-
|
|
49
|
-
.dsh-connect-settings .ds-
|
|
50
|
-
.dsh-connect-settings .ds-
|
|
91
|
+
.dsh-connect-settings .ds-input:focus{outline:none;border-color:var(--ds-accent);box-shadow:0 0 0 2px rgba(59,130,246,.25);box-shadow:0 0 0 2px color-mix(in srgb,var(--ds-accent) 25%,transparent)}
|
|
92
|
+
.dsh-connect-settings .ds-adv{display:flex;flex-direction:column;gap:8px}
|
|
93
|
+
.dsh-connect-settings .ds-advanced-toggle{align-self:flex-start;display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border:1px dashed var(--ds-border-2);border-radius:99px;background:transparent;color:var(--ds-muted);font:inherit;font-size:11px;cursor:pointer}
|
|
94
|
+
.dsh-connect-settings .ds-advanced-toggle:hover{background:var(--ds-hover);color:var(--ds-text)}
|
|
95
|
+
.dsh-connect-settings .ds-footer{position:sticky;bottom:0;z-index:1;display:flex;align-items:center;gap:12px;padding:10px 14px;border:1px solid var(--ds-border);border-radius:10px;background:var(--ds-bg)}
|
|
96
|
+
.dsh-connect-settings .ds-btn{height:32px;padding:0 18px;border:0;border-radius:6px;background:var(--dsw-alias-button-primary-fill,var(--ds-accent));color:var(--dsw-alias-label-primary-foreground,#ffffff);font:inherit;font-weight:500;cursor:pointer}
|
|
97
|
+
.dsh-connect-settings .ds-btn:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--ds-accent))}
|
|
51
98
|
.dsh-connect-settings .ds-btn:disabled{opacity:.55;cursor:default}
|
|
52
99
|
.dsh-connect-settings .ds-status{font-size:12px;color:var(--ds-muted)}
|
|
53
100
|
`;
|
|
@@ -61,29 +108,138 @@ function injectStyles() {
|
|
|
61
108
|
document.head.appendChild(el);
|
|
62
109
|
}
|
|
63
110
|
|
|
64
|
-
// Render
|
|
65
|
-
|
|
66
|
-
|
|
111
|
+
// Render one non-secret config field: its control, then its explanation.
|
|
112
|
+
//
|
|
113
|
+
// Label and hint are looked up by *config key* (`f.<key>`), so a field added
|
|
114
|
+
// without its text shows a legible fallback (`field.label`) instead of a raw
|
|
115
|
+
// identifier — and the locale-coverage test fails, which is the real guard.
|
|
116
|
+
function renderConfigField(field, value, onChange, t) {
|
|
117
|
+
const label = tr(t, `f.${field.key}`, field.label ?? field.key);
|
|
118
|
+
const hint = optionalText(t, `f.${field.key}.hint`);
|
|
119
|
+
let control;
|
|
67
120
|
if (field.kind === 'boolean') {
|
|
68
|
-
|
|
121
|
+
control = h('label', { className: 'ds-control ds-check-field' },
|
|
69
122
|
h('input', { className: 'ds-check', type: 'checkbox', checked: !!value, onChange: (e) => onChange(e.target.checked) }),
|
|
70
123
|
' ' + label);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return h('label', { className: 'ds-field', key: `cfg-${field.key}` },
|
|
74
|
-
label + ' ',
|
|
124
|
+
} else if (field.kind === 'select') {
|
|
125
|
+
control = h('label', { className: 'ds-control' }, label + ' ',
|
|
75
126
|
h('select', { className: 'ds-input', value: value ?? '', onChange: (e) => onChange(e.target.value) },
|
|
76
|
-
h('option', { value: '' }, '
|
|
77
|
-
...(field.options ?? []).map((opt) => h('option', { key: opt, value: opt }, opt))));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return h('label', { className: 'ds-field', key: `cfg-${field.key}` },
|
|
81
|
-
label + ' ',
|
|
127
|
+
h('option', { value: '' }, tr(t, 'o.default', label)),
|
|
128
|
+
...(field.options ?? []).map((opt) => h('option', { key: opt, value: opt }, tr(t, `o.${opt}`, opt)))));
|
|
129
|
+
} else if (field.kind === 'number') {
|
|
130
|
+
control = h('label', { className: 'ds-control' }, label + ' ',
|
|
82
131
|
h('input', { className: 'ds-input', type: 'number', value: value ?? '', onChange: (e) => onChange(e.target.value) }));
|
|
132
|
+
} else {
|
|
133
|
+
control = h('label', { className: 'ds-control' }, label + ' ',
|
|
134
|
+
h('input', { className: 'ds-input', type: 'text', value: value ?? '', onChange: (e) => onChange(e.target.value) }));
|
|
83
135
|
}
|
|
84
|
-
return h('
|
|
85
|
-
|
|
86
|
-
h('
|
|
136
|
+
return h('div', { className: 'ds-field', key: `cfg-${field.key}` },
|
|
137
|
+
control,
|
|
138
|
+
hint ? h('p', { className: 'ds-hint' }, hint) : null);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Render one secret field: a write-only input, plus a read-only preview of the
|
|
142
|
+
// stored value so the user can confirm what they configured.
|
|
143
|
+
//
|
|
144
|
+
// The preview is plain text, never the input's `value`. The host already masked
|
|
145
|
+
// it, and seeding an input with a mask would let a save write the mask *back* as
|
|
146
|
+
// the credential. Keeping the input blank also keeps "empty" meaning "leave the
|
|
147
|
+
// stored value alone", which is what a password field is expected to do.
|
|
148
|
+
function renderSecretField(ch, field, form, onChange, t) {
|
|
149
|
+
const preview = form.secretPreviews?.[ch]?.[field] ?? '';
|
|
150
|
+
const hint = optionalText(t, `s.${ch}.${field}.hint`);
|
|
151
|
+
return h('div', { className: 'ds-field', key: `sec-${ch}-${field}` },
|
|
152
|
+
h('label', { className: 'ds-control' },
|
|
153
|
+
tr(t, `s.${ch}.${field}`, field),
|
|
154
|
+
h('input', {
|
|
155
|
+
className: 'ds-input',
|
|
156
|
+
// Confidential keys stay masked while typing; identifiers (appId,
|
|
157
|
+
// clientId) don't, so a typo is visible before it is ever saved.
|
|
158
|
+
type: isMaskedSecret(field) ? 'password' : 'text',
|
|
159
|
+
autoComplete: 'off',
|
|
160
|
+
placeholder: preview ? t('configured') : t('notConfigured'),
|
|
161
|
+
value: form.secrets?.[ch]?.[field] ?? '',
|
|
162
|
+
onChange: (e) => onChange(e.target.value),
|
|
163
|
+
})),
|
|
164
|
+
h('p', { className: 'ds-preview' },
|
|
165
|
+
h('b', null, t('current')),
|
|
166
|
+
preview === '' ? t('notConfigured') : preview),
|
|
167
|
+
hint ? h('p', { className: 'ds-hint' }, hint) : null);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// One channel card: an enable checkbox, a header that folds the card, and — when
|
|
171
|
+
// open — its credentials and settings.
|
|
172
|
+
//
|
|
173
|
+
// A plain function rather than a child component, deliberately: the bundle test
|
|
174
|
+
// supplies hook values *positionally*, so every `useState` in the tree has to
|
|
175
|
+
// live in `ConnectSettingsTab` in a fixed order. Nothing in here may call a hook.
|
|
176
|
+
function renderChannel(ch, ctx) {
|
|
177
|
+
const { form, creds, t, open, advOverride, setChannels, setField, setChannelConfig, toggleOpen, toggleAdvanced } = ctx;
|
|
178
|
+
const name = tr(t, `channel.${ch}`, ch);
|
|
179
|
+
const channelHint = optionalText(t, `channel.${ch}.hint`);
|
|
180
|
+
const isOpen = open.has(ch);
|
|
181
|
+
const advOpen = advOverride?.has(ch) ?? false;
|
|
182
|
+
const configFields = CHANNEL_CONFIG_FIELDS[ch] ?? [];
|
|
183
|
+
// The split is what keeps a common case on one screen: two credentials and the
|
|
184
|
+
// behavioural switches, with the set-once fields one disclosure deeper.
|
|
185
|
+
const common = configFields.filter((f) => !isAdvanced(ch, f.key));
|
|
186
|
+
const advanced = configFields.filter((f) => isAdvanced(ch, f.key));
|
|
187
|
+
|
|
188
|
+
const configField = (field) => renderConfigField(
|
|
189
|
+
field,
|
|
190
|
+
form.channelConfigs?.[ch]?.[field.key],
|
|
191
|
+
(raw) => setChannelConfig(ch, field.key, raw),
|
|
192
|
+
t,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
return h('div', { className: 'ds-channel', key: ch, id: `ds-ch-${ch}` },
|
|
196
|
+
// The checkbox sits beside the header button, not inside it: a button may not
|
|
197
|
+
// contain another interactive element, and browsers disagree about what to do
|
|
198
|
+
// when one does.
|
|
199
|
+
h('div', { className: 'ds-channel-head' },
|
|
200
|
+
h('label', { className: 'ds-enable' },
|
|
201
|
+
h('input', {
|
|
202
|
+
className: 'ds-check',
|
|
203
|
+
type: 'checkbox',
|
|
204
|
+
'aria-label': name,
|
|
205
|
+
checked: form.channels.includes(ch),
|
|
206
|
+
onChange: (e) => setChannels(ch, e.target.checked),
|
|
207
|
+
})),
|
|
208
|
+
h('button', {
|
|
209
|
+
type: 'button',
|
|
210
|
+
className: 'ds-channel-toggle',
|
|
211
|
+
'aria-expanded': isOpen,
|
|
212
|
+
// Only while the body exists: pointing at an id that is not in the
|
|
213
|
+
// document is worse than not pointing at all.
|
|
214
|
+
'aria-controls': isOpen ? `ds-ch-${ch}-body` : undefined,
|
|
215
|
+
'aria-label': `${name} — ${isOpen ? t('collapse') : t('expand')}`,
|
|
216
|
+
onClick: () => toggleOpen(ch),
|
|
217
|
+
},
|
|
218
|
+
h('span', { className: 'ds-channel-name' }, name),
|
|
219
|
+
h('span', { className: 'ds-badge' }, creds[ch] ? t('reachable') : t('unreachable')),
|
|
220
|
+
// Drawn in CSS, never a text node: the bundle test reads rendered text as
|
|
221
|
+
// user-visible copy, and a `▾` here would be collected as a stray string.
|
|
222
|
+
h('span', { className: 'ds-chevron' }))),
|
|
223
|
+
// Collapsed means *not rendered*, not `display:none`. The inputs are
|
|
224
|
+
// controlled from the parent's `form.secrets`, so folding a card cannot lose
|
|
225
|
+
// an unsaved secret — keeping it mounted would buy nothing.
|
|
226
|
+
isOpen ? h('div', { className: 'ds-channel-body', id: `ds-ch-${ch}-body` },
|
|
227
|
+
channelHint ? h('p', { className: 'ds-note' }, channelHint) : null,
|
|
228
|
+
h('div', { className: 'ds-fields' },
|
|
229
|
+
...CHANNEL_SECRET_FIELDS[ch].map((field) => renderSecretField(ch, field, form, (value) => setField(ch, field, value), t)),
|
|
230
|
+
...common.map(configField)),
|
|
231
|
+
advanced.length === 0 ? null : h('div', { className: 'ds-adv' },
|
|
232
|
+
h('button', {
|
|
233
|
+
type: 'button',
|
|
234
|
+
className: 'ds-advanced-toggle',
|
|
235
|
+
'aria-expanded': advOpen,
|
|
236
|
+
'aria-controls': advOpen ? `ds-ch-${ch}-adv` : undefined,
|
|
237
|
+
onClick: () => toggleAdvanced(ch),
|
|
238
|
+
},
|
|
239
|
+
h('span', null, t('advanced')),
|
|
240
|
+
h('span', { className: 'ds-chevron' })),
|
|
241
|
+
advOpen ? h('div', { className: 'ds-fields', id: `ds-ch-${ch}-adv` }, ...advanced.map(configField)) : null),
|
|
242
|
+
) : null);
|
|
87
243
|
}
|
|
88
244
|
|
|
89
245
|
// Thin React renderer over the tested settings-model helpers.
|
|
@@ -91,6 +247,8 @@ export function ConnectSettingsTab({ rpcCall, t }) {
|
|
|
91
247
|
const [form, setForm] = React.useState(null);
|
|
92
248
|
const [status, setStatus] = React.useState('loading');
|
|
93
249
|
const [creds, setCreds] = React.useState({});
|
|
250
|
+
const [openOverride, setOpenOverride] = React.useState(null);
|
|
251
|
+
const [advOverride, setAdvOverride] = React.useState(null);
|
|
94
252
|
const rpc = (endpoint, payload) => rpcCall(endpoint, payload);
|
|
95
253
|
|
|
96
254
|
React.useEffect(() => {
|
|
@@ -108,13 +266,32 @@ export function ConnectSettingsTab({ rpcCall, t }) {
|
|
|
108
266
|
if (!form) return;
|
|
109
267
|
setStatus('saving');
|
|
110
268
|
try {
|
|
111
|
-
|
|
112
|
-
|
|
269
|
+
// The last snapshot in the chain wins: it is the one that reflects every
|
|
270
|
+
// write this save performed. Re-seeding the form from it also drops the
|
|
271
|
+
// typed secret values (their inputs are write-only by design) and picks up
|
|
272
|
+
// the new presence flags.
|
|
273
|
+
let snap = await saveSettings(rpc, buildConfigSave(form));
|
|
274
|
+
for (const c of buildCredentialSaves(form)) snap = await saveCredentials(rpc, c.channel, c.values);
|
|
275
|
+
setForm(snapshotToForm(snap));
|
|
276
|
+
setCreds(snap.credentials ?? {});
|
|
113
277
|
setStatus('saved');
|
|
114
278
|
} catch { setStatus('error'); }
|
|
115
279
|
};
|
|
116
280
|
|
|
117
|
-
const setChannels = (ch, on) =>
|
|
281
|
+
const setChannels = (ch, on) => {
|
|
282
|
+
setForm((f) => ({ ...f, channels: on ? [...f.channels, ch] : f.channels.filter((x) => x !== ch) }));
|
|
283
|
+
// Enabling a channel opens it, and only ever opens: the user just said they
|
|
284
|
+
// care about this one.
|
|
285
|
+
//
|
|
286
|
+
// Recording the fold *either way* is what keeps the card from vanishing
|
|
287
|
+
// under the cursor. Until the user touches an enable box, the open set is
|
|
288
|
+
// derived on every render from the enabled list — so unticking Feishu drops
|
|
289
|
+
// it from `form.channels`, the derived default is recomputed without it, and
|
|
290
|
+
// the card collapses exactly as the user is looking at it. Writing the
|
|
291
|
+
// current fold into the override first freezes it, so an untick closes
|
|
292
|
+
// nothing.
|
|
293
|
+
setOpenOverride(on && !open.has(ch) ? new Set(open).add(ch) : new Set(open));
|
|
294
|
+
};
|
|
118
295
|
const setField = (ch, field, value) => setForm((f) => ({ ...f, secrets: { ...f.secrets, [ch]: { ...(f.secrets[ch] ?? {}), [field]: value } } }));
|
|
119
296
|
const setChannelConfig = (ch, key, raw) => setForm((f) => {
|
|
120
297
|
const descriptor = (CHANNEL_CONFIG_FIELDS[ch] ?? []).find((x) => x.key === key);
|
|
@@ -132,36 +309,80 @@ export function ConnectSettingsTab({ rpcCall, t }) {
|
|
|
132
309
|
});
|
|
133
310
|
|
|
134
311
|
if (!form) return h('div', { className: 'dsh-connect-settings' }, t('loading'));
|
|
312
|
+
|
|
313
|
+
// Which cards are open is *derived*, not stored: the first render happens
|
|
314
|
+
// before the host's snapshot arrives, and the test stub cannot run effects, so
|
|
315
|
+
// an effect here would be the only place the default could ever be set — and
|
|
316
|
+
// it would never run. The override records only what the user has since done,
|
|
317
|
+
// on top of that default (`toggleInSet(open, …)` rather than a bare toggle),
|
|
318
|
+
// so opening DingTalk does not silently drop the enabled Feishu the user was
|
|
319
|
+
// already looking at.
|
|
320
|
+
const open = openOverride ?? initialOpenChannels(form.channels, ALL_CHANNELS);
|
|
321
|
+
|
|
322
|
+
const toggleOpen = (ch) => setOpenOverride(toggleInSet(open, ch));
|
|
323
|
+
// Advanced folds default to closed for every channel, so an empty set is the
|
|
324
|
+
// derived default here and no counterpart to `initialOpenChannels` is needed.
|
|
325
|
+
const toggleAdvanced = (ch) => setAdvOverride(toggleInSet(advOverride ?? new Set(), ch));
|
|
326
|
+
const focusChannel = (ch) => {
|
|
327
|
+
// A tab expands and scrolls, but never collapses: "show me Telegram" must not
|
|
328
|
+
// close the channel the user is halfway through editing.
|
|
329
|
+
if (!open.has(ch)) setOpenOverride(new Set(open).add(ch));
|
|
330
|
+
if (typeof document === 'undefined' || !document.getElementById) return;
|
|
331
|
+
// The header is rendered whether or not the card is open, so this resolves
|
|
332
|
+
// before React has re-rendered, and lands in the same place either way.
|
|
333
|
+
document.getElementById(`ds-ch-${ch}`)?.scrollIntoView?.({ block: 'nearest' });
|
|
334
|
+
};
|
|
335
|
+
|
|
135
336
|
return h('div', { className: 'dsh-connect-settings' },
|
|
136
337
|
h('section', { className: 'ds-card' },
|
|
137
338
|
h('h4', { className: 'ds-card-title' }, t('channels')),
|
|
339
|
+
// Explains the masking before the user meets a truncated value and wonders
|
|
340
|
+
// whether their stored secret is corrupt.
|
|
341
|
+
h('p', { className: 'ds-note' }, t('previewNote')),
|
|
342
|
+
// A tab strip, but not `role=tablist`: several channels can be open at
|
|
343
|
+
// once, so there is no single "selected" tab to report. These are buttons
|
|
344
|
+
// that open and jump to a channel, and `aria-expanded` says so honestly.
|
|
345
|
+
h('nav', { className: 'ds-tabs', 'aria-label': t('tabsAria') },
|
|
346
|
+
...ALL_CHANNELS.map((ch) => h('button', {
|
|
347
|
+
key: `tab-${ch}`,
|
|
348
|
+
type: 'button',
|
|
349
|
+
className: 'ds-tab',
|
|
350
|
+
'aria-expanded': open.has(ch),
|
|
351
|
+
'aria-controls': open.has(ch) ? `ds-ch-${ch}-body` : undefined,
|
|
352
|
+
onClick: () => focusChannel(ch),
|
|
353
|
+
},
|
|
354
|
+
h('span', { className: 'ds-dot', 'data-on': form.channels.includes(ch) ? '1' : '0' }),
|
|
355
|
+
tr(t, `channel.${ch}`, ch)))),
|
|
138
356
|
// One card per built-in channel: enable toggle + cred badge + secret + config fields.
|
|
139
|
-
...
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
h('span', null, ch),
|
|
144
|
-
h('span', { className: 'ds-badge' }, creds[ch] ? t('reachable') : t('unreachable'))),
|
|
145
|
-
),
|
|
146
|
-
h('div', { className: 'ds-fields' },
|
|
147
|
-
...CHANNEL_SECRET_FIELDS[ch].map((field) => h('label', { className: 'ds-field', key: `sec-${field}` },
|
|
148
|
-
field,
|
|
149
|
-
h('input', { className: 'ds-input', type: 'password', placeholder: field, value: form.secrets?.[ch]?.[field] ?? '', onChange: (e) => setField(ch, field, e.target.value) }))),
|
|
150
|
-
...(CHANNEL_CONFIG_FIELDS[ch] ?? []).map((field) => renderConfigField(field, form.channelConfigs?.[ch]?.[field.key], (raw) => setChannelConfig(ch, field.key, raw))),
|
|
151
|
-
),
|
|
152
|
-
)),
|
|
153
|
-
),
|
|
357
|
+
...ALL_CHANNELS.map((ch) => renderChannel(ch, {
|
|
358
|
+
form, creds, t, open, advOverride,
|
|
359
|
+
setChannels, setField, setChannelConfig, toggleOpen, toggleAdvanced,
|
|
360
|
+
}))),
|
|
154
361
|
h('section', { className: 'ds-card' },
|
|
155
362
|
h('h4', { className: 'ds-card-title' }, t('defaults')),
|
|
363
|
+
h('p', { className: 'ds-note' }, t('defaultsHint')),
|
|
156
364
|
h('div', { className: 'ds-fields' },
|
|
157
|
-
...CHANNEL_DEFAULT_FIELDS.map((field) => renderConfigField(field, form.channelDefaults?.[field.key], (raw) => setDefault(field.key, raw))),
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
h('
|
|
163
|
-
|
|
365
|
+
...CHANNEL_DEFAULT_FIELDS.map((field) => renderConfigField(field, form.channelDefaults?.[field.key], (raw) => setDefault(field.key, raw), t)),
|
|
366
|
+
// Only meaningful on the fallback plane: it names the file the pane
|
|
367
|
+
// persists to. With the namespace live that path is not consulted (and
|
|
368
|
+
// is not part of the section), so showing an editable field for it would
|
|
369
|
+
// silently swallow edits.
|
|
370
|
+
form.live ? null : h('div', { className: 'ds-field' },
|
|
371
|
+
h('label', { className: 'ds-control' }, t('statePath'),
|
|
372
|
+
h('input', { className: 'ds-input', value: form.settingsStatePath ?? '', onChange: (e) => setForm((f) => ({ ...f, settingsStatePath: e.target.value })) })),
|
|
373
|
+
h('p', { className: 'ds-hint' }, t('statePathHint'))),
|
|
164
374
|
),
|
|
375
|
+
h('div', { className: 'ds-status' }, form.live ? t('livePlane') : t('filePlane')),
|
|
376
|
+
),
|
|
377
|
+
// Last child of the root, not of a card: `position:sticky` pins to the
|
|
378
|
+
// nearest scrollport only while its containing block is the scrolled box.
|
|
379
|
+
// Nested in the defaults card it could never move outside that card, so it
|
|
380
|
+
// pinned to nothing and Save scrolled away with the channel list.
|
|
381
|
+
h('div', { className: 'ds-footer' },
|
|
382
|
+
h('button', { className: 'ds-btn', type: 'button', onClick: onSave, disabled: status === 'saving' }, t('save')),
|
|
383
|
+
// Rendering `status` directly leaks the raw state ids (`idle`, `saving`)
|
|
384
|
+
// into the UI; every state has a locale entry instead.
|
|
385
|
+
h('span', { className: 'ds-status' }, tr(t, `status.${status}`, status)),
|
|
165
386
|
),
|
|
166
387
|
);
|
|
167
388
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/binding.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAYA,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,GAAG,OAAO,CAAC;IAC1B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC9C,oGAAoG;IACpG,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,SAAS,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC7B,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,cAAc,CAAC,EAAE,KAAK,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC1B,qFAAqF;QACrF,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,gEAAgE;IAChE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AA0BD,+CAA+C;AAC/C,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC;AAE5G,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkC;IACtD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;gBAExD,QAAQ,CAAC,EAAE,MAAM;IAK7B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,IAAI;IAOrD;;OAEG;IACH,OAAO,CAAC,UAAU;IAUlB;;;OAGG;IACH,OAAO,IAAI,gBAAgB,CAAC,WAAW,CAAC;IAIxC;;OAEG;IACH,IAAI,IAAI,WAAW,EAAE;IAIrB;;;;OAIG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE;IAUjD,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI9D,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAQ/B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAU9C,OAAO,CAAC,IAAI;IAoCZ,OAAO,CAAC,IAAI;CAQb"}
|
package/lib/binding.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { dirname, resolve } from "node:path";
|
|
10
|
+
import { resolveStateDir } from "./state-dir.js";
|
|
10
11
|
function keyOf(channel, chatKey) {
|
|
11
12
|
return `${channel}\u0000${chatKey}`;
|
|
12
13
|
}
|
|
@@ -35,8 +36,7 @@ export class BindingStore {
|
|
|
35
36
|
file;
|
|
36
37
|
changeListeners = new Set();
|
|
37
38
|
constructor(stateDir) {
|
|
38
|
-
|
|
39
|
-
this.file = resolve(dir, "bindings.json");
|
|
39
|
+
this.file = resolve(resolveStateDir({ stateDir }), "bindings.json");
|
|
40
40
|
this.load();
|
|
41
41
|
}
|
|
42
42
|
/**
|
package/lib/binding.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding.js","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"binding.js","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAoDjD,SAAS,KAAK,CAAC,OAAe,EAAE,OAAe;IAC7C,OAAO,GAAG,OAAO,SAAS,OAAO,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAc;IACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ;YAAE,SAAS;QACpE,GAAG,CAAC,IAAI,CAAC;YACP,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS;YACnE,SAAS,EAAE,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;YAC3E,YAAY,EAAE,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;YACpF,OAAO,EAAE,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;SAC9D,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAKD,MAAM,OAAO,YAAY;IACN,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;IACrC,IAAI,CAAS;IACb,eAAe,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEpE,YAAY,QAAiB;QAC3B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,eAAe,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,QAAQ,CAAC,QAA+B;QACtC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,UAAU,CAAC,OAAoB,EAAE,UAAuC;QAC9E,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YAC5C,IAAI,CAAC;gBACH,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,6CAA6C;YAC/C,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,OAAO;QACL,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACH,eAAe,CAAC,SAAiB;QAC/B,MAAM,OAAO,GAAkB,EAAE,CAAC;QAClC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;YACxC,IAAI,OAAO,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;gBAC7C,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,GAAG,CAAC,OAAe,EAAE,OAAe;QAClC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,GAAG,CAAC,OAAoB;QACtB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,CAAC,OAAe,EAAE,OAAe;QACrC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACrB,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAEO,IAAI;QACV,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC5C,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACxC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,OAAO;YACnC,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;gBAC1B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAC9B,MAAM,CAAC,GAAG,IAAI,CAAC;gBACf,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;oBAAE,SAAS;gBAChH,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE;oBACxC,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;oBAClD,SAAS,EAAE,CAAC,CAAC,SAAS;oBACtB,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;oBAC1D,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;oBACrE,YAAY,EAAE,OAAO,CAAC,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;oBAC9E,GAAG,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/E,GAAG,CAAC,CAAC,CAAC,WAAW,KAAK,MAAM,IAAI,CAAC,CAAC,WAAW,KAAK,WAAW,IAAI,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAClI,GAAG,CAAC,OAAO,CAAC,CAAC,iBAAiB,KAAK,QAAQ,IAAI,CAAC,CAAC,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC1H,GAAG,CAAC,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACzE,GAAG,CAAC,OAAO,CAAC,CAAC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACjG,GAAG,CAAC,CAAC,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACxF,GAAG,CAAC,OAAO,CAAC,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACrF,GAAG,CAAC,OAAO,CAAC,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAClF,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAiF,EAAE,CACnK,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,OAAQ,CAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAQ,CAAS,CAAC,SAAS,KAAK,QAAQ,CACvH,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAQ,CAAS,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAG,CAAS,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC1G,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC;iBACxC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;QAC1C,CAAC;IACH,CAAC;IAEO,IAAI;QACV,IAAI,CAAC;YACH,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACpF,CAAC;QAAC,MAAM,CAAC;YACP,kEAAkE;QACpE,CAAC;IACH,CAAC;CACF"}
|