dsh-files-native 0.2.0 → 0.2.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 +20 -7
- package/lib/client.js +152 -138
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,24 @@
|
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
|
+
## ⭐ 欢迎点星收藏
|
|
15
|
+
|
|
16
|
+
如果附件轨帮到了你,欢迎到 [GitHub 仓库](https://github.com/meyaomiao/dsh-files-native) 点个 Star ⭐,让更多 DSH 用户看到它。问题与建议请提 Issue。
|
|
17
|
+
|
|
18
|
+
## 📋 兼容性
|
|
19
|
+
|
|
20
|
+
| 插件版本 | 状态 | 对应 DSH |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| **0.2.x**(当前主线,含 0.2.1) | ✅ | **0.1.5-rc.1 / 0.1.5-rc.2**(及之后的 0.1.5 线) |
|
|
23
|
+
| 0.1.x | 🔧 维护态(仅修 bug) | DSH ≤ 0.1.2-rc.1(仍兼容 0.1.1-rc.2 / 0.1.2-alpha.4) |
|
|
24
|
+
|
|
25
|
+
### 本次升级功能变化
|
|
26
|
+
|
|
27
|
+
- **官方已有的交给官方**:回复尾的「产物」卡不再由本插件绘制,改用 DSH 官方交付文件卡(0.1.5-rc.2 起排版/图标更紧)。用户气泡旁的附件卡仍由本插件画。
|
|
28
|
+
- **粘贴让路**:纯官方四类图(PNG / JPEG / WebP / GIF)交给官方草稿图;其它文件仍走本插件轨。
|
|
29
|
+
- **不依赖 better-sidebar**:模块 `inject` 只有 `slots`。有侧栏则气泡卡走预览,没有则新页签打开。
|
|
30
|
+
- 不要和 `dsh-file-fix` / `dsh-file-upload` / `dsh-paste-to-path` 叠装。
|
|
31
|
+
|
|
14
32
|
运行时插件 id 仍是 `file-native`(上传路由 `/plugins/file-native/*`)。npm / GitHub 仓库名是 `dsh-files-native`。
|
|
15
33
|
|
|
16
34
|
- **图片**(PNG / JPEG / WebP / GIF)走官方草稿图栏(64px 缩略图,点击灯箱),发送后进模型视觉上下文。
|
|
@@ -19,7 +37,7 @@
|
|
|
19
37
|
- **气泡附件卡**:消息发出后,文件卡渲染在用户气泡上方(与图片同列右对齐),注入提示行自动隐藏。
|
|
20
38
|
- **点击打开**:点气泡附件卡——装了 [better-sidebar](https://github.com/用户/dsh-better-sidebar) 走侧栏预览;未装则新页签打开(文本/图片/PDF 页签内展示,二进制自动下载)。
|
|
21
39
|
- 发送后文件卡贴在用户气泡栈(与官方图同一列),作业回复不会插在提问和文件中间。
|
|
22
|
-
-
|
|
40
|
+
- 发送成功即清输入框待发轨。**回复尾产物卡让给官方。**
|
|
23
41
|
|
|
24
42
|
不要和 `dsh-file-fix` / `dsh-file-upload` / `dsh-paste-to-path` 叠装。
|
|
25
43
|
|
|
@@ -29,7 +47,7 @@
|
|
|
29
47
|
|---|---|
|
|
30
48
|
|  |  |
|
|
31
49
|
|
|
32
|
-
| 拖入窗口任意位置 |
|
|
50
|
+
| 拖入窗口任意位置 | 发送后:图与文件卡跟用户气泡(产物走官方卡) |
|
|
33
51
|
|---|---|
|
|
34
52
|
|  |  |
|
|
35
53
|
|
|
@@ -82,9 +100,4 @@ dsh plugin --profile web add .
|
|
|
82
100
|
- 上传路由仅 loopback。
|
|
83
101
|
- 刷新会丢掉「已上传未发送」的文件卡(磁盘文件仍在 `.dsh-uploads/`)。
|
|
84
102
|
|
|
85
|
-
## 兼容性
|
|
86
|
-
|
|
87
|
-
- DeepSeek Harness `0.1.2-rc.1`(仍兼容 `0.1.1-rc.2` 与 `0.1.2-alpha.4`,web profile)
|
|
88
|
-
- 不要和 `dsh-file-fix` / `dsh-file-upload` / `dsh-paste-to-path` 叠装
|
|
89
|
-
|
|
90
103
|
改仓库前先读 [CONTRIBUTING.md](./CONTRIBUTING.md)(Issue → 分支 → Draft PR)。思考原则见 [AI-ISSUE-WORKFLOW.md](./AI-ISSUE-WORKFLOW.md)。插件硬约束见 [AGENTS.md](./AGENTS.md)。
|
package/lib/client.js
CHANGED
|
@@ -28,7 +28,7 @@ __export(client_exports, {
|
|
|
28
28
|
name: () => name
|
|
29
29
|
});
|
|
30
30
|
module.exports = __toCommonJS(client_exports);
|
|
31
|
-
var
|
|
31
|
+
var import_react7 = require("react");
|
|
32
32
|
|
|
33
33
|
// src/rail.tsx
|
|
34
34
|
var import_react2 = require("react");
|
|
@@ -70,11 +70,30 @@ function splitIntake(files) {
|
|
|
70
70
|
}
|
|
71
71
|
return { images, others };
|
|
72
72
|
}
|
|
73
|
+
function planPaste(files) {
|
|
74
|
+
const nativeImages = [];
|
|
75
|
+
const cards = [];
|
|
76
|
+
for (const file of files) {
|
|
77
|
+
if (isNativeImage(file)) nativeImages.push(file);
|
|
78
|
+
else cards.push(file);
|
|
79
|
+
}
|
|
80
|
+
if (cards.length === 0 && nativeImages.length > 0) return { action: "yield" };
|
|
81
|
+
return { action: "take", cards, nativeImages };
|
|
82
|
+
}
|
|
83
|
+
function extractPasteText(raw, files) {
|
|
84
|
+
const text = raw.replace(/\uFFFC/g, "").trim();
|
|
85
|
+
if (text === "") return "";
|
|
86
|
+
const names = files.map((file) => file.name.replace(/^.*[/\\]/, "")).filter((name2) => name2.length >= 4);
|
|
87
|
+
if (names.length === 0) return text;
|
|
88
|
+
const tokens = text.split(/\s+/).filter(Boolean);
|
|
89
|
+
const referenced = (token) => names.some((name2) => token.toLowerCase().includes(name2.toLowerCase()));
|
|
90
|
+
const meaningful = tokens.filter((token) => !referenced(token));
|
|
91
|
+
return meaningful.length === 0 ? "" : text;
|
|
92
|
+
}
|
|
73
93
|
|
|
74
94
|
// src/store.ts
|
|
75
95
|
var items = [];
|
|
76
96
|
var sentBySession = /* @__PURE__ */ new Map();
|
|
77
|
-
var sentByTurn = /* @__PURE__ */ new Map();
|
|
78
97
|
var listeners = /* @__PURE__ */ new Set();
|
|
79
98
|
var generation = 0;
|
|
80
99
|
function emit() {
|
|
@@ -127,12 +146,6 @@ function archiveSent(sessionId) {
|
|
|
127
146
|
if (files.length > 0) emit();
|
|
128
147
|
return files;
|
|
129
148
|
}
|
|
130
|
-
function lastSent(sessionId) {
|
|
131
|
-
return sentBySession.get(sessionId) ?? [];
|
|
132
|
-
}
|
|
133
|
-
function filesForTurn(sessionId, turn) {
|
|
134
|
-
return sentByTurn.get(`${sessionId}:${turn}`) ?? lastSent(sessionId);
|
|
135
|
-
}
|
|
136
149
|
|
|
137
150
|
// src/styles.ts
|
|
138
151
|
var FR_CSS = `
|
|
@@ -676,6 +689,86 @@ function ComposerRail(props) {
|
|
|
676
689
|
}, host !== null && host.isConnected ? (0, import_react_dom2.createPortal)(rail, host) : null);
|
|
677
690
|
}
|
|
678
691
|
|
|
692
|
+
// src/vision.ts
|
|
693
|
+
function currentModelLabel() {
|
|
694
|
+
const buttons = document.querySelectorAll("button[aria-label]");
|
|
695
|
+
for (const button of buttons) {
|
|
696
|
+
const label = button.getAttribute("aria-label") ?? "";
|
|
697
|
+
if (/选择模型|select model|current model/i.test(label)) return label;
|
|
698
|
+
}
|
|
699
|
+
return "";
|
|
700
|
+
}
|
|
701
|
+
function insertComposerText(text) {
|
|
702
|
+
const active = document.activeElement;
|
|
703
|
+
const el = active instanceof HTMLTextAreaElement || active instanceof HTMLInputElement ? active : document.querySelector("[data-composer-card] textarea");
|
|
704
|
+
if (!(el instanceof HTMLTextAreaElement) && !(el instanceof HTMLInputElement)) return;
|
|
705
|
+
el.focus();
|
|
706
|
+
let inserted = false;
|
|
707
|
+
try {
|
|
708
|
+
inserted = document.execCommand("insertText", false, text);
|
|
709
|
+
} catch {
|
|
710
|
+
inserted = false;
|
|
711
|
+
}
|
|
712
|
+
if (inserted) return;
|
|
713
|
+
const proto = el instanceof HTMLTextAreaElement ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
|
|
714
|
+
const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
|
|
715
|
+
setter?.call(el, `${el.value}${text}`);
|
|
716
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
717
|
+
}
|
|
718
|
+
function pasteAccompanyingText(text) {
|
|
719
|
+
insertComposerText(text);
|
|
720
|
+
}
|
|
721
|
+
var modlensRouteAvailable = true;
|
|
722
|
+
async function probeModlensTakeover() {
|
|
723
|
+
if (!modlensRouteAvailable) return false;
|
|
724
|
+
const label = currentModelLabel();
|
|
725
|
+
try {
|
|
726
|
+
const res = await fetch(`/modlens/paste?model=${encodeURIComponent(label)}`, {
|
|
727
|
+
signal: AbortSignal.timeout(2e3)
|
|
728
|
+
});
|
|
729
|
+
if (res.status === 404) {
|
|
730
|
+
modlensRouteAvailable = false;
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
if (!res.ok) return false;
|
|
734
|
+
const body = await res.json();
|
|
735
|
+
return body.takeover === true;
|
|
736
|
+
} catch {
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
async function uploadModlensPaste(file) {
|
|
741
|
+
const res = await fetch("/modlens/paste", { method: "POST", body: await file.arrayBuffer() });
|
|
742
|
+
if (!res.ok) return void 0;
|
|
743
|
+
const body = await res.json();
|
|
744
|
+
return typeof body.path === "string" && body.path !== "" ? body.path : void 0;
|
|
745
|
+
}
|
|
746
|
+
async function deliverImages(sessionId, files) {
|
|
747
|
+
if (files.length === 0) return;
|
|
748
|
+
const takeover = await probeModlensTakeover();
|
|
749
|
+
if (!takeover) {
|
|
750
|
+
addImages(sessionId, files);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const paths = [];
|
|
754
|
+
const failed = [];
|
|
755
|
+
for (const file of files) {
|
|
756
|
+
try {
|
|
757
|
+
const path = await uploadModlensPaste(file);
|
|
758
|
+
if (path !== void 0) paths.push(path);
|
|
759
|
+
else failed.push(file);
|
|
760
|
+
} catch {
|
|
761
|
+
failed.push(file);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (paths.length === 0) {
|
|
765
|
+
addImages(sessionId, files);
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
if (failed.length > 0) addImages(sessionId, failed);
|
|
769
|
+
insertComposerText(`${paths.join(" ")} `);
|
|
770
|
+
}
|
|
771
|
+
|
|
679
772
|
// src/picker.tsx
|
|
680
773
|
function PaperclipButton(props) {
|
|
681
774
|
ensureStyles();
|
|
@@ -689,7 +782,9 @@ function PaperclipButton(props) {
|
|
|
689
782
|
event.currentTarget.value = "";
|
|
690
783
|
const sid = String(props.sessionId || currentSessionId());
|
|
691
784
|
if (files.length === 0) return;
|
|
692
|
-
intakeFiles(sid, files, (images) =>
|
|
785
|
+
intakeFiles(sid, files, (images) => {
|
|
786
|
+
void deliverImages(sid, images);
|
|
787
|
+
});
|
|
693
788
|
};
|
|
694
789
|
return (0, import_react5.createElement)(
|
|
695
790
|
"span",
|
|
@@ -713,97 +808,15 @@ function PaperclipButton(props) {
|
|
|
713
808
|
);
|
|
714
809
|
}
|
|
715
810
|
|
|
716
|
-
// src/tail.tsx
|
|
717
|
-
var import_react6 = require("react");
|
|
718
|
-
function basename(path) {
|
|
719
|
-
const at = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
720
|
-
return at === -1 ? path : path.slice(at + 1);
|
|
721
|
-
}
|
|
722
|
-
function ChipRow({ label, items: items2, open }) {
|
|
723
|
-
if (items2.length === 0) return null;
|
|
724
|
-
const shown = items2.slice(0, 6);
|
|
725
|
-
const hidden = items2.length - shown.length;
|
|
726
|
-
return (0, import_react6.createElement)(
|
|
727
|
-
"div",
|
|
728
|
-
{ className: "fr-tail" },
|
|
729
|
-
(0, import_react6.createElement)("span", { className: "fr-tail-label" }, label),
|
|
730
|
-
(0, import_react6.createElement)(
|
|
731
|
-
"div",
|
|
732
|
-
{ className: "fr-tail-row" },
|
|
733
|
-
...shown.map((item) => (0, import_react6.createElement)("button", {
|
|
734
|
-
key: item.path,
|
|
735
|
-
type: "button",
|
|
736
|
-
className: "fr-chip",
|
|
737
|
-
title: item.path,
|
|
738
|
-
onClick: () => open(item.path)
|
|
739
|
-
}, item.name)),
|
|
740
|
-
hidden > 0 ? (0, import_react6.createElement)("span", { className: "fr-more" }, `+ ${hidden} \u4E2A\u6587\u4EF6`) : null
|
|
741
|
-
)
|
|
742
|
-
);
|
|
743
|
-
}
|
|
744
|
-
function UploadedTail(props) {
|
|
745
|
-
ensureStyles();
|
|
746
|
-
const sessionId = String(props.sessionId ?? "");
|
|
747
|
-
const [uploaded, setUploaded] = (0, import_react6.useState)(
|
|
748
|
-
props.matched?.uploaded ?? (props.turn !== void 0 ? filesForTurn(sessionId, props.turn) : lastSent(sessionId))
|
|
749
|
-
);
|
|
750
|
-
(0, import_react6.useEffect)(() => {
|
|
751
|
-
if (props.matched?.uploaded) {
|
|
752
|
-
setUploaded(props.matched.uploaded);
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
let cancelled = false;
|
|
756
|
-
void fetch(`/plugins/file-native/last?sessionId=${encodeURIComponent(sessionId)}`).then((res) => res.json()).then((body) => {
|
|
757
|
-
if (!cancelled && Array.isArray(body.files) && body.files.length > 0) setUploaded(body.files);
|
|
758
|
-
}).catch(() => {
|
|
759
|
-
});
|
|
760
|
-
return () => {
|
|
761
|
-
cancelled = true;
|
|
762
|
-
};
|
|
763
|
-
}, [sessionId, props.matched, props.turn]);
|
|
764
|
-
const produced = props.matched?.produced ?? [];
|
|
765
|
-
if (uploaded.length === 0 && produced.length === 0) return null;
|
|
766
|
-
const open = props.openFile ?? ((path) => {
|
|
767
|
-
window.open(path, "_blank");
|
|
768
|
-
});
|
|
769
|
-
return (0, import_react6.createElement)(
|
|
770
|
-
"div",
|
|
771
|
-
{ "data-file-native-tail": true },
|
|
772
|
-
(0, import_react6.createElement)(ChipRow, {
|
|
773
|
-
label: "\u9644\u4EF6",
|
|
774
|
-
items: uploaded.map((file) => ({ name: file.name, path: file.relPath })),
|
|
775
|
-
open
|
|
776
|
-
}),
|
|
777
|
-
(0, import_react6.createElement)(ChipRow, {
|
|
778
|
-
label: "\u4EA7\u7269",
|
|
779
|
-
items: produced.map((path) => ({ name: basename(path), path })),
|
|
780
|
-
open
|
|
781
|
-
})
|
|
782
|
-
);
|
|
783
|
-
}
|
|
784
|
-
function producedPathsOf(owner2) {
|
|
785
|
-
const data = owner2.turn?.data?.get?.("deliverables");
|
|
786
|
-
const seq = owner2.seq ?? Number.POSITIVE_INFINITY;
|
|
787
|
-
if (!data?.produced) return [];
|
|
788
|
-
const paths = [];
|
|
789
|
-
const seen = /* @__PURE__ */ new Set();
|
|
790
|
-
for (const item of data.produced) {
|
|
791
|
-
if (item.seq > seq || seen.has(item.path)) continue;
|
|
792
|
-
seen.add(item.path);
|
|
793
|
-
paths.push(item.path);
|
|
794
|
-
}
|
|
795
|
-
return paths;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
811
|
// src/context.tsx
|
|
799
|
-
var
|
|
812
|
+
var import_react6 = require("react");
|
|
800
813
|
var import_react_dom3 = require("react-dom");
|
|
801
814
|
function isFileNoticeRow(el) {
|
|
802
815
|
const text = el.textContent ?? "";
|
|
803
816
|
return text.includes("\u{1F4CE} \u9644\u4EF6") && text.includes("\u4E2A\u6587\u4EF6");
|
|
804
817
|
}
|
|
805
818
|
function FileCard({ file, onOpen }) {
|
|
806
|
-
return (0,
|
|
819
|
+
return (0, import_react6.createElement)(
|
|
807
820
|
"button",
|
|
808
821
|
{
|
|
809
822
|
type: "button",
|
|
@@ -811,12 +824,12 @@ function FileCard({ file, onOpen }) {
|
|
|
811
824
|
title: file.relPath,
|
|
812
825
|
onClick: () => onOpen?.(file)
|
|
813
826
|
},
|
|
814
|
-
(0,
|
|
815
|
-
(0,
|
|
827
|
+
(0, import_react6.createElement)("span", { className: "fr-ext" }, extOf(file.name)),
|
|
828
|
+
(0, import_react6.createElement)(
|
|
816
829
|
"span",
|
|
817
830
|
{ className: "fr-copy" },
|
|
818
|
-
(0,
|
|
819
|
-
file.size > 0 ? (0,
|
|
831
|
+
(0, import_react6.createElement)("span", { className: "fr-name" }, file.name),
|
|
832
|
+
file.size > 0 ? (0, import_react6.createElement)("span", { className: "fr-meta" }, formatSize(file.size)) : null
|
|
820
833
|
)
|
|
821
834
|
);
|
|
822
835
|
}
|
|
@@ -859,11 +872,11 @@ function placeHostInRow(notice) {
|
|
|
859
872
|
}
|
|
860
873
|
function MessageCards(props) {
|
|
861
874
|
ensureStyles();
|
|
862
|
-
const [hosted, setHosted] = (0,
|
|
863
|
-
const sessionKey = (0,
|
|
864
|
-
const lastCount = (0,
|
|
865
|
-
const cachedFiles = (0,
|
|
866
|
-
(0,
|
|
875
|
+
const [hosted, setHosted] = (0, import_react6.useState)([]);
|
|
876
|
+
const sessionKey = (0, import_react6.useRef)("");
|
|
877
|
+
const lastCount = (0, import_react6.useRef)(-1);
|
|
878
|
+
const cachedFiles = (0, import_react6.useRef)(null);
|
|
879
|
+
(0, import_react6.useEffect)(() => {
|
|
867
880
|
if (sessionKey.current !== (props.sessionId ?? "")) {
|
|
868
881
|
sessionKey.current = props.sessionId ?? "";
|
|
869
882
|
lastCount.current = -1;
|
|
@@ -923,14 +936,14 @@ function MessageCards(props) {
|
|
|
923
936
|
};
|
|
924
937
|
}, [props.sessionId]);
|
|
925
938
|
if (hosted.length === 0) return null;
|
|
926
|
-
return (0,
|
|
939
|
+
return (0, import_react6.createElement)(
|
|
927
940
|
"span",
|
|
928
941
|
{ "aria-hidden": true },
|
|
929
942
|
...hosted.map((item, i) => (0, import_react_dom3.createPortal)(
|
|
930
|
-
(0,
|
|
943
|
+
(0, import_react6.createElement)(
|
|
931
944
|
"div",
|
|
932
945
|
{ className: "fr-msg-row-inner" },
|
|
933
|
-
...item.files.map((file) => (0,
|
|
946
|
+
...item.files.map((file) => (0, import_react6.createElement)(FileCard, { key: file.relPath, file, onOpen: props.onOpen }))
|
|
934
947
|
),
|
|
935
948
|
item.host,
|
|
936
949
|
`file-native-cards-${i}`
|
|
@@ -1006,9 +1019,16 @@ function installFileIntercept(handlers) {
|
|
|
1006
1019
|
const fromItems = Array.from(event.clipboardData?.items ?? []).filter((item) => item.kind === "file").map((item) => item.getAsFile()).filter((file) => file !== null);
|
|
1007
1020
|
const files = fromList.length > 0 ? fromList : fromItems;
|
|
1008
1021
|
if (files.length === 0) return;
|
|
1022
|
+
const plan = planPaste(files);
|
|
1023
|
+
if (plan.action === "yield") return;
|
|
1009
1024
|
event.preventDefault();
|
|
1010
|
-
event.
|
|
1011
|
-
handlers.onFiles(
|
|
1025
|
+
event.stopImmediatePropagation();
|
|
1026
|
+
if (plan.cards.length > 0) handlers.onFiles(plan.cards);
|
|
1027
|
+
if (plan.nativeImages.length > 0) handlers.onImages?.(plan.nativeImages);
|
|
1028
|
+
if (handlers.onText !== void 0) {
|
|
1029
|
+
const kept = extractPasteText(event.clipboardData?.getData("text/plain") ?? "", files);
|
|
1030
|
+
if (kept !== "") handlers.onText(kept);
|
|
1031
|
+
}
|
|
1012
1032
|
};
|
|
1013
1033
|
document.addEventListener("dragenter", onDragEnter, true);
|
|
1014
1034
|
document.addEventListener("dragover", onDragOver, true);
|
|
@@ -1028,13 +1048,13 @@ function installFileIntercept(handlers) {
|
|
|
1028
1048
|
|
|
1029
1049
|
// src/client.ts
|
|
1030
1050
|
var name = "file-native";
|
|
1031
|
-
var inject = ["slots"
|
|
1051
|
+
var inject = ["slots"];
|
|
1032
1052
|
function RailSlot(props) {
|
|
1033
1053
|
ensureStyles();
|
|
1034
1054
|
const sessionId = resolveSessionId(props);
|
|
1035
1055
|
rememberSessionId(sessionId);
|
|
1036
|
-
const tokenRef = (0,
|
|
1037
|
-
(0,
|
|
1056
|
+
const tokenRef = (0, import_react7.useRef)(0);
|
|
1057
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
1038
1058
|
tokenRef.current = setLiveOwner({
|
|
1039
1059
|
attachments: props.attachments,
|
|
1040
1060
|
canAcceptDrop: props.canAcceptDrop,
|
|
@@ -1044,7 +1064,7 @@ function RailSlot(props) {
|
|
|
1044
1064
|
dropLimits: props.dropLimits
|
|
1045
1065
|
});
|
|
1046
1066
|
});
|
|
1047
|
-
(0,
|
|
1067
|
+
(0, import_react7.useLayoutEffect)(() => () => clearLiveOwner(tokenRef.current), []);
|
|
1048
1068
|
return null;
|
|
1049
1069
|
}
|
|
1050
1070
|
var chatCardOpener;
|
|
@@ -1055,7 +1075,7 @@ function PickerSlot(props) {
|
|
|
1055
1075
|
const inputActions = props.inputActions;
|
|
1056
1076
|
const submitAction = inputActions?.submit;
|
|
1057
1077
|
const kitSig = `inputActions=${props.inputActions ? "y" : "n"} useSession=${props.useSession ? "y" : "n"}`;
|
|
1058
|
-
(0,
|
|
1078
|
+
(0, import_react7.useEffect)(() => {
|
|
1059
1079
|
console.info(`[file-native] kit: ${kitSig}`);
|
|
1060
1080
|
}, [kitSig]);
|
|
1061
1081
|
const submitWithFiles = (ev, sid) => {
|
|
@@ -1071,7 +1091,7 @@ function PickerSlot(props) {
|
|
|
1071
1091
|
void fetch(`/plugins/file-native/arm?sessionId=${encodeURIComponent(sid)}`, { method: "POST" }).catch(() => {
|
|
1072
1092
|
}).then(() => submitFn());
|
|
1073
1093
|
};
|
|
1074
|
-
(0,
|
|
1094
|
+
(0, import_react7.useEffect)(() => {
|
|
1075
1095
|
const onKeyDown = (ev) => {
|
|
1076
1096
|
if (ev.key !== "Enter" || ev.shiftKey || ev.isComposing || ev.altKey || ev.ctrlKey || ev.metaKey) return;
|
|
1077
1097
|
if (running) return;
|
|
@@ -1082,7 +1102,7 @@ function PickerSlot(props) {
|
|
|
1082
1102
|
document.addEventListener("keydown", onKeyDown, true);
|
|
1083
1103
|
return () => document.removeEventListener("keydown", onKeyDown, true);
|
|
1084
1104
|
}, [running, inputActions, sessionId]);
|
|
1085
|
-
(0,
|
|
1105
|
+
(0, import_react7.useEffect)(() => {
|
|
1086
1106
|
const onClick = (ev) => {
|
|
1087
1107
|
if (running) return;
|
|
1088
1108
|
const button = ev.target instanceof Element ? ev.target.closest("button") : null;
|
|
@@ -1095,34 +1115,28 @@ function PickerSlot(props) {
|
|
|
1095
1115
|
document.addEventListener("click", onClick, true);
|
|
1096
1116
|
return () => document.removeEventListener("click", onClick, true);
|
|
1097
1117
|
}, [running, inputActions, sessionId]);
|
|
1098
|
-
return (0,
|
|
1118
|
+
return (0, import_react7.createElement)(
|
|
1099
1119
|
"span",
|
|
1100
1120
|
null,
|
|
1101
|
-
(0,
|
|
1102
|
-
(0,
|
|
1121
|
+
(0, import_react7.createElement)(PaperclipButton, { sessionId }),
|
|
1122
|
+
(0, import_react7.createElement)(MessageCards, { sessionId, onOpen: (file) => chatCardOpener?.(sessionId || currentSessionId(), file.relPath, file.name) })
|
|
1103
1123
|
);
|
|
1104
1124
|
}
|
|
1105
|
-
function TailSlot(props) {
|
|
1106
|
-
return (0, import_react8.createElement)(UploadedTail, {
|
|
1107
|
-
sessionId: props.sessionId,
|
|
1108
|
-
openFile: props.openFile,
|
|
1109
|
-
matched: props.matched ?? null,
|
|
1110
|
-
turn: props.turn?.turn
|
|
1111
|
-
});
|
|
1112
|
-
}
|
|
1113
|
-
function selectTail(owner2) {
|
|
1114
|
-
const produced = producedPathsOf(owner2);
|
|
1115
|
-
if (produced.length === 0) return null;
|
|
1116
|
-
return { uploaded: [], produced };
|
|
1117
|
-
}
|
|
1118
1125
|
function installLiveIntercept() {
|
|
1119
1126
|
return installFileIntercept({
|
|
1120
1127
|
canAccept: () => true,
|
|
1121
1128
|
onDepth: (depth) => setDragDepth(depth),
|
|
1122
1129
|
onFiles: (files) => {
|
|
1123
1130
|
const sessionId = currentSessionId();
|
|
1124
|
-
intakeFiles(sessionId, files, (images) =>
|
|
1125
|
-
|
|
1131
|
+
intakeFiles(sessionId, files, (images) => {
|
|
1132
|
+
void deliverImages(sessionId, images);
|
|
1133
|
+
});
|
|
1134
|
+
},
|
|
1135
|
+
onImages: (files) => {
|
|
1136
|
+
const sessionId = currentSessionId();
|
|
1137
|
+
void deliverImages(sessionId, files);
|
|
1138
|
+
},
|
|
1139
|
+
onText: (text) => pasteAccompanyingText(text)
|
|
1126
1140
|
});
|
|
1127
1141
|
}
|
|
1128
1142
|
var INSTANCE = (() => {
|
|
@@ -1134,7 +1148,12 @@ var INSTANCE = (() => {
|
|
|
1134
1148
|
})();
|
|
1135
1149
|
function apply(ctx) {
|
|
1136
1150
|
ctx.effect(() => installLiveIntercept(), "file-native: document intercept");
|
|
1137
|
-
|
|
1151
|
+
let sidebar;
|
|
1152
|
+
try {
|
|
1153
|
+
sidebar = ctx.betterSidebar;
|
|
1154
|
+
} catch {
|
|
1155
|
+
sidebar = void 0;
|
|
1156
|
+
}
|
|
1138
1157
|
if (sidebar !== void 0 && sidebar.features.includes("openFile")) {
|
|
1139
1158
|
chatCardOpener = (sid, relPath, name2) => sidebar.openFile({ sessionId: sid }, relPath, name2);
|
|
1140
1159
|
} else {
|
|
@@ -1164,11 +1183,6 @@ function apply(ctx) {
|
|
|
1164
1183
|
order: 0,
|
|
1165
1184
|
priority: base
|
|
1166
1185
|
}, PickerSlot));
|
|
1167
|
-
safeInject("conversation.chat.turnTail", () => ctx.slots.register({
|
|
1168
|
-
name: "conversation.chat.turnTail",
|
|
1169
|
-
select: selectTail,
|
|
1170
|
-
priority: base
|
|
1171
|
-
}, TailSlot));
|
|
1172
1186
|
console.info(`[file-native] client loaded (instance #${INSTANCE})`);
|
|
1173
1187
|
}
|
|
1174
1188
|
|