@perrylink/dsh-github 0.7.10 → 0.7.12

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/lib/client.js CHANGED
@@ -1,305 +1,291 @@
1
- window.__ModuleLoader__.load({
2
- id: "@perrylink/dsh-github",
3
- factory: (require) => {
4
- var module = { exports: {} };
5
- var exports = module.exports;
6
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- let react = require("react");
8
- let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
- //#region ../src/client.ts
10
- /**
11
- * dsh-github browser half — one card in the "Plugins" settings section.
12
- *
13
- * The card edits the `dsh-github` settings namespace and lets the user fill
14
- * the GitHub token directly from the settings page. The token literal never
15
- * rides the settings document: it is written through the credentials domain,
16
- * addressed by the reference the namespace names (`tokenRef`, default
17
- * `GITHUB_TOKEN`), which is exactly where the host half resolves it — per
18
- * operation, no restart needed.
19
- *
20
- * The card is collapsible and mirrors the host's `PluginCard` chrome one for
21
- * one (same design tokens, same layout), the way the market card does: the
22
- * plugins tab only lays out a flex column and dispatches
23
- * `settings.plugin.item`, so the container is ours to draw, but drawing it
24
- * with the same tokens is what keeps it from looking like it wandered in from
25
- * another product. Buttons and the chevron come from the shared primitives.
26
- *
27
- * The shipped `lib/client.js` is the __ModuleLoader__ bundle built from this
28
- * module (plain ESM here; the bundle wraps it in the loader factory). The
29
- * browser module loader executes that bundle, not this file.
30
- * @module @perrylink/dsh-github/client
31
- */
32
- /**
33
- * Create the minimal card store.
34
- * @param init - initial state.
35
- * @returns the store.
36
- */
37
- function createSnapshotStore(init) {
38
- let state = init;
39
- const listeners = /* @__PURE__ */ new Set();
40
- return {
41
- getSnapshot: () => state,
42
- subscribe: (listener) => {
43
- listeners.add(listener);
44
- return () => {
45
- listeners.delete(listener);
46
- };
47
- },
48
- set: (next) => {
49
- state = next;
50
- for (const listener of [...listeners]) listener();
51
- }
52
- };
53
- }
54
- /** Namespace of the GitHub capability. Spelled here rather than imported: a client package must not depend on a Host package. */
55
- const GITHUB_NS = "dsh-github";
56
- /** Credential reference the provider resolves when the section names none. */
57
- const DEFAULT_TOKEN_REF = "GITHUB_TOKEN";
58
- /** English copy. */
59
- const en = {
60
- githubTitle: "GitHub",
61
- githubDescription: "GitHub pull requests, issues, and CI through the agent.",
62
- tokenLabel: "GitHub token",
63
- tokenHint: "Stored in the credentials file, not here. Applied immediately; leave blank to keep the current token.",
64
- tokenSet: "A token is configured.",
65
- tokenUnset: "No token is configured; GitHub tools are unavailable until one is.",
66
- unsaved: "Unsaved",
67
- readOnly: "This deployment stores settings read-only.",
68
- save: "Save",
69
- saving: "Saving…",
70
- discard: "Discard",
71
- saveFailed: "The deployment did not accept this value; it was left for you to correct."
72
- };
73
- /** Simplified Chinese copy. */
74
- const zh = {
75
- githubTitle: "GitHub",
76
- githubDescription: "通过 agent 操作 GitHub 的 PR、issue 与 CI。",
77
- tokenLabel: "GitHub Token",
78
- tokenHint: "写入凭证文件而非设置文件;保存后立即生效。留空表示保持现有令牌。",
79
- tokenSet: "已配置令牌。",
80
- tokenUnset: "未配置令牌;配置之前 GitHub 工具不可用。",
81
- unsaved: "未保存",
82
- readOnly: "本部署的设置为只读。",
83
- save: "保存",
84
- saving: "保存中…",
85
- discard: "放弃修改",
86
- saveFailed: "本部署没有接受该值,已保留供你修改。"
87
- };
88
- /**
89
- * Bridges the `dsh-github` scope and the credentials domain onto the card.
90
- * The token is the one control that does not live in the section: its literal
91
- * never rides a response, so the card learns only whether one is configured
92
- * and writes it through the credentials domain, addressed by the reference the
93
- * section names.
94
- */
95
- var GithubCardController = class {
96
- scope;
97
- api;
98
- store;
99
- saving = false;
100
- failed = false;
101
- credential = {
102
- ref: "",
103
- configured: false,
104
- writable: true
105
- };
106
- constructor(scope, api) {
107
- this.scope = scope;
108
- this.api = api;
109
- this.store = createSnapshotStore(this.projection());
110
- scope.subscribe(() => {
111
- this.readCredential();
112
- this.publish();
113
- });
114
- this.readCredential();
115
- }
116
- /** Project the card's full state for its snapshot store. */
117
- projection() {
118
- const snapshot = this.scope.getSnapshot();
119
- return {
120
- available: snapshot.status === "ready",
121
- writable: snapshot.writable,
122
- configured: this.credential.configured,
123
- credentialWritable: this.credential.writable,
124
- saving: this.saving,
125
- failed: this.failed,
126
- ref: this.credential.ref
127
- };
128
- }
129
- /** The credential reference the section names, or the provider default. */
130
- refOf() {
131
- const declared = this.scope.getSnapshot().value?.tokenRef;
132
- return declared !== void 0 && declared.length > 0 ? declared : DEFAULT_TOKEN_REF;
133
- }
134
- /**
135
- * Ask the credentials domain about the reference the section names. A
136
- * response is published only while it still answers for the reference in
137
- * force, so two reads settling out of order cannot clobber each other.
138
- */
139
- async readCredential() {
140
- const ref = this.refOf();
141
- if (ref !== this.credential.ref) {
142
- this.credential = {
143
- ref,
144
- configured: false,
145
- writable: true
146
- };
147
- this.publish();
148
- }
149
- let response;
150
- try {
151
- response = await this.api.describe([ref]);
152
- } catch {
153
- return;
154
- }
155
- if (!response.ok || ref !== this.refOf()) return;
156
- const view = response.value[ref];
157
- const next = {
158
- ref,
159
- configured: view?.configured ?? false,
160
- writable: view?.writable ?? true
161
- };
162
- if (next.configured === this.credential.configured && next.writable === this.credential.writable) return;
163
- this.credential = next;
164
- this.publish();
165
- }
166
- /**
167
- * Re-read after the Host reports a change to the reference this card
168
- * watches — a token can be written from elsewhere, and the section does not
169
- * change when it is.
170
- * @param ref - the reference the Host reports as changed.
171
- */
172
- refreshCredential(ref) {
173
- if (ref !== this.credential.ref) return;
174
- this.readCredential();
175
- }
176
- /**
177
- * Write the staged token, then re-read whether the Host now holds one. A
178
- * blank value writes nothing, which keeps the stored key.
179
- * @param value - the staged credential literal.
180
- * @returns whether the Host reports a configured credential afterwards.
181
- */
182
- async save(value) {
183
- const text = (value ?? "").trim();
184
- if (text === "" || this.saving) return false;
185
- this.saving = true;
186
- this.failed = false;
187
- this.publish();
188
- try {
189
- await this.api.set(this.refOf(), text);
190
- } catch {}
191
- await this.readCredential();
192
- const landed = this.credential.configured;
193
- this.saving = false;
194
- this.failed = !landed;
195
- this.publish();
196
- return landed;
197
- }
198
- /** Build the face the card's slot registration injects. */
199
- inject() {
200
- return {
201
- hooks: { githubCard: this.store },
202
- submit: (value) => this.save(value)
203
- };
204
- }
205
- publish() {
206
- this.store.set(this.projection());
207
- }
208
- };
209
- /** One class name builder (static strings only — no bundler CSS modules here). */
210
- const cx = (...parts) => parts.filter(Boolean).join(" ");
211
- /**
212
- * Render the GitHub card: a collapsible header naming the plugin, and — once
213
- * expanded — the token control and the save/discard row. Renders nothing
214
- * while the namespace is unavailable.
215
- */
216
- function GithubCard(props) {
217
- const { t } = props;
218
- const state = props.useGithubCard((snapshot) => snapshot);
219
- const [open, setOpen] = (0, react.useState)(false);
220
- const [draft, setDraft] = (0, react.useState)("");
221
- if (!state.available) return null;
222
- const title = t("githubTitle");
223
- const dirty = draft.trim() !== "";
224
- const saveLabel = state.saving ? t("saving") : t("save");
225
- return (0, react.createElement)("li", { className: cx("ghc-card", open && "ghc-cardOpen") }, (0, react.createElement)("button", {
226
- type: "button",
227
- className: "ghc-header",
228
- "aria-expanded": open,
229
- "aria-label": `${open ? "Collapse" : "Expand"}: ${title}`,
230
- onClick: () => setOpen(!open)
231
- }, (0, react.createElement)("span", { className: "ghc-headText" }, (0, react.createElement)("span", { className: "ghc-name" }, title), (0, react.createElement)("span", { className: "ghc-description" }, t("githubDescription"))), dirty ? (0, react.createElement)("span", { className: "ghc-pending" }, t("unsaved")) : null, (0, react.createElement)("span", { className: cx("ghc-chevron", open && "ghc-chevronOpen") }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }))), open ? (0, react.createElement)("div", { className: "ghc-body" }, !state.writable ? (0, react.createElement)("p", {
232
- className: "ghc-readOnly",
233
- role: "status"
234
- }, t("readOnly")) : null, (0, react.createElement)("div", { className: "ghc-field" }, (0, react.createElement)("div", { className: "ghc-head" }, (0, react.createElement)("label", {
235
- className: "ghc-label",
236
- htmlFor: "plugin-config-github-token"
237
- }, t("tokenLabel")), (0, react.createElement)("span", { className: "ghc-badges" }, (0, react.createElement)("span", { className: state.configured ? "ghc-badge" : "ghc-badgeMuted" }, state.configured ? t("tokenSet") : t("tokenUnset")))), (0, react.createElement)("input", {
238
- id: "plugin-config-github-token",
239
- className: "ghc-input",
240
- type: "password",
241
- autoComplete: "off",
242
- value: draft,
243
- disabled: !state.credentialWritable,
244
- onChange: (event) => setDraft(event.target.value)
245
- }), (0, react.createElement)("p", { className: "ghc-hint" }, t("tokenHint"))), (0, react.createElement)("div", { className: "ghc-actions" }, state.failed ? (0, react.createElement)("p", {
246
- className: "ghc-failed",
247
- role: "status"
248
- }, t("saveFailed")) : null, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
249
- variant: "ghost",
250
- size: "sm",
251
- disabled: !dirty || state.saving,
252
- onClick: () => setDraft("")
253
- }, t("discard")), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
254
- variant: "primary",
255
- size: "sm",
256
- disabled: !state.writable || !dirty || state.saving,
257
- icon: state.saving ? (0, react.createElement)("span", { className: "ghc-spin" }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutline16, { size: 16 })) : void 0,
258
- onClick: async () => {
259
- if (await props.submit(draft)) setDraft("");
260
- }
261
- }, saveLabel))) : null);
262
- }
263
- /** Dictionary namespace owned by this plugin. */
264
- const NS = "dsh-github";
265
- /** Required services (cordis fiber inject). */
266
- const inject = [
267
- "slots",
268
- "locale",
269
- "connection",
270
- "remote",
271
- "settingsScope",
272
- "remote.credentials"
273
- ];
274
- /** Mount the GitHub configuration card into the plugins settings section. */
275
- function apply(ctx) {
276
- ctx.locale.bind(NS);
277
- ctx.effect(() => ctx.locale.register(NS, {
278
- zh,
279
- en
280
- }), "dsh-github: card dictionaries");
281
- const github = new GithubCardController(ctx.settingsScope.bind({ namespace: NS }), ctx.remote.credentials);
282
- ctx.effect(() => ctx.remote.$on("credentials/reference-updated", (ref) => github.refreshCredential(ref)), "dsh-github: credential invalidations");
283
- ctx.slots.inject("settings.plugin.item", function* () {
284
- yield ctx.slots.register({
285
- name: "settings.plugin.item",
286
- key: NS,
287
- locale: NS,
288
- inject: () => github.inject()
289
- }, GithubCard);
290
- });
291
- }
292
- //#endregion
293
- exports.DEFAULT_TOKEN_REF = DEFAULT_TOKEN_REF;
294
- exports.GITHUB_NS = GITHUB_NS;
295
- exports.GithubCard = GithubCard;
296
- exports.GithubCardController = GithubCardController;
297
- exports.NS = NS;
298
- exports.apply = apply;
299
- exports.createSnapshotStore = createSnapshotStore;
300
- exports.en = en;
301
- exports.inject = inject;
302
- exports.zh = zh;
303
- return module.exports;
304
- }
305
- });
1
+ /**
2
+ * dsh-github browser half — one card in the Plugins page's Official group.
3
+ *
4
+ * The card reports and edits the GitHub token. The token literal never rides
5
+ * the settings document: it is written through the credentials domain,
6
+ * addressed by the reference the plugin's cordis.yml `Config.tokenRef` names
7
+ * (default `GITHUB_TOKEN`), which is exactly where the host half resolves it —
8
+ * per operation, no restart needed.
9
+ *
10
+ * Since host 0.1.7-alpha.1 the page owns configuration: the card renders the
11
+ * `summary` one-liner and the `page` body from the `plugins.item` owner props
12
+ * (`view`, and the `form` the Plugins page supplies), instead of binding a
13
+ * `settingsScope` itself — that service, `settings.plugin.item`'s old shape,
14
+ * and the `dsh-settings-file` package behind them were all removed. The card
15
+ * therefore reads `tokenRef` from `form.state.value` and never writes the
16
+ * settings document; the credential is written through the credentials
17
+ * domain, which is a different seam and stayed.
18
+ *
19
+ * The shipped `lib/client.js` is the built bundle of this module. The browser
20
+ * module loader executes that bundle, not this file.
21
+ * @module @perrylink/dsh-github/client
22
+ */
23
+ import { createElement, useState } from 'react';
24
+ import { Button, IconLoadingOutline16, } from '@deepseek-ai/dsh-client-ui-primitives';
25
+ /**
26
+ * Create the minimal card store.
27
+ * @param init - initial state.
28
+ * @returns the store.
29
+ */
30
+ export function createSnapshotStore(init) {
31
+ let state = init;
32
+ const listeners = new Set();
33
+ return {
34
+ getSnapshot: () => state,
35
+ subscribe: (listener) => {
36
+ listeners.add(listener);
37
+ return () => { listeners.delete(listener); };
38
+ },
39
+ set: (next) => {
40
+ state = next;
41
+ for (const listener of [...listeners])
42
+ listener();
43
+ },
44
+ };
45
+ }
46
+ /** Namespace of the GitHub capability. Spelled here rather than imported: a client package must not depend on a Host package. */
47
+ export const GITHUB_NS = 'dsh-github';
48
+ /** Credential reference the provider resolves when the configuration names none. */
49
+ export const DEFAULT_TOKEN_REF = 'GITHUB_TOKEN';
50
+ /** English copy. */
51
+ export const en = {
52
+ githubTitle: 'GitHub',
53
+ githubDescription: 'GitHub pull requests, issues, and CI through the agent.',
54
+ tokenLabel: 'GitHub token',
55
+ tokenHint: 'Stored in the credentials file, not here. Applied immediately; leave blank to keep the current token.',
56
+ tokenSet: 'A token is configured.',
57
+ tokenUnset: 'No token is configured; GitHub tools are unavailable until one is.',
58
+ unsaved: 'Unsaved',
59
+ readOnly: 'This deployment stores settings read-only.',
60
+ save: 'Save',
61
+ saving: 'Saving…',
62
+ discard: 'Discard',
63
+ saveFailed: 'The deployment did not accept this value; it was left for you to correct.',
64
+ };
65
+ /** Simplified Chinese copy. */
66
+ export const zh = {
67
+ githubTitle: 'GitHub',
68
+ githubDescription: '通过 agent 操作 GitHub 的 PR、issue 与 CI。',
69
+ tokenLabel: 'GitHub Token',
70
+ tokenHint: '写入凭证文件而非设置文件;保存后立即生效。留空表示保持现有令牌。',
71
+ tokenSet: '已配置令牌。',
72
+ tokenUnset: '未配置令牌;配置之前 GitHub 工具不可用。',
73
+ unsaved: '未保存',
74
+ readOnly: '本部署的设置为只读。',
75
+ save: '保存',
76
+ saving: '保存中…',
77
+ discard: '放弃修改',
78
+ saveFailed: '本部署没有接受该值,已保留供你修改。',
79
+ };
80
+ /**
81
+ * Bridges the page-supplied configuration form and the credentials domain onto
82
+ * the card. The token is the one control that does not live in the
83
+ * configuration: its literal never rides a response, so the card learns only
84
+ * whether one is configured and writes it through the credentials domain,
85
+ * addressed by the reference `Config.tokenRef` names.
86
+ */
87
+ export class GithubCardController {
88
+ form;
89
+ api;
90
+ store;
91
+ saving = false;
92
+ failed = false;
93
+ credential = { ref: '', configured: false, writable: true };
94
+ /**
95
+ * @param form - the configuration form the Plugins page hands this entry, or
96
+ * `undefined` when the page supplied none (the card then reads the default
97
+ * reference and offers the credential control alone).
98
+ * @param api - the credentials Remote namespace.
99
+ */
100
+ constructor(form, api) {
101
+ this.form = form;
102
+ this.api = api;
103
+ this.store = createSnapshotStore(this.projection());
104
+ form?.subscribe(() => {
105
+ void this.readCredential();
106
+ this.publish();
107
+ });
108
+ void this.readCredential();
109
+ }
110
+ /** Whether the Host configuration document accepts writes. */
111
+ writable() {
112
+ return this.form?.getSnapshot().writable ?? true;
113
+ }
114
+ /** The configuration form this controller reads (identity-compared by the caller). */
115
+ formOf() {
116
+ return this.form;
117
+ }
118
+ /** Project the card's full state for its snapshot store. */
119
+ projection() {
120
+ return {
121
+ writable: this.writable(),
122
+ configured: this.credential.configured,
123
+ credentialWritable: this.credential.writable,
124
+ saving: this.saving,
125
+ failed: this.failed,
126
+ ref: this.credential.ref,
127
+ };
128
+ }
129
+ /** The credential reference the configuration names, or the provider default. */
130
+ refOf() {
131
+ const declared = this.form?.getSnapshot().value?.tokenRef;
132
+ return declared !== undefined && declared.length > 0 ? declared : DEFAULT_TOKEN_REF;
133
+ }
134
+ /**
135
+ * Ask the credentials domain about the reference the section names. A
136
+ * response is published only while it still answers for the reference in
137
+ * force, so two reads settling out of order cannot clobber each other.
138
+ */
139
+ async readCredential() {
140
+ const ref = this.refOf();
141
+ if (ref !== this.credential.ref) {
142
+ this.credential = { ref, configured: false, writable: true };
143
+ this.publish();
144
+ }
145
+ let response;
146
+ try {
147
+ response = await this.api.describe([ref]);
148
+ }
149
+ catch {
150
+ return;
151
+ }
152
+ if (!response.ok || ref !== this.refOf())
153
+ return;
154
+ const view = response.value[ref];
155
+ const next = { ref, configured: view?.configured ?? false, writable: view?.writable ?? true };
156
+ if (next.configured === this.credential.configured && next.writable === this.credential.writable)
157
+ return;
158
+ this.credential = next;
159
+ this.publish();
160
+ }
161
+ /**
162
+ * Re-read after the Host reports a change to the reference this card
163
+ * watches — a token can be written from elsewhere, and the section does not
164
+ * change when it is.
165
+ * @param ref - the reference the Host reports as changed.
166
+ */
167
+ refreshCredential(ref) {
168
+ if (ref !== this.credential.ref)
169
+ return;
170
+ void this.readCredential();
171
+ }
172
+ /**
173
+ * Write the staged token, then re-read whether the Host now holds one. A
174
+ * blank value writes nothing, which keeps the stored key.
175
+ * @param value - the staged credential literal.
176
+ * @returns whether the Host reports a configured credential afterwards.
177
+ */
178
+ async save(value) {
179
+ const text = (value ?? '').trim();
180
+ if (text === '' || this.saving)
181
+ return false;
182
+ this.saving = true;
183
+ this.failed = false;
184
+ this.publish();
185
+ try {
186
+ await this.api.set(this.refOf(), text);
187
+ }
188
+ catch {
189
+ // handled below by re-reading the configured state
190
+ }
191
+ await this.readCredential();
192
+ const landed = this.credential.configured;
193
+ this.saving = false;
194
+ this.failed = !landed;
195
+ this.publish();
196
+ return landed;
197
+ }
198
+ /** Build the face the card's slot registration injects. */
199
+ inject() {
200
+ return {
201
+ hooks: { githubCard: this.store },
202
+ submit: (value) => this.save(value),
203
+ };
204
+ }
205
+ publish() {
206
+ this.store.set(this.projection());
207
+ }
208
+ }
209
+ /**
210
+ * Render the GitHub card. `summary` renders the one-liner the Plugins page
211
+ * places under the card's title (the description plus the token state badge);
212
+ * `page` renders the token control and the save/discard row on the plugin's
213
+ * own page. The page supplies the configuration form, so the card never
214
+ * depends on a served namespace to render.
215
+ */
216
+ export function GithubCard(props) {
217
+ const { t } = props;
218
+ const state = props.useGithubCard((snapshot) => snapshot);
219
+ const [draft, setDraft] = useState('');
220
+ if (props.view === 'summary') {
221
+ return createElement('span', { className: 'ghc-summary' }, createElement('span', null, t('githubDescription')), createElement('span', { className: state.configured ? 'ghc-badge' : 'ghc-badgeMuted' }, state.configured ? t('tokenSet') : t('tokenUnset')));
222
+ }
223
+ const dirty = draft.trim() !== '';
224
+ const saveLabel = state.saving ? t('saving') : t('save');
225
+ return createElement('div', { className: 'ghc-card ghc-cardPage' }, !state.writable
226
+ ? createElement('p', { className: 'ghc-readOnly', role: 'status' }, t('readOnly'))
227
+ : null, createElement('div', { className: 'ghc-field' }, createElement('div', { className: 'ghc-head' }, createElement('label', { className: 'ghc-label', htmlFor: 'plugin-config-github-token' }, t('tokenLabel')), createElement('span', { className: 'ghc-badges' }, createElement('span', { className: state.configured ? 'ghc-badge' : 'ghc-badgeMuted' }, state.configured ? t('tokenSet') : t('tokenUnset')))), createElement('input', {
228
+ id: 'plugin-config-github-token',
229
+ className: 'ghc-input',
230
+ type: 'password',
231
+ autoComplete: 'off',
232
+ value: draft,
233
+ disabled: !state.credentialWritable,
234
+ onChange: (event) => setDraft(event.target.value),
235
+ }), createElement('p', { className: 'ghc-hint' }, t('tokenHint'))), createElement('div', { className: 'ghc-actions' }, state.failed ? createElement('p', { className: 'ghc-failed', role: 'status' }, t('saveFailed')) : null, createElement(Button, {
236
+ variant: 'ghost',
237
+ size: 'sm',
238
+ disabled: !dirty || state.saving,
239
+ onClick: () => setDraft(''),
240
+ }, t('discard')), createElement(Button, {
241
+ variant: 'primary',
242
+ size: 'sm',
243
+ disabled: !state.writable || !dirty || state.saving,
244
+ icon: state.saving
245
+ ? createElement('span', { className: 'ghc-spin' }, createElement(IconLoadingOutline16, { size: 16 }))
246
+ : undefined,
247
+ onClick: async () => {
248
+ const landed = await props.submit(draft);
249
+ if (landed)
250
+ setDraft('');
251
+ },
252
+ }, saveLabel)));
253
+ }
254
+ /** Dictionary namespace owned by this plugin. */
255
+ export const NS = 'dsh-github';
256
+ /**
257
+ * Required services (cordis fiber inject). `settingsScope` is gone: host
258
+ * 0.1.7-alpha.1 removed it, and the Plugins page now hands the configuration
259
+ * form to the `plugins.item` entry instead. `@deepseek-ai/dsh-client-ui-plugin-manager`
260
+ * owns that slot, so it must be composed for the card to mount at all.
261
+ */
262
+ export const inject = ['slots', 'locale', 'connection', 'remote', 'remote.credentials'];
263
+ /**
264
+ * Mount the GitHub configuration card into the Plugins page's Official group.
265
+ *
266
+ * The form arrives per render, so the controller is rebuilt whenever the page
267
+ * supplies a different one — the page's own form owner stays authoritative and
268
+ * the card keeps no second copy of the accepted values.
269
+ * @param ctx - the browser plugin context.
270
+ */
271
+ export function apply(ctx) {
272
+ const t = ctx.locale.bind(NS);
273
+ ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-github: card dictionaries');
274
+ let github;
275
+ const controllerFor = (form) => {
276
+ if (github === undefined || github.formOf() !== form) {
277
+ github = new GithubCardController(form, ctx.remote.credentials);
278
+ }
279
+ return github;
280
+ };
281
+ ctx.effect(() => ctx.remote.$on('credentials/reference-updated', (ref) => github?.refreshCredential(ref)), 'dsh-github: credential invalidations');
282
+ ctx.slots.inject('plugins.item', () => ctx.slots.register({
283
+ name: 'plugins.item',
284
+ id: NS,
285
+ order: 100,
286
+ label: () => t('githubTitle'),
287
+ locale: NS,
288
+ inject: () => controllerFor(github?.formOf()).inject(),
289
+ }, GithubCard));
290
+ }
291
+ //# sourceMappingURL=client.js.map