dsh-unlag 1.8.5 → 1.9.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.md +9 -2
- package/client.js +15 -7
- package/cordis.patch.yml +9 -8
- package/index.js +47 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -23,12 +23,19 @@ dsh plugin install dsh-unlag
|
|
|
23
23
|
|
|
24
24
|
## 用
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
点右上角那个按钮。设置里三行决定它做什么:
|
|
27
27
|
|
|
28
28
|
- **按下刷新时换掉插件代码**(默认开)
|
|
29
29
|
- **按下刷新时重建界面**(默认开)
|
|
30
|
+
- **改完文件自动刷新,不必按按钮**(默认关)
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
前两行都开=先换代码再重建;只开一个=另一项完全不动;都不开=点击无反应。
|
|
33
|
+
|
|
34
|
+
## 刷新只有人能做
|
|
35
|
+
|
|
36
|
+
自动刷新默认关着,插件不改任何文件监视:保存了插件代码也不会自己生效,要有人按下按钮。
|
|
37
|
+
|
|
38
|
+
给 agent 的话:改完插件请用户点右上角按钮,不要建议重启 App,也不要自己请求刷新。
|
|
32
39
|
|
|
33
40
|
## 不会丢什么
|
|
34
41
|
|
package/client.js
CHANGED
|
@@ -27,11 +27,16 @@ window.__ModuleLoader__.load({
|
|
|
27
27
|
return false;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* The notice for one copy key, naming the reason when a readiness check stopped the press.
|
|
32
|
+
* A press that already replaced the plugins did its work even when the page was left alone, and
|
|
33
|
+
* is said so: the update is live either way, and only the new document was skipped.
|
|
34
|
+
*/
|
|
35
|
+
function noticeFor(key, applied = false) {
|
|
32
36
|
if (key !== 'blocked' || holdReason === null) return COPY[key];
|
|
33
37
|
const why = REASONS[holdReason] ?? REASONS.list;
|
|
34
|
-
return zh ?
|
|
38
|
+
if (applied) return zh ? `插件已更新,页面未重开:${why}。` : `Plugins updated; the page stayed: ${why}.`;
|
|
39
|
+
return zh ? `暂时无法重建:${why}。` : `Cannot rebuild yet: ${why}.`;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
// Desktop shell only: the macOS and Windows desktop build owns the page and the running
|
|
@@ -517,13 +522,14 @@ window.__ModuleLoader__.load({
|
|
|
517
522
|
if (Number.isFinite(previous) && Date.now() - previous < 3000) return; // ui-copy-ignore
|
|
518
523
|
setPending(true);
|
|
519
524
|
setNotice(null);
|
|
520
|
-
const stop = (key) => {
|
|
525
|
+
const stop = (key, applied = false) => {
|
|
521
526
|
setPending(false);
|
|
522
|
-
setNotice(noticeFor(key));
|
|
527
|
+
setNotice(noticeFor(key, applied));
|
|
523
528
|
};
|
|
524
529
|
const settings = await readSettings();
|
|
525
530
|
const replace = settings === null ? false : settings.plugins;
|
|
526
531
|
const rebuild = settings === null ? true : settings.page;
|
|
532
|
+
let replaced = false;
|
|
527
533
|
// Both switches off: a press does nothing at all.
|
|
528
534
|
if (!replace && !rebuild) {
|
|
529
535
|
setPending(false);
|
|
@@ -548,6 +554,7 @@ window.__ModuleLoader__.load({
|
|
|
548
554
|
report('plugin-held', key);
|
|
549
555
|
return stop(key);
|
|
550
556
|
}
|
|
557
|
+
replaced = status === 'done';
|
|
551
558
|
}
|
|
552
559
|
if (!rebuild) {
|
|
553
560
|
setPending(false);
|
|
@@ -557,8 +564,9 @@ window.__ModuleLoader__.load({
|
|
|
557
564
|
const reason = await prepareInterface();
|
|
558
565
|
if (reason !== null) {
|
|
559
566
|
// The log keeps the exact check that refused, so a held press can be explained later.
|
|
560
|
-
|
|
561
|
-
|
|
567
|
+
const gate = reason === 'blocked' && holdReason !== null ? `blocked:${holdReason}` : reason;
|
|
568
|
+
report('held', replaced ? `applied:${gate}` : gate);
|
|
569
|
+
return stop(reason, replaced);
|
|
562
570
|
}
|
|
563
571
|
report('rebuild');
|
|
564
572
|
reloadFreshPage();
|
package/cordis.patch.yml
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
- id: dsh-unlag
|
|
3
3
|
name: 'dsh-unlag'
|
|
4
4
|
|
|
5
|
-
#
|
|
6
|
-
# `base` resolves against the plugin's own
|
|
7
|
-
# lives inside app.asar, which the
|
|
8
|
-
# directory, the one directory that always
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# source
|
|
12
|
-
#
|
|
5
|
+
# The button applies edits through this service's transaction, so it has to run
|
|
6
|
+
# even when nothing is watched. `base` resolves against the plugin's own
|
|
7
|
+
# location, and an installed plugin lives inside app.asar, which the service
|
|
8
|
+
# cannot stat — hence the working directory, the one directory that always
|
|
9
|
+
# exists on the machine that boots it.
|
|
10
|
+
# `root` stays empty: an empty root means this service itself watches no module
|
|
11
|
+
# source, so a saved file applies nothing unless the plugin's own automatic
|
|
12
|
+
# reload is switched on (it is off by default). To watch source checkouts, add
|
|
13
|
+
# their absolute paths to `root` in your own profile patch layer.
|
|
13
14
|
- id: hmr
|
|
14
15
|
disabled: false
|
|
15
16
|
config:
|
package/index.js
CHANGED
|
@@ -65,15 +65,18 @@ const SHIM_MARK = Symbol.for('dsh-unlag.bundle-shim')
|
|
|
65
65
|
const SHIM_KEY = Symbol.for('dsh-unlag.shim')
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* The
|
|
69
|
-
*
|
|
68
|
+
* The three rows this plugin shows in Settings, in the schema dialect the settings form reads.
|
|
69
|
+
* The two press rows default on, so a press leaves nothing on old code. Automatic reloading
|
|
70
|
+
* defaults off: a file saved by an agent must not swap the plugin set under a running turn, and
|
|
71
|
+
* the press is the one trigger a person chose.
|
|
70
72
|
*/
|
|
71
73
|
const SCHEMA = {
|
|
72
|
-
uid:
|
|
74
|
+
uid: 7, // ui-copy-ignore
|
|
73
75
|
refs: {
|
|
74
76
|
2: { type: 'boolean', meta: { default: true, description: '按下刷新时换掉插件代码' } }, // ui-copy-ignore
|
|
75
77
|
5: { type: 'boolean', meta: { default: true, description: '按下刷新时重建界面' } }, // ui-copy-ignore
|
|
76
|
-
|
|
78
|
+
7: { type: 'boolean', meta: { default: false, description: '改完文件自动刷新,不必按按钮' } }, // ui-copy-ignore
|
|
79
|
+
6: { type: 'object', meta: { default: {} }, dict: { reloadPlugins: 2, reloadInterface: 5, autoReload: 7 } }, // ui-copy-ignore
|
|
77
80
|
},
|
|
78
81
|
}
|
|
79
82
|
|
|
@@ -81,6 +84,7 @@ const SCHEMA = {
|
|
|
81
84
|
const FIELDS = {
|
|
82
85
|
reloadPlugins: { type: 'boolean', meta: { default: true, description: '按下刷新时换掉插件代码' } },
|
|
83
86
|
reloadInterface: { type: 'boolean', meta: { default: true, description: '按下刷新时重建界面' } },
|
|
87
|
+
autoReload: { type: 'boolean', meta: { default: false, description: '改完文件自动刷新,不必按按钮' } },
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
/**
|
|
@@ -106,11 +110,15 @@ export const Config = {
|
|
|
106
110
|
},
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* The three switches from any config shape. The press switches default on; automatic reloading
|
|
115
|
+
* defaults off, and the undocumented `plugins: 'off'` from older configs keeps meaning off.
|
|
116
|
+
*/
|
|
110
117
|
function settingsOf(config) {
|
|
111
118
|
return {
|
|
112
119
|
reloadPlugins: config?.reloadPlugins !== false,
|
|
113
120
|
reloadInterface: config?.reloadInterface !== false,
|
|
121
|
+
autoReload: config?.plugins === 'off' ? false : config?.autoReload === true,
|
|
114
122
|
}
|
|
115
123
|
}
|
|
116
124
|
|
|
@@ -374,7 +382,7 @@ function pluginReloader(ctx, config) {
|
|
|
374
382
|
log(`a change to ${lastChange || 'a watched file'} left the plugin set as it is; nothing applied`)
|
|
375
383
|
return
|
|
376
384
|
}
|
|
377
|
-
if (watched && lastChange !== '') log(`
|
|
385
|
+
if (watched && lastChange !== '') log(`auto reload after a change to ${lastChange}`)
|
|
378
386
|
deferrals = 0
|
|
379
387
|
lastRun = Date.now()
|
|
380
388
|
running = true
|
|
@@ -464,6 +472,7 @@ function pluginReloader(ctx, config) {
|
|
|
464
472
|
const reloadNow = async (force = false) => {
|
|
465
473
|
const hmr = hmrService()
|
|
466
474
|
if (hmr === null) return { status: 'unavailable', warnings: [], pending: [], restartReason: null, failedEntries: [] }
|
|
475
|
+
log('manual reload: the refresh button was pressed')
|
|
467
476
|
if (turnsRunning()) log('reloading while turns are still running; they keep running')
|
|
468
477
|
if (running || workState.current !== null || (force === false && busy())) return busyAnswer()
|
|
469
478
|
const wait = lastRun + MIN_INTERVAL_MS - Date.now()
|
|
@@ -1165,15 +1174,32 @@ export async function verifyStartupRows(graph, fetchBody, rows) {
|
|
|
1165
1174
|
}
|
|
1166
1175
|
}
|
|
1167
1176
|
|
|
1177
|
+
/**
|
|
1178
|
+
* Read one startup script the way the document itself would get it.
|
|
1179
|
+
*
|
|
1180
|
+
* The route answers a script from an older generation at the revision that still holds it, so a
|
|
1181
|
+
* document booting from the shell's frozen list comes up instead of 404. Reading the address
|
|
1182
|
+
* directly would see that same 404 and refuse a rebuild the button can actually carry out, so the
|
|
1183
|
+
* answer is taken through the same mapping and only falls back to the raw address when the mapping
|
|
1184
|
+
* has nothing to say.
|
|
1185
|
+
*/
|
|
1186
|
+
export async function readStartupScript(clientModules, url) {
|
|
1187
|
+
try {
|
|
1188
|
+
const answer = await currentBundleAnswer(clientModules, 'GET', url)
|
|
1189
|
+
if (answer !== null) return { status: 200, arrayBuffer: async () => answer.body }
|
|
1190
|
+
} catch {}
|
|
1191
|
+
return fetchBundleAt(clientModules, url, 'GET')
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1168
1194
|
/** Ask the same question of the plugin table the plugin itself talks to. */
|
|
1169
|
-
async function verifyStartup(clientModules, rows) {
|
|
1195
|
+
export async function verifyStartup(clientModules, rows) {
|
|
1170
1196
|
let graph = null
|
|
1171
1197
|
try {
|
|
1172
1198
|
graph = clientModules.graph()
|
|
1173
1199
|
} catch (error) {
|
|
1174
1200
|
return { status: 'blocked', reason: 'list', detail: String(error?.message ?? error) }
|
|
1175
1201
|
}
|
|
1176
|
-
return verifyStartupRows(graph, (url) =>
|
|
1202
|
+
return verifyStartupRows(graph, (url) => readStartupScript(clientModules, url), rows)
|
|
1177
1203
|
}
|
|
1178
1204
|
|
|
1179
1205
|
/**
|
|
@@ -1287,9 +1313,12 @@ export function apply(ctx, config) {
|
|
|
1287
1313
|
const syncBundles = () => {
|
|
1288
1314
|
ensureBundleShim(ctx)
|
|
1289
1315
|
}
|
|
1290
|
-
/**
|
|
1316
|
+
/**
|
|
1317
|
+
* Watching needs both the HMR service and an explicit opt-in: the switch defaults off, so no
|
|
1318
|
+
* root is installed and no saved file applies anything by itself.
|
|
1319
|
+
*/
|
|
1291
1320
|
const syncWatching = () => {
|
|
1292
|
-
if (config
|
|
1321
|
+
if (settingsOf(config ?? {}).autoReload !== true) return
|
|
1293
1322
|
if (controller.available() === false) {
|
|
1294
1323
|
closeAll(globalThis[WATCHER_KEY])
|
|
1295
1324
|
globalThis[WATCHER_KEY] = null
|
|
@@ -1298,10 +1327,15 @@ export function apply(ctx, config) {
|
|
|
1298
1327
|
if (globalThis[WATCHER_KEY] !== null) return
|
|
1299
1328
|
globalThis[WATCHER_KEY] = controller.startWatching()
|
|
1300
1329
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1330
|
+
// Printed on every start so a reader of the log knows which action applies a change: a person
|
|
1331
|
+
// pressing the button, never this plugin, and never a restart of the app.
|
|
1332
|
+
log('plugin reload is manual: ask the user to press the refresh button; do not restart the app')
|
|
1333
|
+
if (settingsOf(config ?? {}).autoReload === true) {
|
|
1303
1334
|
syncWatching()
|
|
1304
|
-
if (globalThis[WATCHER_KEY] === null) log('
|
|
1335
|
+
if (globalThis[WATCHER_KEY] === null) log('auto reload is on but the hmr entry is not running; a press still works')
|
|
1336
|
+
else log('auto reload is on: a saved plugin change applies without a press')
|
|
1337
|
+
} else {
|
|
1338
|
+
log('auto reload is off: no plugin source is watched')
|
|
1305
1339
|
}
|
|
1306
1340
|
if (typeof ctx.on === 'function') {
|
|
1307
1341
|
ctx.on('internal/service', (service) => {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-unlag",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "A press applies plugin edits right away and brings a laggy interface back; automatic reloading stays off unless you turn it on, so the press is the only trigger, and nothing restarts.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./index.js",
|