dsh-smooth-stream 0.1.0 → 0.3.1
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.en.md +103 -0
- package/README.md +62 -47
- package/lib/client.js +545 -44
- package/lib/client.js.map +1 -1
- package/lib/index.js +229 -0
- package/package.json +25 -3
- package/src/client/SmoothStreamCard.module.css +253 -0
- package/src/client/SmoothStreamCard.tsx +122 -0
- package/src/client/TypewriterAssistantNodeView.tsx +11 -3
- package/src/client/index.ts +96 -3
- package/src/client/locales.ts +74 -0
- package/src/client/smooth-stream-card-controller.ts +162 -0
- package/src/client/smooth-stream-settings-api.ts +65 -0
- package/src/package-meta.ts +39 -0
- package/src/plugin.ts +101 -0
- package/src/profile-installation.ts +121 -0
- package/src/settings-api.ts +33 -0
- package/src/settings.ts +30 -0
- package/README.zh.md +0 -88
package/lib/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
1
2
|
import Schema from "@deepseek-ai/schemastery";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
2
7
|
//#region src/config.ts
|
|
3
8
|
/** Defaults shared by the Host schema and the client-side fallback. */
|
|
4
9
|
const DEFAULT_STREAM_CONFIG = {
|
|
@@ -41,6 +46,122 @@ function injectStreamConfig(html, config) {
|
|
|
41
46
|
return `${html.slice(0, at)}${script}${html.slice(at)}`;
|
|
42
47
|
}
|
|
43
48
|
//#endregion
|
|
49
|
+
//#region src/package-meta.ts
|
|
50
|
+
/** Host-only package metadata. The package manifest remains the source of truth. */
|
|
51
|
+
function packageManifestPath() {
|
|
52
|
+
try {
|
|
53
|
+
return join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
54
|
+
} catch {}
|
|
55
|
+
return join(process.cwd(), "package.json");
|
|
56
|
+
}
|
|
57
|
+
const manifest = JSON.parse(readFileSync(packageManifestPath(), "utf8"));
|
|
58
|
+
function readRequiredString(field) {
|
|
59
|
+
const value = manifest[field];
|
|
60
|
+
if (typeof value !== "string" || value.length === 0) throw new Error(`dsh-smooth-stream: package.json must contain a non-empty ${field}`);
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
/** npm package name, read from this plugin's manifest. */
|
|
64
|
+
const STREAM_PACKAGE_NAME = readRequiredString("name");
|
|
65
|
+
/** Version of the code currently loaded by the Host, read from its manifest. */
|
|
66
|
+
const STREAM_PACKAGE_VERSION = readRequiredString("version");
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/profile-installation.ts
|
|
69
|
+
/** Host-only profile source inspection and fixed npm update runner. */
|
|
70
|
+
function record(value) {
|
|
71
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
72
|
+
}
|
|
73
|
+
function profileDirectory(baseUrl) {
|
|
74
|
+
if (baseUrl === void 0) return void 0;
|
|
75
|
+
try {
|
|
76
|
+
const url = new URL(baseUrl);
|
|
77
|
+
return url.protocol === "file:" ? fileURLToPath(url) : void 0;
|
|
78
|
+
} catch {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function hasBundle(manifest, packageName) {
|
|
83
|
+
const bundles = record(record(manifest.dsh)?.profile)?.bundles;
|
|
84
|
+
return Array.isArray(bundles) && bundles.includes(packageName);
|
|
85
|
+
}
|
|
86
|
+
function isLocalSpecifier(specifier) {
|
|
87
|
+
return specifier.startsWith("link:") || specifier.startsWith("file:") || specifier.startsWith(".") || isAbsolute(specifier) || /^[A-Za-z]:[\\/]/.test(specifier);
|
|
88
|
+
}
|
|
89
|
+
function isRegistrySpecifier(specifier) {
|
|
90
|
+
return specifier.length > 0 && !specifier.includes(":") && !/[\\/]/.test(specifier);
|
|
91
|
+
}
|
|
92
|
+
function isNpmSpecifier(specifier, packageName) {
|
|
93
|
+
if (!specifier.startsWith("npm:")) return isRegistrySpecifier(specifier);
|
|
94
|
+
const aliased = specifier.slice(4);
|
|
95
|
+
if (aliased === packageName) return true;
|
|
96
|
+
const prefix = `${packageName}@`;
|
|
97
|
+
return aliased.startsWith(prefix) && isRegistrySpecifier(aliased.slice(prefix.length));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Read only the profile manifest anchored by the current Cordis config tree.
|
|
101
|
+
* A malformed or unrelated tree never receives an update affordance.
|
|
102
|
+
*/
|
|
103
|
+
function inspectProfileInstallation(baseUrl, packageName) {
|
|
104
|
+
const profileDir = profileDirectory(baseUrl);
|
|
105
|
+
if (profileDir === void 0) return { kind: "unmanaged" };
|
|
106
|
+
let manifest;
|
|
107
|
+
try {
|
|
108
|
+
manifest = JSON.parse(readFileSync(join(profileDir, "package.json"), "utf8"));
|
|
109
|
+
} catch {
|
|
110
|
+
return { kind: "unmanaged" };
|
|
111
|
+
}
|
|
112
|
+
const specifier = record(manifest.dependencies)?.[packageName];
|
|
113
|
+
if (typeof specifier !== "string" || !hasBundle(manifest, packageName)) return { kind: "unmanaged" };
|
|
114
|
+
if (isLocalSpecifier(specifier)) return { kind: "development" };
|
|
115
|
+
if (!isNpmSpecifier(specifier, packageName)) return { kind: "unmanaged" };
|
|
116
|
+
return {
|
|
117
|
+
kind: "npm",
|
|
118
|
+
profileDir,
|
|
119
|
+
profileName: basename(profileDir)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** Run the same fixed package update operation that a profile user would invoke. */
|
|
123
|
+
function updateNpmProfilePackage(profileDir, packageName) {
|
|
124
|
+
const command = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
const child = spawn(command, ["update", packageName], {
|
|
127
|
+
cwd: profileDir,
|
|
128
|
+
stdio: "ignore",
|
|
129
|
+
shell: false,
|
|
130
|
+
windowsHide: true
|
|
131
|
+
});
|
|
132
|
+
child.once("error", reject);
|
|
133
|
+
child.once("exit", (code, signal) => {
|
|
134
|
+
if (code === 0) resolve();
|
|
135
|
+
else reject(/* @__PURE__ */ new Error(`dsh-smooth-stream: pnpm update failed (${signal ?? String(code)})`));
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/settings-api.ts
|
|
141
|
+
/** Shared wire vocabulary for the plugin-owned settings RPC channel. */
|
|
142
|
+
/** Dedicated, loopback-only RPC channel registered by the Host half. */
|
|
143
|
+
const STREAM_SETTINGS_RPC_CHANNEL = "/smooth-stream";
|
|
144
|
+
/** Endpoints accepted by {@link STREAM_SETTINGS_RPC_CHANNEL}. */
|
|
145
|
+
const STREAM_SETTINGS_RPC = {
|
|
146
|
+
read: "settings.read",
|
|
147
|
+
write: "settings.write",
|
|
148
|
+
upgrade: "plugin.upgrade"
|
|
149
|
+
};
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/settings.ts
|
|
152
|
+
/**
|
|
153
|
+
* User-owned settings for the smooth-stream plugin, exposed to the Host
|
|
154
|
+
* settings service and edited from the Web Settings "plugin configuration"
|
|
155
|
+
* page. This is the runtime-editable complement to {@link StreamConfig}: that
|
|
156
|
+
* contract is composed at load and bridged once through the boot global, while
|
|
157
|
+
* these preferences live in the durable user-settings document and take effect
|
|
158
|
+
* live.
|
|
159
|
+
*/
|
|
160
|
+
/** Settings namespace registered by the Host and served through the plugin RPC. */
|
|
161
|
+
const STREAM_SETTINGS_NS = "smooth-stream";
|
|
162
|
+
/** Defaults shared by the Host schema and the client-side fallback. */
|
|
163
|
+
const DEFAULT_STREAM_SETTINGS = { thinkAutoExpand: true };
|
|
164
|
+
//#endregion
|
|
44
165
|
//#region src/plugin.ts
|
|
45
166
|
/** Display name shown by the Host loader while the plugin is mounted. */
|
|
46
167
|
const name = "dsh-smooth-stream";
|
|
@@ -56,6 +177,11 @@ const Config = Schema.object({
|
|
|
56
177
|
maxScrollSpeedPxPerSec: Schema.number().min(1).max(2e3).default(DEFAULT_STREAM_CONFIG.maxScrollSpeedPxPerSec)
|
|
57
178
|
});
|
|
58
179
|
/**
|
|
180
|
+
* Schema of the user-owned settings section. The Host keeps it in the durable
|
|
181
|
+
* settings provider while the browser edits it through the plugin RPC below.
|
|
182
|
+
*/
|
|
183
|
+
const StreamSettingsSchema = Schema.object({ thinkAutoExpand: Schema.boolean().default(DEFAULT_STREAM_SETTINGS.thinkAutoExpand) });
|
|
184
|
+
/**
|
|
59
185
|
* Host half: log the resolved configuration and bridge it to the browser
|
|
60
186
|
* half. The web boot graph carries no per-entry config, so the validated
|
|
61
187
|
* value is injected into every served index response as a boot global the
|
|
@@ -68,6 +194,109 @@ function apply(ctx, config) {
|
|
|
68
194
|
ctx.inject(["webServer"], (httpCtx) => {
|
|
69
195
|
httpCtx.effect(() => httpCtx.webServer.tapIndex((html) => injectStreamConfig(html, config)), "dsh-smooth-stream: boot config bridge");
|
|
70
196
|
});
|
|
197
|
+
ctx.inject(["settings"], (settingsCtx) => {
|
|
198
|
+
const scope = settingsCtx.settings.register(settingsNamespace(STREAM_SETTINGS_NS), StreamSettingsSchema, { applies: "live" });
|
|
199
|
+
settingsCtx.inject(["connection"], (connectionCtx) => {
|
|
200
|
+
let upgrade;
|
|
201
|
+
const view = () => {
|
|
202
|
+
const installation = inspectProfileInstallation(connectionCtx.baseUrl, STREAM_PACKAGE_NAME);
|
|
203
|
+
return {
|
|
204
|
+
version: STREAM_PACKAGE_VERSION,
|
|
205
|
+
installation: installation.kind,
|
|
206
|
+
writable: connectionCtx.settings.writable,
|
|
207
|
+
thinkAutoExpand: scope.get().thinkAutoExpand,
|
|
208
|
+
canUpgrade: installation.kind === "npm"
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
const handle = async (endpoint, payload) => {
|
|
212
|
+
if (endpoint === STREAM_SETTINGS_RPC.read) return {
|
|
213
|
+
ok: true,
|
|
214
|
+
value: view()
|
|
215
|
+
};
|
|
216
|
+
if (endpoint === STREAM_SETTINGS_RPC.write) {
|
|
217
|
+
if (typeof payload !== "object" || payload === null || typeof payload.thinkAutoExpand !== "boolean") return {
|
|
218
|
+
ok: false,
|
|
219
|
+
error: {
|
|
220
|
+
code: "settings-rejected",
|
|
221
|
+
message: "thinkAutoExpand must be a boolean",
|
|
222
|
+
details: { ns: STREAM_SETTINGS_NS }
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
if (!connectionCtx.settings.writable) return {
|
|
226
|
+
ok: false,
|
|
227
|
+
error: {
|
|
228
|
+
code: "settings-rejected",
|
|
229
|
+
message: "smooth-stream settings are read-only",
|
|
230
|
+
details: { ns: STREAM_SETTINGS_NS }
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
try {
|
|
234
|
+
await scope.update({ thinkAutoExpand: payload.thinkAutoExpand });
|
|
235
|
+
} catch {
|
|
236
|
+
return {
|
|
237
|
+
ok: false,
|
|
238
|
+
error: {
|
|
239
|
+
code: "settings-rejected",
|
|
240
|
+
message: "smooth-stream settings update failed",
|
|
241
|
+
details: { ns: STREAM_SETTINGS_NS }
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
ok: true,
|
|
247
|
+
value: view()
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (endpoint === STREAM_SETTINGS_RPC.upgrade) {
|
|
251
|
+
const installation = inspectProfileInstallation(connectionCtx.baseUrl, STREAM_PACKAGE_NAME);
|
|
252
|
+
if (installation.kind !== "npm") return {
|
|
253
|
+
ok: false,
|
|
254
|
+
error: {
|
|
255
|
+
code: "internal",
|
|
256
|
+
message: "smooth-stream is not an npm profile dependency",
|
|
257
|
+
details: {}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
if (upgrade !== void 0) return {
|
|
261
|
+
ok: false,
|
|
262
|
+
error: {
|
|
263
|
+
code: "internal",
|
|
264
|
+
message: "smooth-stream update is already running",
|
|
265
|
+
details: {}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
upgrade = updateNpmProfilePackage(installation.profileDir, STREAM_PACKAGE_NAME);
|
|
269
|
+
try {
|
|
270
|
+
await upgrade;
|
|
271
|
+
} catch {
|
|
272
|
+
return {
|
|
273
|
+
ok: false,
|
|
274
|
+
error: {
|
|
275
|
+
code: "internal",
|
|
276
|
+
message: "smooth-stream update failed",
|
|
277
|
+
details: {}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
} finally {
|
|
281
|
+
upgrade = void 0;
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
ok: true,
|
|
285
|
+
value: { restartRequired: true }
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
ok: false,
|
|
290
|
+
error: {
|
|
291
|
+
code: "internal",
|
|
292
|
+
message: `unknown smooth-stream endpoint ${JSON.stringify(endpoint)}`,
|
|
293
|
+
details: {}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
};
|
|
297
|
+
connectionCtx.effect(() => connectionCtx.connection.rpc.handle(STREAM_SETTINGS_RPC_CHANNEL, handle, { authority: "loopback" }), "dsh-smooth-stream: settings RPC");
|
|
298
|
+
});
|
|
299
|
+
});
|
|
71
300
|
}
|
|
72
301
|
//#endregion
|
|
73
302
|
export { Config, apply, name };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-smooth-stream",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "DeepSeek Harness (dsh) plugin: silky streaming reveal, no flicker. dsh 丝滑流式渲染插件。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"src/**/*.css",
|
|
54
54
|
"cordis.patch.yml",
|
|
55
55
|
"README.md",
|
|
56
|
-
"README.
|
|
56
|
+
"README.en.md",
|
|
57
57
|
"LICENSE"
|
|
58
58
|
],
|
|
59
59
|
"dsh": {
|
|
@@ -65,7 +65,10 @@
|
|
|
65
65
|
"inject": [
|
|
66
66
|
"@deepseek-ai/dsh-client-runtime",
|
|
67
67
|
"@deepseek-ai/dsh-client-ui-conversation",
|
|
68
|
-
"@deepseek-ai/dsh-client-locale"
|
|
68
|
+
"@deepseek-ai/dsh-client-locale",
|
|
69
|
+
"@deepseek-ai/dsh-client-connection",
|
|
70
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
71
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins"
|
|
69
72
|
]
|
|
70
73
|
}
|
|
71
74
|
},
|
|
@@ -75,13 +78,19 @@
|
|
|
75
78
|
"test": "vitest run",
|
|
76
79
|
"typecheck": "tsc -p tsconfig.json --pretty false"
|
|
77
80
|
},
|
|
81
|
+
"dependencies": {
|
|
82
|
+
"@deepseek-ai/dsh-settings": "0.1.0-rc.6"
|
|
83
|
+
},
|
|
78
84
|
"peerDependencies": {
|
|
79
85
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
80
86
|
"@deepseek-ai/dsh-client-locale": "*",
|
|
81
87
|
"@deepseek-ai/dsh-client-runtime": "*",
|
|
82
88
|
"@deepseek-ai/dsh-client-ui-attachment": "*",
|
|
89
|
+
"@deepseek-ai/dsh-client-connection": "*",
|
|
83
90
|
"@deepseek-ai/dsh-client-ui-conversation": "*",
|
|
84
91
|
"@deepseek-ai/dsh-client-ui-primitives": "*",
|
|
92
|
+
"@deepseek-ai/dsh-client-ui-settings": "*",
|
|
93
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "*",
|
|
85
94
|
"@deepseek-ai/dsh-client-ui-slots": "*",
|
|
86
95
|
"@deepseek-ai/dsh-host-webserver": "*",
|
|
87
96
|
"@deepseek-ai/schemastery": "*",
|
|
@@ -97,12 +106,21 @@
|
|
|
97
106
|
"@deepseek-ai/dsh-client-ui-attachment": {
|
|
98
107
|
"optional": true
|
|
99
108
|
},
|
|
109
|
+
"@deepseek-ai/dsh-client-connection": {
|
|
110
|
+
"optional": true
|
|
111
|
+
},
|
|
100
112
|
"@deepseek-ai/dsh-client-ui-conversation": {
|
|
101
113
|
"optional": true
|
|
102
114
|
},
|
|
103
115
|
"@deepseek-ai/dsh-client-ui-primitives": {
|
|
104
116
|
"optional": true
|
|
105
117
|
},
|
|
118
|
+
"@deepseek-ai/dsh-client-ui-settings": {
|
|
119
|
+
"optional": true
|
|
120
|
+
},
|
|
121
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": {
|
|
122
|
+
"optional": true
|
|
123
|
+
},
|
|
106
124
|
"@deepseek-ai/dsh-client-ui-slots": {
|
|
107
125
|
"optional": true
|
|
108
126
|
},
|
|
@@ -115,10 +133,14 @@
|
|
|
115
133
|
"@deepseek-ai/dsh-client-locale": "0.1.0-rc.6",
|
|
116
134
|
"@deepseek-ai/dsh-client-runtime": "0.1.0-rc.6",
|
|
117
135
|
"@deepseek-ai/dsh-client-ui-attachment": "0.1.0-rc.6",
|
|
136
|
+
"@deepseek-ai/dsh-client-connection": "0.1.0-rc.6",
|
|
118
137
|
"@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.6",
|
|
119
138
|
"@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.6",
|
|
139
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.6",
|
|
140
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.0-rc.6",
|
|
120
141
|
"@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6",
|
|
121
142
|
"@deepseek-ai/dsh-host-webserver": "0.1.0-rc.6",
|
|
143
|
+
"@deepseek-ai/dsh-settings": "0.1.0-rc.6",
|
|
122
144
|
"@deepseek-ai/schemastery": "3.18.1",
|
|
123
145
|
"@testing-library/react": "^16.3.0",
|
|
124
146
|
"@types/node": "^22.20.0",
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/* Mirrors the shipped PluginCard chrome without importing an internal module
|
|
2
|
+
from another client package. */
|
|
3
|
+
|
|
4
|
+
.card {
|
|
5
|
+
list-style: none;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
9
|
+
border-radius: 12px;
|
|
10
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
11
|
+
transition: border-color .16s, background .16s;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.card:hover {
|
|
15
|
+
border-color: var(--dsw-alias-label-dimmed);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.cardOpen {
|
|
19
|
+
border-color: var(--dsw-alias-label-dimmed);
|
|
20
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.header {
|
|
24
|
+
appearance: none;
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
gap: 12px;
|
|
28
|
+
width: 100%;
|
|
29
|
+
padding: 14px 16px;
|
|
30
|
+
border: 0;
|
|
31
|
+
border-radius: 12px;
|
|
32
|
+
background: none;
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
text-align: left;
|
|
35
|
+
font: inherit;
|
|
36
|
+
color: inherit;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.header:focus-visible {
|
|
40
|
+
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
41
|
+
outline-offset: -2px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.headText {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
gap: 4px;
|
|
48
|
+
min-width: 0;
|
|
49
|
+
flex: 1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.name {
|
|
53
|
+
font-weight: 600;
|
|
54
|
+
font-size: 15px;
|
|
55
|
+
line-height: 1.4;
|
|
56
|
+
color: var(--dsw-alias-label-primary);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.description {
|
|
60
|
+
font-size: 13px;
|
|
61
|
+
line-height: 1.5;
|
|
62
|
+
color: var(--dsw-alias-label-tertiary);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.version {
|
|
66
|
+
flex: none;
|
|
67
|
+
max-width: 16rem;
|
|
68
|
+
overflow: hidden;
|
|
69
|
+
text-overflow: ellipsis;
|
|
70
|
+
white-space: nowrap;
|
|
71
|
+
font-size: 12px;
|
|
72
|
+
line-height: 1.5;
|
|
73
|
+
color: var(--dsw-alias-label-tertiary);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.pending {
|
|
77
|
+
flex: none;
|
|
78
|
+
padding: 1px 8px;
|
|
79
|
+
border-radius: 999px;
|
|
80
|
+
background: var(--dsw-alias-bg-module-platform);
|
|
81
|
+
color: var(--dsw-alias-label-secondary);
|
|
82
|
+
font-size: 11px;
|
|
83
|
+
font-weight: 500;
|
|
84
|
+
line-height: 17px;
|
|
85
|
+
white-space: nowrap;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.chevron {
|
|
89
|
+
flex: none;
|
|
90
|
+
color: var(--dsw-alias-label-tertiary);
|
|
91
|
+
transition: transform .16s;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.chevronOpen {
|
|
95
|
+
transform: rotate(180deg);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.body {
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: column;
|
|
101
|
+
margin: 0 16px;
|
|
102
|
+
padding-bottom: 8px;
|
|
103
|
+
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.readOnly {
|
|
107
|
+
margin: 12px 0 0;
|
|
108
|
+
font-size: 12px;
|
|
109
|
+
line-height: 1.5;
|
|
110
|
+
color: var(--dsw-alias-label-tertiary);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.field {
|
|
114
|
+
display: flex;
|
|
115
|
+
flex-direction: column;
|
|
116
|
+
gap: 6px;
|
|
117
|
+
padding: 12px 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.fieldHead {
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
justify-content: space-between;
|
|
124
|
+
gap: 8px;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.label {
|
|
128
|
+
flex: 1;
|
|
129
|
+
min-width: 0;
|
|
130
|
+
font-size: 13px;
|
|
131
|
+
font-weight: 500;
|
|
132
|
+
line-height: 1.5;
|
|
133
|
+
color: var(--dsw-alias-label-primary);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.toggle {
|
|
137
|
+
width: 16px;
|
|
138
|
+
height: 16px;
|
|
139
|
+
accent-color: var(--dsw-alias-brand-primary);
|
|
140
|
+
cursor: pointer;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.toggle:disabled {
|
|
144
|
+
cursor: default;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.toggle:focus-visible {
|
|
148
|
+
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
149
|
+
outline-offset: 2px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.hint {
|
|
153
|
+
margin: 0;
|
|
154
|
+
font-size: 12px;
|
|
155
|
+
line-height: 1.5;
|
|
156
|
+
color: var(--dsw-alias-label-tertiary);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.failure {
|
|
160
|
+
display: flex;
|
|
161
|
+
align-items: center;
|
|
162
|
+
justify-content: space-between;
|
|
163
|
+
gap: 12px;
|
|
164
|
+
padding: 12px 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.updateRow {
|
|
168
|
+
display: flex;
|
|
169
|
+
align-items: center;
|
|
170
|
+
justify-content: space-between;
|
|
171
|
+
gap: 12px;
|
|
172
|
+
padding: 12px 0;
|
|
173
|
+
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.updateCopy {
|
|
177
|
+
display: flex;
|
|
178
|
+
flex-direction: column;
|
|
179
|
+
min-width: 0;
|
|
180
|
+
gap: 4px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.footer {
|
|
184
|
+
display: flex;
|
|
185
|
+
align-items: center;
|
|
186
|
+
justify-content: flex-end;
|
|
187
|
+
gap: 8px;
|
|
188
|
+
padding: 12px 0 4px;
|
|
189
|
+
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.failed {
|
|
193
|
+
flex: 1;
|
|
194
|
+
min-width: 0;
|
|
195
|
+
margin: 0;
|
|
196
|
+
font-size: 12px;
|
|
197
|
+
line-height: 1.5;
|
|
198
|
+
color: var(--dsw-alias-label-error);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.save,
|
|
202
|
+
.discard,
|
|
203
|
+
.update {
|
|
204
|
+
appearance: none;
|
|
205
|
+
border: 1px solid transparent;
|
|
206
|
+
border-radius: 8px;
|
|
207
|
+
font: inherit;
|
|
208
|
+
font-size: 13px;
|
|
209
|
+
line-height: 1.5;
|
|
210
|
+
padding: 5px 14px;
|
|
211
|
+
cursor: pointer;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.save {
|
|
215
|
+
background: var(--dsw-alias-label-primary);
|
|
216
|
+
color: var(--dsw-alias-bg-layer-3);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.discard {
|
|
220
|
+
border-color: var(--dsw-alias-border-l2);
|
|
221
|
+
background: none;
|
|
222
|
+
color: var(--dsw-alias-label-secondary);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.discard:hover:not(:disabled),
|
|
226
|
+
.update:hover:not(:disabled) {
|
|
227
|
+
border-color: var(--dsw-alias-label-dimmed);
|
|
228
|
+
color: var(--dsw-alias-label-primary);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.update {
|
|
232
|
+
display: inline-flex;
|
|
233
|
+
align-items: center;
|
|
234
|
+
flex: none;
|
|
235
|
+
gap: 6px;
|
|
236
|
+
border-color: var(--dsw-alias-border-l2);
|
|
237
|
+
background: none;
|
|
238
|
+
color: var(--dsw-alias-label-secondary);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.discard:disabled,
|
|
242
|
+
.save:disabled,
|
|
243
|
+
.update:disabled {
|
|
244
|
+
opacity: 0.4;
|
|
245
|
+
cursor: default;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.discard:focus-visible,
|
|
249
|
+
.save:focus-visible,
|
|
250
|
+
.update:focus-visible {
|
|
251
|
+
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
252
|
+
outline-offset: 1px;
|
|
253
|
+
}
|