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.
Files changed (3) hide show
  1. package/client.js +23 -3
  2. package/index.js +28 -5
  3. 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
- /** Ask the 服务端 to select the file in the OS file manager. */
552
+ /** 在系统文件管理器里选中路径,接口不可用时走本地通道。 */
551
553
  function reveal(path) {
552
- fetch("/chat-link-actions?action=reveal&path=" + encodeURIComponent(path), { method: "POST" }).catch(function () {
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)) return reply(res, 403, { ok: false, error: 'forbidden' })
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 { command, args } = revealCommand(path)
66
- const child = spawn(command, args, { detached: true, stdio: 'ignore' })
67
- child.on('error', () => {})
68
- child.unref()
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.0",
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",