dsh-chat-link-actions 0.2.0 → 0.2.2
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/client.js +23 -3
- package/index.js +28 -5
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -35,6 +35,8 @@ window.__ModuleLoader__.load({
|
|
|
35
35
|
var drafts = new Map();
|
|
36
36
|
/** Client root context, kept for the session-scoped chip event. */
|
|
37
37
|
var rootContext = null;
|
|
38
|
+
/** 应用提供的服务,用来在文件管理器里定位路径。 */
|
|
39
|
+
var appRemote = null;
|
|
38
40
|
/** Session whose composer rendered most recently. */
|
|
39
41
|
var activeSession = null;
|
|
40
42
|
|
|
@@ -547,9 +549,26 @@ window.__ModuleLoader__.load({
|
|
|
547
549
|
}, 1400);
|
|
548
550
|
}
|
|
549
551
|
|
|
550
|
-
/**
|
|
552
|
+
/** 在系统文件管理器里选中路径,接口不可用时走本地通道。 */
|
|
551
553
|
function reveal(path) {
|
|
552
|
-
|
|
554
|
+
var remote = appRemote === null ? null : appRemote.session;
|
|
555
|
+
if (remote !== null && remote !== undefined && typeof remote.openWorkspacePath === "function") {
|
|
556
|
+
remote.openWorkspacePath({ path: path, action: "reveal" }).then(function (result) {
|
|
557
|
+
if (result !== null && result !== undefined && result.ok) return;
|
|
558
|
+
revealViaRoute(path);
|
|
559
|
+
}, function () {
|
|
560
|
+
revealViaRoute(path);
|
|
561
|
+
});
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
revealViaRoute(path);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/** 本地通道:请应用在系统文件管理器里选中路径。 */
|
|
568
|
+
function revealViaRoute(path) {
|
|
569
|
+
fetch("/chat-link-actions?action=reveal&path=" + encodeURIComponent(path), { method: "POST" }).then(function (response) {
|
|
570
|
+
if (!response.ok) toast("无法打开文件管理器");
|
|
571
|
+
}, function () {
|
|
553
572
|
toast("无法打开文件管理器");
|
|
554
573
|
});
|
|
555
574
|
}
|
|
@@ -568,10 +587,11 @@ window.__ModuleLoader__.load({
|
|
|
568
587
|
|
|
569
588
|
/* ── plugin entry ───────────────────────────────────────────────────── */
|
|
570
589
|
|
|
571
|
-
var inject = ["slots", "sessions"];
|
|
590
|
+
var inject = ["slots", "sessions", "remote", "remote.session"];
|
|
572
591
|
|
|
573
592
|
function apply(ctx) {
|
|
574
593
|
rootContext = ctx;
|
|
594
|
+
appRemote = ctx.remote === undefined ? null : ctx.remote;
|
|
575
595
|
ctx.effect(function () {
|
|
576
596
|
document.addEventListener("contextmenu", onContextMenu, true);
|
|
577
597
|
return function () {
|
package/index.js
CHANGED
|
@@ -48,11 +48,33 @@ function reply(res, status, body) {
|
|
|
48
48
|
export function apply(ctx) {
|
|
49
49
|
const fence = (req) => ctx.connection.requestRejection(req) === undefined
|
|
50
50
|
|
|
51
|
+
/** Start the file manager and report whether the launcher accepted the command. */
|
|
52
|
+
const launch = async (path) => {
|
|
53
|
+
const { command, args } = revealCommand(path)
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
let settled = false
|
|
56
|
+
const done = (value) => {
|
|
57
|
+
if (settled) return
|
|
58
|
+
settled = true
|
|
59
|
+
clearTimeout(timer)
|
|
60
|
+
resolve(value)
|
|
61
|
+
}
|
|
62
|
+
const timer = setTimeout(() => done('launched'), 1500)
|
|
63
|
+
const child = spawn(command, args, { detached: true, stdio: 'ignore' })
|
|
64
|
+
child.once('spawn', () => done('launched'))
|
|
65
|
+
child.once('error', (error) => done(error))
|
|
66
|
+
child.unref()
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
51
70
|
ctx.effect(() => ctx.webServer.register({
|
|
52
71
|
kind: 'prefix',
|
|
53
72
|
path: '/chat-link-actions',
|
|
54
73
|
handler: async (req, res) => {
|
|
55
|
-
if (!fence(req))
|
|
74
|
+
if (!fence(req)) {
|
|
75
|
+
ctx.logger?.warn?.('chat-link-actions: reveal request rejected by the connection fence')
|
|
76
|
+
return reply(res, 403, { ok: false, error: 'forbidden' })
|
|
77
|
+
}
|
|
56
78
|
if (req.method !== 'POST') return reply(res, 405, { ok: false, error: 'method' })
|
|
57
79
|
try {
|
|
58
80
|
const url = new URL(req.url ?? '/', 'http://dsh.internal')
|
|
@@ -62,10 +84,11 @@ export function apply(ctx) {
|
|
|
62
84
|
const path = requireAbsolute(url.searchParams.get('path'))
|
|
63
85
|
const info = await stat(path).catch(() => null)
|
|
64
86
|
if (info === null) return reply(res, 404, { ok: false, error: 'not-found' })
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
87
|
+
const outcome = await launch(path)
|
|
88
|
+
if (outcome !== 'launched') {
|
|
89
|
+
ctx.logger?.warn?.(`chat-link-actions: file manager launch failed: ${String(outcome?.message ?? outcome)}`)
|
|
90
|
+
return reply(res, 500, { ok: false, error: 'launch-failed' })
|
|
91
|
+
}
|
|
69
92
|
return reply(res, 200, { ok: true })
|
|
70
93
|
} catch (error) {
|
|
71
94
|
return reply(res, 400, { ok: false, error: String(error?.message ?? error) })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-chat-link-actions",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Right-click actions on file links in the DSH transcript: quote the path into the composer, or reveal the file in the OS file manager. Menus are drawn in the app's own style, never the system one.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|