dsh-wsl-tool 1.10.3 → 1.10.5
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 +52 -20
- package/lib/config.js +26 -6
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -191,7 +191,12 @@ window.__ModuleLoader__.load({
|
|
|
191
191
|
if (cursor === null || typeof cursor !== 'object') return undefined
|
|
192
192
|
cursor = cursor[step]
|
|
193
193
|
}
|
|
194
|
-
|
|
194
|
+
// A `.volatile()` field can arrive as a cosmokit cell rather than its value;
|
|
195
|
+
// the platform's own form model hands out plain values, but unwrapping here
|
|
196
|
+
// costs nothing and keeps a switch from rendering a cell object as "on".
|
|
197
|
+
return cursor !== null && typeof cursor === 'object' && typeof cursor.get === 'function'
|
|
198
|
+
? cursor.get()
|
|
199
|
+
: cursor
|
|
195
200
|
}
|
|
196
201
|
|
|
197
202
|
/**
|
|
@@ -301,12 +306,17 @@ window.__ModuleLoader__.load({
|
|
|
301
306
|
* a refusal, so this panel does the same — `false` means "not saved", and
|
|
302
307
|
* the panel words it instead of pretending the switch moved.
|
|
303
308
|
*/
|
|
304
|
-
write: async (
|
|
309
|
+
write: async (ops) => {
|
|
305
310
|
const snapshot = read()
|
|
306
311
|
if (scope === undefined || typeof scope.mutate !== 'function') return false
|
|
307
312
|
if (snapshot.writable !== true) return false
|
|
313
|
+
// One op or an ordered batch: the service applies a batch in one
|
|
314
|
+
// revision-fenced write, which is what the panel's single "restore every
|
|
315
|
+
// default" control uses.
|
|
316
|
+
const batch = Array.isArray(ops) ? ops : [ops]
|
|
317
|
+
if (batch.length === 0) return true
|
|
308
318
|
try {
|
|
309
|
-
const landed = await scope.mutate(
|
|
319
|
+
const landed = await scope.mutate(batch, snapshot.revision)
|
|
310
320
|
return Boolean(landed)
|
|
311
321
|
} catch (error) {
|
|
312
322
|
console.error('dsh-wsl-tool: writing the configuration failed', error)
|
|
@@ -355,6 +365,14 @@ window.__ModuleLoader__.load({
|
|
|
355
365
|
const ROW_COPY_STYLE = { display: 'flex', flexDirection: 'column', gap: '2px', minWidth: 0 }
|
|
356
366
|
const ROW_LABEL_STYLE = { display: 'flex', alignItems: 'center', gap: '6px', fontWeight: 500 }
|
|
357
367
|
const ROW_ACTIONS_STYLE = { display: 'flex', alignItems: 'center', gap: '8px', flexShrink: 0 }
|
|
368
|
+
/** The panel's single override bar; `rowGap` keeps it readable above the rows. */
|
|
369
|
+
const OVERRIDE_BAR_STYLE = {
|
|
370
|
+
display: 'flex',
|
|
371
|
+
alignItems: 'center',
|
|
372
|
+
gap: '8px',
|
|
373
|
+
justifyContent: 'flex-end',
|
|
374
|
+
padding: '6px 0',
|
|
375
|
+
}
|
|
358
376
|
const HINT_STYLE = { margin: 0, color: LABEL_TERTIARY, fontSize: '12px', lineHeight: '18px', maxWidth: '66ch' }
|
|
359
377
|
const BADGE_STYLE = {
|
|
360
378
|
border: '0.5px solid ' + BORDER_BADGE,
|
|
@@ -492,8 +510,9 @@ window.__ModuleLoader__.load({
|
|
|
492
510
|
})
|
|
493
511
|
}
|
|
494
512
|
|
|
495
|
-
/** One switch row:
|
|
496
|
-
*
|
|
513
|
+
/** One switch row: copy on the left, an override badge and the switch on the
|
|
514
|
+
* right. Clearing overrides is NOT offered per row — the panel has one
|
|
515
|
+
* control for that, so eleven rows do not repeat the same button. */
|
|
497
516
|
const rowElement = (row) => {
|
|
498
517
|
const key = row.path.join('.')
|
|
499
518
|
const overridden = hasPath(snapshot.user, row.path)
|
|
@@ -518,21 +537,6 @@ window.__ModuleLoader__.load({
|
|
|
518
537
|
'div',
|
|
519
538
|
{ style: ROW_ACTIONS_STYLE },
|
|
520
539
|
overridden ? React.createElement('span', { style: BADGE_STYLE }, '已覆盖') : null,
|
|
521
|
-
overridden
|
|
522
|
-
? React.createElement(
|
|
523
|
-
'button',
|
|
524
|
-
{
|
|
525
|
-
type: 'button',
|
|
526
|
-
style: RESET_STYLE,
|
|
527
|
-
disabled,
|
|
528
|
-
// Returned as well as performed: React ignores a handler's
|
|
529
|
-
// return value, but awaiting it is how a test sees the write
|
|
530
|
-
// settle.
|
|
531
|
-
onClick: () => submit({ op: 'unset', path: row.path }, key),
|
|
532
|
-
},
|
|
533
|
-
'恢复默认',
|
|
534
|
-
)
|
|
535
|
-
: null,
|
|
536
540
|
React.createElement(primitives.Switch, {
|
|
537
541
|
checked,
|
|
538
542
|
label: row.label,
|
|
@@ -575,9 +579,37 @@ window.__ModuleLoader__.load({
|
|
|
575
579
|
React.createElement('p', { key: 'note-unavailable', style: NOTE_STYLE }, '此 DSH 未提供该插件的配置作用域'),
|
|
576
580
|
)
|
|
577
581
|
} else {
|
|
582
|
+
const overriddenPaths = SECTIONS
|
|
583
|
+
.flatMap((group) => group.rows)
|
|
584
|
+
.filter((row) => hasPath(snapshot.user, row.path))
|
|
585
|
+
.map((row) => row.path)
|
|
578
586
|
const blocks = SECTIONS.map((group) =>
|
|
579
587
|
section(group.id, group.title, React.createElement('div', { style: ROWS_STYLE }, group.rows.map(rowElement))),
|
|
580
588
|
)
|
|
589
|
+
// ONE place to clear every override, instead of the same button repeated on
|
|
590
|
+
// every row. "Put this panel back the way the composition had it" is one
|
|
591
|
+
// intention, and the settings service takes it as one revision-fenced write.
|
|
592
|
+
if (overriddenPaths.length > 0) {
|
|
593
|
+
blocks.unshift(
|
|
594
|
+
React.createElement(
|
|
595
|
+
'div',
|
|
596
|
+
{ key: 'overrides', style: OVERRIDE_BAR_STYLE },
|
|
597
|
+
React.createElement('span', { style: BADGE_STYLE }, '已覆盖 ' + overriddenPaths.length + ' 项'),
|
|
598
|
+
React.createElement(
|
|
599
|
+
'button',
|
|
600
|
+
{
|
|
601
|
+
type: 'button',
|
|
602
|
+
style: RESET_STYLE,
|
|
603
|
+
disabled: snapshot.writable !== true || busy !== undefined,
|
|
604
|
+
// Returned as well as performed: React ignores a handler's return
|
|
605
|
+
// value, but awaiting it is how a test sees the write settle.
|
|
606
|
+
onClick: () => submit(overriddenPaths.map((path) => ({ op: 'unset', path })), 'all-overrides'),
|
|
607
|
+
},
|
|
608
|
+
'全部恢复默认',
|
|
609
|
+
),
|
|
610
|
+
),
|
|
611
|
+
)
|
|
612
|
+
}
|
|
581
613
|
if (refused) {
|
|
582
614
|
blocks.unshift(
|
|
583
615
|
React.createElement('p', { key: 'note-refused', style: NOTE_STYLE }, '保存被拒绝:配置没有被写入,请重试'),
|
package/lib/config.js
CHANGED
|
@@ -92,7 +92,25 @@ function envInt(env, name, fallback, min, max) {
|
|
|
92
92
|
|
|
93
93
|
/** A configured boolean, or the fallback when the layer did not set one. */
|
|
94
94
|
function setting(value, fallback) {
|
|
95
|
-
|
|
95
|
+
const raw = unbox(value)
|
|
96
|
+
return typeof raw === 'boolean' ? raw : fallback
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Read one configuration value, unwrapping a volatile cell.
|
|
101
|
+
*
|
|
102
|
+
* A `.volatile()` schema field does NOT resolve to its value: schemastery wraps it
|
|
103
|
+
* with cosmokit's `createVolatile(default)`, which is why the shipped plugins read
|
|
104
|
+
* theirs as `config.timeoutMs.get()` (`dsh-bash-local`). The cell is written in
|
|
105
|
+
* place when the user edits the field, and that in-place write is exactly what makes
|
|
106
|
+
* an edit apply without remounting the plugin — so reading through `.get()` is both
|
|
107
|
+
* required for correctness and how live updates arrive. A plain value (an older
|
|
108
|
+
* host, or a hand-built settings object in a test) passes through untouched.
|
|
109
|
+
*/
|
|
110
|
+
function unbox(value) {
|
|
111
|
+
return value !== null && typeof value === 'object' && typeof value.get === 'function'
|
|
112
|
+
? value.get()
|
|
113
|
+
: value
|
|
96
114
|
}
|
|
97
115
|
|
|
98
116
|
/**
|
|
@@ -101,7 +119,8 @@ function setting(value, fallback) {
|
|
|
101
119
|
* @returns a distribution name to pin, or null to use the system default.
|
|
102
120
|
*/
|
|
103
121
|
function resolveDistro(env, configured) {
|
|
104
|
-
|
|
122
|
+
const distro = unbox(configured)
|
|
123
|
+
if (typeof distro === 'string' && distro.trim() !== '') return distro.trim()
|
|
105
124
|
const raw = env.DSH_WSL_DISTRO
|
|
106
125
|
return typeof raw === 'string' && raw.trim() !== '' ? raw.trim() : null
|
|
107
126
|
}
|
|
@@ -121,7 +140,7 @@ function resolveWorkdir(env, settings) {
|
|
|
121
140
|
// Only the ON position is authoritative: off means "whatever the layers below
|
|
122
141
|
// say", so `DSH_WSL_WORKDIR=session` keeps working for a user who never opened
|
|
123
142
|
// the panel, and a fresh install still starts in `~`.
|
|
124
|
-
if (settings.startInSessionWorkspace === true) return null
|
|
143
|
+
if (unbox(settings.startInSessionWorkspace) === true) return null
|
|
125
144
|
const raw = env.DSH_WSL_WORKDIR
|
|
126
145
|
if (typeof raw !== 'string' || raw.trim() === '') return DEFAULTS.workdir
|
|
127
146
|
const value = raw.trim()
|
|
@@ -148,11 +167,12 @@ export function resolveConfig(env = process.env, settings = {}) {
|
|
|
148
167
|
const maxCommandTimeoutMs = envInt(
|
|
149
168
|
env, 'DSH_WSL_MAX_TIMEOUT_MS', DEFAULTS.maxCommandTimeoutMs, TIMEOUT_MS_MIN, DEFAULTS.maxTimerDelayMs,
|
|
150
169
|
)
|
|
151
|
-
const tools = settings.tools ?? {}
|
|
170
|
+
const tools = unbox(settings.tools) ?? {}
|
|
152
171
|
// 0 means "not configured", so the environment and the built-in default still
|
|
153
172
|
// decide; a positive value is the user's own deadline.
|
|
154
|
-
const
|
|
155
|
-
|
|
173
|
+
const configuredTimeout = unbox(settings.timeoutMs)
|
|
174
|
+
const configuredTimeoutMs = Number.isInteger(configuredTimeout) && configuredTimeout > 0
|
|
175
|
+
? configuredTimeout
|
|
156
176
|
: undefined
|
|
157
177
|
return {
|
|
158
178
|
...DEFAULTS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-wsl-tool",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.5",
|
|
4
4
|
"description": "Run Linux commands from Windows through WSL, essentially matching a native Linux DSH for command execution: background jobs, stdin, path translation, a destructive-command guard and a WSL capability report. A left-sidebar panel switches each capability on or off, each with a one-line explanation, and an opt-in patch gives the desktop app's sidebar terminal a WSL shell. WSL calls run below the DSH sandbox, and a project on a Windows drive keeps Windows filesystem semantics.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|