@oversk7/pi-web-dsh-style 1.3.4 → 1.4.0
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 +4 -0
- package/package.json +1 -1
- package/server.ts +5 -15
- package/web/app.js +56 -10
package/README.md
CHANGED
|
@@ -44,6 +44,8 @@ Each visible page subscribes to its selected session, receives an initial snapsh
|
|
|
44
44
|
|
|
45
45
|
Switching back to a newly created or previously loaded session shows its cached content immediately and keeps the composer available while refreshing. Repeated opens share pending requests, and sessions with a running process return their current snapshot without waiting for RPC refreshes.
|
|
46
46
|
|
|
47
|
+
In the in-app workspace directory browser, click the pencil to paste a full path. The list loads the corresponding directory and filters matching folders, including deeply nested paths. Click a folder to select it, or press Enter to navigate directly; a trailing separator previews the folder's contents. Relative file links in replies resolve against the selected session's workspace.
|
|
48
|
+
|
|
47
49
|
The interface starts in Chinese. To switch to English, open **设置 (Settings) → 外观 (Appearance) → 界面语言 (Interface language) → English**. Your choice is saved in this browser.
|
|
48
50
|
|
|
49
51
|
### Phone and remote access
|
|
@@ -93,6 +95,8 @@ pi --web
|
|
|
93
95
|
|
|
94
96
|
切回新建或已加载的会话时,立即显示缓存内容,后台刷新期间仍可编辑输入。重复打开会复用待完成请求;已有运行进程的会话直接返回当前快照,避免等待 RPC 刷新。
|
|
95
97
|
|
|
98
|
+
在内置工作区目录浏览器中,点击铅笔图标即可粘贴完整路径,列表会加载对应目录并筛选匹配的文件夹,支持多层嵌套路径。点击文件夹选择,或按 Enter 直接跳转;路径末尾带分隔符时预览该文件夹的内容。回答中的相对文件链接按当前会话的工作区解析。
|
|
99
|
+
|
|
96
100
|
界面默认使用中文。打开 **设置 → 外观 → 界面语言**,即可一键切换中文或 English;选择会保存在当前浏览器。
|
|
97
101
|
|
|
98
102
|
### 手机与远程访问
|
package/package.json
CHANGED
package/server.ts
CHANGED
|
@@ -1943,21 +1943,11 @@ async function listDirectories(input: string): Promise<Json> {
|
|
|
1943
1943
|
.map((e) => ({ name: e.name, path: join(target, e.name), hidden: e.name.startsWith(".") }))
|
|
1944
1944
|
.sort((a, b) => a.name.localeCompare(b.name, "zh-CN"));
|
|
1945
1945
|
const crumbs: { name: string; path: string; hidden: boolean }[] = [];
|
|
1946
|
-
{
|
|
1947
|
-
const
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
const up = dirname(current);
|
|
1952
|
-
if (up === current) break;
|
|
1953
|
-
current = up;
|
|
1954
|
-
}
|
|
1955
|
-
let acc = parts[0];
|
|
1956
|
-
crumbs.push({ name: parts[0], path: parts[0], hidden: false });
|
|
1957
|
-
for (const part of parts.slice(1)) {
|
|
1958
|
-
acc = join(acc, part);
|
|
1959
|
-
crumbs.push({ name: part, path: acc, hidden: part.startsWith(".") });
|
|
1960
|
-
}
|
|
1946
|
+
for (let current = target; ; current = dirname(current)) {
|
|
1947
|
+
const isRoot = dirname(current) === current;
|
|
1948
|
+
const name = isRoot ? current : basename(current);
|
|
1949
|
+
crumbs.unshift({ name, path: current, hidden: !isRoot && name.startsWith(".") });
|
|
1950
|
+
if (isRoot) break;
|
|
1961
1951
|
}
|
|
1962
1952
|
const parent = dirname(target) === target ? null : dirname(target);
|
|
1963
1953
|
return { path: target, home, parent, crumbs, entries: dirs, truncated: false };
|
package/web/app.js
CHANGED
|
@@ -3278,6 +3278,7 @@ async function ensureWorkspaceThen(action) {
|
|
|
3278
3278
|
let browser = null;
|
|
3279
3279
|
let pickerResolve = null;
|
|
3280
3280
|
let browserKeyHandler = null;
|
|
3281
|
+
let directoryRequest = null;
|
|
3281
3282
|
|
|
3282
3283
|
async function adoptWorkspacePath(path) {
|
|
3283
3284
|
const added = await post("/api/workspaces", { path });
|
|
@@ -3354,6 +3355,7 @@ function promptWorkspaceBrowse() {
|
|
|
3354
3355
|
}
|
|
3355
3356
|
|
|
3356
3357
|
function closeWorkspacePicker() {
|
|
3358
|
+
directoryRequest = null;
|
|
3357
3359
|
if (browserKeyHandler) {
|
|
3358
3360
|
document.removeEventListener("keydown", browserKeyHandler);
|
|
3359
3361
|
browserKeyHandler = null;
|
|
@@ -3387,10 +3389,44 @@ function draftDirectory(draft) {
|
|
|
3387
3389
|
function readDraft(listing, draft) {
|
|
3388
3390
|
const directory = draftDirectory(draft);
|
|
3389
3391
|
if (directory === null) return { directory: null, tail: null };
|
|
3390
|
-
const
|
|
3392
|
+
const fold = (value) => separatorOf() === "\\" ? value.replaceAll("/", "\\").toLowerCase() : value;
|
|
3393
|
+
const answers = fold(directory) === fold(levelDirectory(listing));
|
|
3391
3394
|
return { directory, tail: answers ? draft.slice(directory.length) : null };
|
|
3392
3395
|
}
|
|
3393
3396
|
|
|
3397
|
+
async function previewPathDraft(draft) {
|
|
3398
|
+
const current = browser;
|
|
3399
|
+
const request = {};
|
|
3400
|
+
directoryRequest = request;
|
|
3401
|
+
if (!current || current.pathDraft !== draft) return;
|
|
3402
|
+
const listing = current.child ?? current.parent;
|
|
3403
|
+
const directory = draftDirectory(draft);
|
|
3404
|
+
current.error = null;
|
|
3405
|
+
if (directory === null || (listing && readDraft(listing, draft).tail !== null)) {
|
|
3406
|
+
current.loading = false;
|
|
3407
|
+
renderBrowser();
|
|
3408
|
+
return;
|
|
3409
|
+
}
|
|
3410
|
+
current.loading = true;
|
|
3411
|
+
renderBrowser();
|
|
3412
|
+
try {
|
|
3413
|
+
const data = await api(`/api/fs/list?path=${encodeURIComponent(directory)}`);
|
|
3414
|
+
if (browser !== current || directoryRequest !== request) return;
|
|
3415
|
+
if (data.error) throw new Error(data.error);
|
|
3416
|
+
current.parent = data;
|
|
3417
|
+
current.selected = null;
|
|
3418
|
+
current.child = null;
|
|
3419
|
+
current.home = data.home || current.home;
|
|
3420
|
+
current.loading = false;
|
|
3421
|
+
renderBrowser();
|
|
3422
|
+
} catch (error) {
|
|
3423
|
+
if (browser !== current || directoryRequest !== request) return;
|
|
3424
|
+
current.loading = false;
|
|
3425
|
+
current.error = error.message || String(error);
|
|
3426
|
+
renderBrowser();
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3394
3430
|
function visibleEntries(entries, selectedPath, showHidden, filterPrefix) {
|
|
3395
3431
|
const needle = filterPrefix === null ? "" : filterPrefix.toLowerCase();
|
|
3396
3432
|
const displayable = (entry) => showHidden || !entry.hidden || needle.startsWith(".");
|
|
@@ -3429,6 +3465,9 @@ function browserTargetPath() {
|
|
|
3429
3465
|
|
|
3430
3466
|
async function navigate(path) {
|
|
3431
3467
|
if (!browser || !path) return;
|
|
3468
|
+
const current = browser;
|
|
3469
|
+
const request = {};
|
|
3470
|
+
directoryRequest = request;
|
|
3432
3471
|
browser.loading = true;
|
|
3433
3472
|
browser.error = null;
|
|
3434
3473
|
browser.selected = null;
|
|
@@ -3437,7 +3476,7 @@ async function navigate(path) {
|
|
|
3437
3476
|
let target = null;
|
|
3438
3477
|
try {
|
|
3439
3478
|
const data = await api(`/api/fs/list?path=${encodeURIComponent(path)}`);
|
|
3440
|
-
if (
|
|
3479
|
+
if (browser !== current || directoryRequest !== request) return;
|
|
3441
3480
|
if (data.error) throw new Error(data.error);
|
|
3442
3481
|
target = data;
|
|
3443
3482
|
browser.home = data.home || browser.home;
|
|
@@ -3446,7 +3485,7 @@ async function navigate(path) {
|
|
|
3446
3485
|
browser.loading = false;
|
|
3447
3486
|
renderBrowser();
|
|
3448
3487
|
} catch (error) {
|
|
3449
|
-
if (
|
|
3488
|
+
if (browser !== current || directoryRequest !== request) return;
|
|
3450
3489
|
browser.loading = false;
|
|
3451
3490
|
browser.error = error.message || String(error);
|
|
3452
3491
|
renderBrowser();
|
|
@@ -3459,7 +3498,7 @@ async function navigate(path) {
|
|
|
3459
3498
|
if (!parentCrumb) return;
|
|
3460
3499
|
try {
|
|
3461
3500
|
const data = await api(`/api/fs/list?path=${encodeURIComponent(parentCrumb.path)}`);
|
|
3462
|
-
if (
|
|
3501
|
+
if (browser !== current || directoryRequest !== request || data.error) return;
|
|
3463
3502
|
const sep = separatorOf();
|
|
3464
3503
|
const fold = (value) => (sep === "\\" ? value.toLowerCase() : value);
|
|
3465
3504
|
const match = (data.entries || []).find((entry) => fold(entry.path) === fold(target.path));
|
|
@@ -3475,6 +3514,10 @@ async function navigate(path) {
|
|
|
3475
3514
|
|
|
3476
3515
|
async function selectEntry(entry) {
|
|
3477
3516
|
if (!browser) return;
|
|
3517
|
+
const current = browser;
|
|
3518
|
+
const request = {};
|
|
3519
|
+
directoryRequest = request;
|
|
3520
|
+
browser.pathDraft = null;
|
|
3478
3521
|
browser.selected = entry;
|
|
3479
3522
|
browser.child = null;
|
|
3480
3523
|
browser.loading = true;
|
|
@@ -3482,13 +3525,13 @@ async function selectEntry(entry) {
|
|
|
3482
3525
|
renderBrowser();
|
|
3483
3526
|
try {
|
|
3484
3527
|
const data = await api(`/api/fs/list?path=${encodeURIComponent(entry.path)}`);
|
|
3485
|
-
if (
|
|
3528
|
+
if (browser !== current || directoryRequest !== request) return;
|
|
3486
3529
|
if (data.error) throw new Error(data.error);
|
|
3487
3530
|
browser.child = data;
|
|
3488
3531
|
browser.loading = false;
|
|
3489
3532
|
renderBrowser();
|
|
3490
3533
|
} catch (error) {
|
|
3491
|
-
if (
|
|
3534
|
+
if (browser !== current || directoryRequest !== request) return;
|
|
3492
3535
|
browser.loading = false;
|
|
3493
3536
|
browser.error = error.message || String(error);
|
|
3494
3537
|
browser.selected = null;
|
|
@@ -3513,6 +3556,8 @@ function openPathEditor() {
|
|
|
3513
3556
|
|
|
3514
3557
|
function cancelPathEdit() {
|
|
3515
3558
|
if (!browser) return;
|
|
3559
|
+
directoryRequest = null;
|
|
3560
|
+
browser.loading = false;
|
|
3516
3561
|
browser.pathDraft = null;
|
|
3517
3562
|
browser.error = null;
|
|
3518
3563
|
renderBrowser();
|
|
@@ -3628,6 +3673,8 @@ function renderBrowser() {
|
|
|
3628
3673
|
</div>
|
|
3629
3674
|
</div>` : "";
|
|
3630
3675
|
|
|
3676
|
+
const previousPathInput = $("#dbPathInput");
|
|
3677
|
+
const pathSelection = previousPathInput ? [previousPathInput.selectionStart, previousPathInput.selectionEnd] : null;
|
|
3631
3678
|
mount.innerHTML = `
|
|
3632
3679
|
<div class="db-modal">
|
|
3633
3680
|
<div class="db-modalMask" data-action="db-cancel"></div>
|
|
@@ -3654,10 +3701,9 @@ function renderBrowser() {
|
|
|
3654
3701
|
|
|
3655
3702
|
const pathInput = $("#dbPathInput");
|
|
3656
3703
|
if (pathInput) {
|
|
3657
|
-
const start = pathInput.selectionStart;
|
|
3658
|
-
const end = pathInput.selectionEnd;
|
|
3704
|
+
const [start, end] = pathSelection ?? [pathInput.selectionStart, pathInput.selectionEnd];
|
|
3659
3705
|
pathInput.focus();
|
|
3660
|
-
if (start != null && end != null
|
|
3706
|
+
if (start != null && end != null) pathInput.setSelectionRange(start, end);
|
|
3661
3707
|
pathInput.addEventListener("keydown", (event) => {
|
|
3662
3708
|
if (event.key === "Enter" && !event.isComposing) {
|
|
3663
3709
|
event.preventDefault();
|
|
@@ -3672,7 +3718,7 @@ function renderBrowser() {
|
|
|
3672
3718
|
pathInput.addEventListener("input", () => {
|
|
3673
3719
|
const caret = pathInput.selectionStart ?? pathInput.value.length;
|
|
3674
3720
|
b.pathDraft = pathInput.value;
|
|
3675
|
-
|
|
3721
|
+
void previewPathDraft(b.pathDraft);
|
|
3676
3722
|
const next = $("#dbPathInput");
|
|
3677
3723
|
if (next) next.setSelectionRange(caret, caret);
|
|
3678
3724
|
});
|