dsh-jiuling-remote-mobile 1.0.17 → 1.0.18
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 +36 -0
- package/lib/index.js +82 -11
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -415,6 +415,38 @@ window.__ModuleLoader__.load({
|
|
|
415
415
|
};
|
|
416
416
|
}, []);
|
|
417
417
|
|
|
418
|
+
/**
|
|
419
|
+
* 让官方前端的模型目录**失效重载**。
|
|
420
|
+
*
|
|
421
|
+
* ## ⚠️ 这一步为什么不能省
|
|
422
|
+
*
|
|
423
|
+
* 官方 `ModelCatalogDirectory` 在页面加载时就 `load()` 一次并进入
|
|
424
|
+
* `status === 'ready'`,此后**点菜单一个请求都不发**
|
|
425
|
+
* (`dsh-client-ui-model-selection/lib/client.js:100`:ready 时直接
|
|
426
|
+
* 返回旧值)。所以「配对之后模型列表变成电脑的」这件事,只把宿主那边的
|
|
427
|
+
* 缓存换掉是**看不见的** —— 前端那一层永远捧着配对前拉到的手机列表。
|
|
428
|
+
*
|
|
429
|
+
* 这正是用户报的「预设列表正确地加载了电脑预设,但模型列表还是没同步」:
|
|
430
|
+
* 预设走 `remoteExportList`,**没有缓存层**、每次重读,所以它是对的。
|
|
431
|
+
*
|
|
432
|
+
* `modelDirectories` 是官方那个服务实例(`super(ctx, "modelDirectories")`),
|
|
433
|
+
* `.catalog.refresh()` 就是官方在 `llm/adapters-updated` 时走的同一条路 ——
|
|
434
|
+
* 走公开方法,不碰前端 bundle 内部类。
|
|
435
|
+
*
|
|
436
|
+
* `ctx.get` 拿不到服务时静默跳过(见 `injected()` 里 `modelDirectories`
|
|
437
|
+
* 的注释):最坏是回到「要点一下刷新按钮」的旧行为,不会报错。
|
|
438
|
+
*/
|
|
439
|
+
var reloadModelCatalog = function () {
|
|
440
|
+
try {
|
|
441
|
+
var models = props.modelDirectories;
|
|
442
|
+
if (models && models.catalog && typeof models.catalog.refresh === 'function') {
|
|
443
|
+
models.catalog.refresh();
|
|
444
|
+
}
|
|
445
|
+
} catch (error) {
|
|
446
|
+
/* 服务结构变了就只更新宿主缓存,不打扰用户 */
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
|
|
418
450
|
// 每次打开都回到「手机」页签,避免上次停在电脑目录造成困惑。
|
|
419
451
|
react.useEffect(
|
|
420
452
|
function () {
|
|
@@ -539,6 +571,10 @@ window.__ModuleLoader__.load({
|
|
|
539
571
|
// 必须作废,否则用户会看到上一台电脑的目录。
|
|
540
572
|
clearDesktopDirCache();
|
|
541
573
|
patch({ path: null, listing: null });
|
|
574
|
+
// ⚠️ 前端那份模型目录是**永久缓存**的(见 `reloadModelCatalog`),
|
|
575
|
+
// 不作废的话用户接下来看到的仍然是配对前那份手机模型列表 ——
|
|
576
|
+
// 就是用户报的「模型列表还是没同步」。
|
|
577
|
+
reloadModelCatalog();
|
|
542
578
|
},
|
|
543
579
|
function (reason) {
|
|
544
580
|
if (!aliveRef.current) return;
|
package/lib/index.js
CHANGED
|
@@ -678,7 +678,7 @@ export function apply(ctx) {
|
|
|
678
678
|
const watchers = new Set();
|
|
679
679
|
|
|
680
680
|
/**
|
|
681
|
-
* 电脑侧的静默缓存:`{ catalog, presets, at }`。
|
|
681
|
+
* 电脑侧的静默缓存:`{ catalog, presets, directory, at }`。
|
|
682
682
|
*
|
|
683
683
|
* ## 为什么要缓存(用户需求原文)
|
|
684
684
|
*
|
|
@@ -686,10 +686,17 @@ export function apply(ctx) {
|
|
|
686
686
|
* 以及目录树这样体验会好很多……因为一般项目目录是不会变的,可以提前加载」。
|
|
687
687
|
*
|
|
688
688
|
* 配对后立刻拉一次,之后前端一打开模型菜单就有内容,不用等一轮往返。
|
|
689
|
-
*
|
|
690
|
-
*
|
|
689
|
+
*
|
|
690
|
+
* ⚠️ **`directory` 只缓存「根」那一层**(`listDirectory` 不给 path 时返回的
|
|
691
|
+
* home),因为「点开电脑页签」是每次都逃不掉的第一步,而它现在必然要过一轮
|
|
692
|
+
* 隧道往返 —— 用户看到的就是那个转圈(原话:「还是有一个加载过程,
|
|
693
|
+
* 不能预加载吗」)。
|
|
694
|
+
*
|
|
695
|
+
* **整棵目录树不进缓存**:电脑上几万个目录,全拉一遍既慢又没意义,而且
|
|
696
|
+
* 会大面积显示过期内容。点进子目录仍然实时拉(前端另有 `desktopDirCache`
|
|
697
|
+
* 兜住「同一层来回点」)。根目录的过期窗口由「刷新」按钮关闭。
|
|
691
698
|
*/
|
|
692
|
-
const desktopCache = { catalog: null, presets: null, at: 0 };
|
|
699
|
+
const desktopCache = { catalog: null, presets: null, directory: null, at: 0 };
|
|
693
700
|
|
|
694
701
|
/**
|
|
695
702
|
* 静默预拉电脑的模型目录与预设列表。
|
|
@@ -703,7 +710,7 @@ export function apply(ctx) {
|
|
|
703
710
|
*/
|
|
704
711
|
const prefetchDesktop = async (why) => {
|
|
705
712
|
if (config.token === EMPTY_TOKEN) return;
|
|
706
|
-
const [catalog, presets] = await Promise.all([
|
|
713
|
+
const [catalog, presets, directory] = await Promise.all([
|
|
707
714
|
callRemote(config, 'modelCatalog', {}).catch((error) => {
|
|
708
715
|
ctx.logger?.warn?.(`[dsh-remote-mobile] 预拉模型列表失败(${why}): ${String(error?.message ?? error)}`);
|
|
709
716
|
return null;
|
|
@@ -712,16 +719,49 @@ export function apply(ctx) {
|
|
|
712
719
|
ctx.logger?.warn?.(`[dsh-remote-mobile] 预拉预设列表失败(${why}): ${String(error?.message ?? error)}`);
|
|
713
720
|
return null;
|
|
714
721
|
}),
|
|
722
|
+
// 根目录那一层:不带 path ⇒ 电脑返回它的 home,正是用户点开
|
|
723
|
+
// 「电脑」页签第一眼看到的东西。见 `desktopCache.directory` 的注释。
|
|
724
|
+
callRemote(config, 'listDirectory', {}).catch((error) => {
|
|
725
|
+
ctx.logger?.warn?.(`[dsh-remote-mobile] 预拉目录树失败(${why}): ${String(error?.message ?? error)}`);
|
|
726
|
+
return null;
|
|
727
|
+
}),
|
|
715
728
|
]);
|
|
716
729
|
if (catalog !== null) desktopCache.catalog = catalog;
|
|
717
730
|
if (presets !== null) desktopCache.presets = presets;
|
|
731
|
+
if (directory !== null) desktopCache.directory = directory;
|
|
718
732
|
desktopCache.at = Date.now();
|
|
719
733
|
const models = catalog?.groups?.reduce((n, g) => n + (g.models?.length ?? 0), 0) ?? 0;
|
|
734
|
+
const dirs = directory?.entries?.length ?? directory?.items?.length ?? 0;
|
|
720
735
|
ctx.logger?.info?.(
|
|
721
|
-
`[dsh-remote-mobile] 已预拉电脑数据(${why}):${models} 个模型、${presets?.presets?.length ?? 0}
|
|
736
|
+
`[dsh-remote-mobile] 已预拉电脑数据(${why}):${models} 个模型、${presets?.presets?.length ?? 0} 个预设、根目录 ${dirs} 项`,
|
|
722
737
|
);
|
|
723
738
|
};
|
|
724
739
|
|
|
740
|
+
/**
|
|
741
|
+
* 取电脑的模型目录,缓存为空就**当场拉一次**。
|
|
742
|
+
*
|
|
743
|
+
* ⚠️ **不能只依赖配对时的预拉**。那是异步的后台动作,而前端页面一加载
|
|
744
|
+
* 就会调一次 `modelCatalog`(`ModelCatalogDirectory` 构造后立刻 `load()`,
|
|
745
|
+
* 见 `dsh-client-ui-model-selection/lib/client.js:354`)—— 谁先到谁决定
|
|
746
|
+
* 用户看到什么。拿不到电脑那份就返回手机本地列表,症状正是用户报的
|
|
747
|
+
* 「预设列表正确地加载了电脑预设,但模型列表还是没同步」:预设走
|
|
748
|
+
* `remoteExportList`,**没有缓存层**、每次重读,所以它是对的。
|
|
749
|
+
*
|
|
750
|
+
* @returns 电脑的模型目录;拉不到时 `null`。
|
|
751
|
+
*/
|
|
752
|
+
const desktopCatalog = async () => {
|
|
753
|
+
if (desktopCache.catalog !== null) return desktopCache.catalog;
|
|
754
|
+
try {
|
|
755
|
+
desktopCache.catalog = await callRemote(config, 'modelCatalog', {});
|
|
756
|
+
return desktopCache.catalog;
|
|
757
|
+
} catch (error) {
|
|
758
|
+
ctx.logger?.warn?.(
|
|
759
|
+
`[dsh-remote-mobile] 取电脑模型列表失败: ${String(error?.message ?? error)}`,
|
|
760
|
+
);
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
|
|
725
765
|
/**
|
|
726
766
|
* 把电脑的模型目录并进手机本地的目录。
|
|
727
767
|
*
|
|
@@ -1152,8 +1192,10 @@ export function apply(ctx) {
|
|
|
1152
1192
|
const originalCatalog = controller.modelCatalog.bind(controller);
|
|
1153
1193
|
controller.modelCatalog = async (...args) => {
|
|
1154
1194
|
const local = await originalCatalog(...args);
|
|
1155
|
-
if (!paired()
|
|
1156
|
-
|
|
1195
|
+
if (!paired()) return local;
|
|
1196
|
+
const desktop = await desktopCatalog();
|
|
1197
|
+
if (desktop === null) return local;
|
|
1198
|
+
return mergeCatalog(local, desktop);
|
|
1157
1199
|
};
|
|
1158
1200
|
undo.push(() => {
|
|
1159
1201
|
controller.modelCatalog = originalCatalog;
|
|
@@ -1569,9 +1611,24 @@ async function handleBridge(ctx, config, io) {
|
|
|
1569
1611
|
case 'ping':
|
|
1570
1612
|
return callRemote(config, 'ping', {});
|
|
1571
1613
|
|
|
1572
|
-
/**
|
|
1573
|
-
|
|
1574
|
-
|
|
1614
|
+
/**
|
|
1615
|
+
* 列电脑上的目录。
|
|
1616
|
+
*
|
|
1617
|
+
* ⚠️ **根那层命中预拉缓存就直接回,不打隧道**:用户点开「电脑」页签时
|
|
1618
|
+
* 看的正是这一层,而它每一轮往返都要穿过 Cloudflare 隧道 ——
|
|
1619
|
+
* 那就是用户抱怨的那个「还是有一个加载过程」(原话:「目录树都提前存档
|
|
1620
|
+
* 手机本地了,为什么还是还加载呢,不能预加载吗」)。
|
|
1621
|
+
*
|
|
1622
|
+
* 只在**没给 path**(= 要 home)且预拉成功过时才走缓存。子目录照旧实时拉,
|
|
1623
|
+
* 前端 `desktopDirCache` 负责「同一层来回点」,两边分工见各自的注释。
|
|
1624
|
+
*/
|
|
1625
|
+
case 'listDesktop': {
|
|
1626
|
+
const wantRoot = params.path === undefined || params.path === null || params.path === '';
|
|
1627
|
+
if (wantRoot && io.desktopCache?.directory != null) return io.desktopCache.directory;
|
|
1628
|
+
const listing = await callRemote(config, 'listDirectory', { path: params.path });
|
|
1629
|
+
if (wantRoot && io.desktopCache !== undefined) io.desktopCache.directory = listing;
|
|
1630
|
+
return listing;
|
|
1631
|
+
}
|
|
1575
1632
|
|
|
1576
1633
|
/**
|
|
1577
1634
|
* 电脑上的模型目录。
|
|
@@ -1615,9 +1672,23 @@ async function handleBridge(ctx, config, io) {
|
|
|
1615
1672
|
// ⚠️ 刷新到的模型/预设要**写回缓存**,否则「刷新按钮」只更新了它自己
|
|
1616
1673
|
// 那份响应,前端模型菜单读的是宿主拦截器里的缓存 —— 用户会看到
|
|
1617
1674
|
// 「点了刷新,模型列表没变」。
|
|
1675
|
+
//
|
|
1676
|
+
// 根目录那一层同理:刷完不回写的话,用户接下来点开「电脑」页签
|
|
1677
|
+
// 命中的还是**刷新前**那份预拉缓存,看起来就是「刷新没生效」。
|
|
1678
|
+
//
|
|
1679
|
+
// ⚠️ **判据是「拉回来的这一层就是缓存的那一层」**(同 path),
|
|
1680
|
+
// 不是「有没有带 path」:前端在根层点刷新时传的是当前显示的绝对路径
|
|
1681
|
+
// (`loc.path`,也就是 home),照样是带 path 的一次调用 —— 只看
|
|
1682
|
+
// `params.path` 空不空会把根层刷新漏掉。
|
|
1618
1683
|
if (io.desktopCache !== undefined) {
|
|
1619
1684
|
if (result.models?.ok === true) io.desktopCache.catalog = result.models.value;
|
|
1620
1685
|
if (result.presets?.ok === true) io.desktopCache.presets = result.presets.value;
|
|
1686
|
+
if (
|
|
1687
|
+
result.directory?.ok === true &&
|
|
1688
|
+
io.desktopCache.directory?.path === result.directory.value?.path
|
|
1689
|
+
) {
|
|
1690
|
+
io.desktopCache.directory = result.directory.value;
|
|
1691
|
+
}
|
|
1621
1692
|
io.desktopCache.at = Date.now();
|
|
1622
1693
|
}
|
|
1623
1694
|
return result;
|