@wszhoho/dsh-file-attachment 0.5.7 → 0.5.9
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 +44 -9
- package/lib/index.js +14 -14
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -118,6 +118,38 @@ window.__ModuleLoader__.load({
|
|
|
118
118
|
hijackedInputs.add(el)
|
|
119
119
|
el.addEventListener('change', onNativeFileChange)
|
|
120
120
|
}
|
|
121
|
+
// ---- 移动端双📎兼容:dsh-web-mobile 向 conversation.input.left 注入 file-upload 回形针,
|
|
122
|
+
// 其设计前提是 host(0.1.6-alpha.2+)已删除 composer 原生📎;在 0.1.5-rc.x 上该前提不成立——
|
|
123
|
+
// 该代 host 仍渲染原生📎(即 hidden fileInput 的前邻 button),移动端窄屏下两枚并排出现
|
|
124
|
+
//(PC 宽屏被 dsh-web-mobile 的 pointer:fine 隐藏规则覆盖,故仅移动端可见)。
|
|
125
|
+
// 此处按 host 原生📎是否存在对调:原生📎在 → 内联 display:none!important 隐藏 mobile 注入的
|
|
126
|
+
//(压过其 display:grid!important);原生📎不在(0.1.6+)→ 移除内联声明交还 CSS,保留为唯一入口。
|
|
127
|
+
// 两枚按钮点击后都触发 host 的 hidden fileInput,change 已被本插件劫持走 runBatch,功能不受影响。
|
|
128
|
+
function cardHasNativeClip(card) {
|
|
129
|
+
let inputs
|
|
130
|
+
try { inputs = card.querySelectorAll('input[type="file"], input[type=file]') } catch (err) { return false }
|
|
131
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
132
|
+
const inp = inputs[i]
|
|
133
|
+
if (typeof inp.hidden !== 'undefined' && !inp.hidden) continue // 只认 host 的 hidden fileInput
|
|
134
|
+
let prev = inp.previousElementSibling
|
|
135
|
+
while (prev !== null && prev.tagName !== 'BUTTON') prev = prev.previousElementSibling
|
|
136
|
+
if (prev !== null) return true
|
|
137
|
+
}
|
|
138
|
+
return false
|
|
139
|
+
}
|
|
140
|
+
function reconcileMobileFileUpload() {
|
|
141
|
+
let cards
|
|
142
|
+
try { cards = Array.from(document.querySelectorAll('[data-composer-card]')) } catch (err) { cards = [] }
|
|
143
|
+
for (let i = 0; i < cards.length; i++) {
|
|
144
|
+
const hideMobile = cardHasNativeClip(cards[i])
|
|
145
|
+
let btns
|
|
146
|
+
try { btns = cards[i].querySelectorAll('[data-mobile-nav="file-upload"]') } catch (err) { btns = [] }
|
|
147
|
+
for (let j = 0; j < btns.length; j++) {
|
|
148
|
+
const curHidden = btns[j].style.display === 'none'
|
|
149
|
+
if (hideMobile !== curHidden) btns[j].style.setProperty('display', hideMobile ? 'none' : '', 'important')
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
121
153
|
function installNativeAttachHijack() {
|
|
122
154
|
if (typeof document === 'undefined' || document.body === null || document.body === undefined) return
|
|
123
155
|
let body
|
|
@@ -127,6 +159,7 @@ window.__ModuleLoader__.load({
|
|
|
127
159
|
let all = []
|
|
128
160
|
try { all = Array.from(body.querySelectorAll(nativeFileInputSelector)) } catch (err) { all = [] }
|
|
129
161
|
for (let i = 0; i < all.length; i++) hijackFileInput(all[i])
|
|
162
|
+
reconcileMobileFileUpload() // 双📎对调:host 原生📎在则隐藏 dsh-web-mobile 注入的
|
|
130
163
|
}
|
|
131
164
|
scan()
|
|
132
165
|
const mo = new MutationObserver(() => scan())
|
|
@@ -772,7 +805,6 @@ window.__ModuleLoader__.load({
|
|
|
772
805
|
bridge.input = input
|
|
773
806
|
bridge.sessionId = sessionId
|
|
774
807
|
bridge.mounted = true
|
|
775
|
-
if (typeof console !== 'undefined' && console.log) console.log('[dsh-file-attachment] FaBridge mounted', { sessionId, shell: shell === null ? 'null' : 'ok', addImages: typeof addImages })
|
|
776
808
|
return () => {
|
|
777
809
|
bridge.shell = null
|
|
778
810
|
bridge.addImages = null
|
|
@@ -1091,6 +1123,7 @@ window.__ModuleLoader__.load({
|
|
|
1091
1123
|
|
|
1092
1124
|
// 设置页扩展名分组:标题 + 可删 tag + 输入框 + 添加按钮
|
|
1093
1125
|
function FaExtGroup(props) {
|
|
1126
|
+
const [hover, setHover] = react.useState(false)
|
|
1094
1127
|
const list = props.list
|
|
1095
1128
|
const setList = props.setList
|
|
1096
1129
|
const input = props.input
|
|
@@ -1110,7 +1143,7 @@ window.__ModuleLoader__.load({
|
|
|
1110
1143
|
style: { cursor: 'pointer', marginLeft: '4px', opacity: 0.55 },
|
|
1111
1144
|
}, '×')
|
|
1112
1145
|
))
|
|
1113
|
-
return react.createElement('div', { style: { marginBottom: '18px' } },
|
|
1146
|
+
return react.createElement('div', { onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), style: { marginBottom: '18px', padding: '14px 16px', borderRadius: '12px', background: hover ? 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.06))' : 'var(--dsw-alias-bg-layer-1, rgba(128,128,128,0.04))', border: `1px solid ${hover ? 'var(--dsw-alias-label-dimmed, var(--dsw-alias-border-l2, rgba(128,128,128,0.3)))' : 'var(--dsw-alias-border-l1, rgba(128,128,128,0.14))'}`, boxShadow: hover ? '0 2px 8px rgba(0,0,0,0.05)' : 'none', transition: 'border-color 0.16s, background 0.16s, box-shadow 0.16s' } },
|
|
1114
1147
|
react.createElement('div', { style: { fontWeight: 600, marginBottom: '8px', fontSize: '13px' } }, props.title),
|
|
1115
1148
|
react.createElement('div', { style: { marginBottom: '8px', minHeight: '22px' } }, tags),
|
|
1116
1149
|
react.createElement('div', { style: { display: 'flex', gap: '8px' } },
|
|
@@ -1149,6 +1182,7 @@ window.__ModuleLoader__.load({
|
|
|
1149
1182
|
const [cfgIn, setCfgIn] = react.useState('')
|
|
1150
1183
|
const [saved, setSaved] = react.useState(false)
|
|
1151
1184
|
const [saving, setSaving] = react.useState(false)
|
|
1185
|
+
const [vlmHover, setVlmHover] = react.useState(false)
|
|
1152
1186
|
react.useEffect(() => {
|
|
1153
1187
|
// 挂载时载入持久化配置到编辑副本(幂等,复用 loadConfig Promise)
|
|
1154
1188
|
let live = true
|
|
@@ -1193,11 +1227,11 @@ window.__ModuleLoader__.load({
|
|
|
1193
1227
|
} finally { setSaving(false) }
|
|
1194
1228
|
}
|
|
1195
1229
|
return react.createElement('div', { style: { padding: '16px', maxWidth: '680px' } },
|
|
1196
|
-
react.createElement('div', { style: { marginBottom: '16px', fontSize: '13px', color: 'rgba(128,128,128,0.
|
|
1230
|
+
react.createElement('div', { style: { marginBottom: '16px', fontSize: '13px', lineHeight: '1.5', color: 'var(--dsw-alias-label-secondary, rgba(128,128,128,0.95))' } }, t('settings.hint')),
|
|
1197
1231
|
react.createElement(FaExtGroup, { title: t('doc.title'), list: doc, setList: setDoc, input: docIn, setInput: setDocIn }),
|
|
1198
1232
|
react.createElement(FaExtGroup, { title: t('code.title'), list: code, setList: setCode, input: codeIn, setInput: setCodeIn }),
|
|
1199
1233
|
react.createElement(FaExtGroup, { title: t('config.title'), list: config, setList: setConfig, input: cfgIn, setInput: setCfgIn }),
|
|
1200
|
-
react.createElement('div', { style: { margin: '16px 0', padding: '
|
|
1234
|
+
react.createElement('div', { onMouseEnter: () => setVlmHover(true), onMouseLeave: () => setVlmHover(false), style: { margin: '16px 0', padding: '14px 16px', borderRadius: '12px', background: vlmHover ? 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.06))' : 'var(--dsw-alias-bg-layer-1, rgba(128,128,128,0.04))', border: `1px solid ${vlmHover ? 'var(--dsw-alias-label-dimmed, var(--dsw-alias-border-l2, rgba(128,128,128,0.3)))' : 'var(--dsw-alias-border-l1, rgba(128,128,128,0.14))'}`, boxShadow: vlmHover ? '0 2px 8px rgba(0,0,0,0.05)' : 'none', transition: 'border-color 0.16s, background 0.16s, box-shadow 0.16s' } },
|
|
1201
1235
|
react.createElement('div', { style: { marginBottom: '8px', fontSize: '13px', fontWeight: '600', color: 'var(--dsw-alias-label-primary, inherit)' } }, t('vlm.title')),
|
|
1202
1236
|
react.createElement('div', { style: { marginBottom: '10px', fontSize: '12px', color: 'var(--dsw-alias-label-tertiary, rgba(128,128,128,0.9))' } }, t('vlm.hint')),
|
|
1203
1237
|
react.createElement('div', { style: { display: 'grid', gridTemplateColumns: '110px 1fr', gap: '8px 10px', alignItems: 'center' } },
|
|
@@ -1237,11 +1271,12 @@ window.__ModuleLoader__.load({
|
|
|
1237
1271
|
const [open, setOpen] = react.useState(false)
|
|
1238
1272
|
const [hover, setHover] = react.useState(false)
|
|
1239
1273
|
const [version, setVersion] = react.useState(null)
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
}
|
|
1274
|
+
// 【已屏蔽 /version 拉取,保留代码以备后用】
|
|
1275
|
+
// react.useEffect(() => {
|
|
1276
|
+
// fetch('/dsh-file-attachment/version').then(r => r.json()).then(d => {
|
|
1277
|
+
// if (d.ok && d.version) setVersion(d.version)
|
|
1278
|
+
// }).catch(() => { /* ignore */ })
|
|
1279
|
+
// }, [])
|
|
1245
1280
|
function t(key) {
|
|
1246
1281
|
if (ctxRef !== null && ctxRef.locale !== void 0 && typeof ctxRef.locale.bind === 'function') {
|
|
1247
1282
|
try { return ctxRef.locale.bind('dsh-file-attachment')(key) } catch (err) { /* 回退 key */ }
|
package/lib/index.js
CHANGED
|
@@ -370,18 +370,19 @@ function registerRoutes(ctx) {
|
|
|
370
370
|
return
|
|
371
371
|
}
|
|
372
372
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
373
|
+
// 【已屏蔽 /version 路由,保留代码以备后用】
|
|
374
|
+
// if (req.method === 'GET' && pathname === '/dsh-file-attachment/version') {
|
|
375
|
+
// try {
|
|
376
|
+
// const pkgPath = fileURLToPath(new URL('../package.json', import.meta.url))
|
|
377
|
+
// const raw = await readFile(pkgPath, 'utf8')
|
|
378
|
+
// const pkg = JSON.parse(raw)
|
|
379
|
+
// json(res, { ok: true, version: pkg.version || '0.0.0' })
|
|
380
|
+
// return
|
|
381
|
+
// } catch {
|
|
382
|
+
// json(res, { ok: false, version: 'unknown' }, 500)
|
|
383
|
+
// return
|
|
384
|
+
// }
|
|
385
|
+
// }
|
|
385
386
|
if (req.method === 'GET' && pathname === '/dsh-file-attachment/config') {
|
|
386
387
|
json(res, { ok: true, value: await readConfig() })
|
|
387
388
|
return
|
|
@@ -488,9 +489,8 @@ function registerTools(ctx) {
|
|
|
488
489
|
},
|
|
489
490
|
},
|
|
490
491
|
render: (_args, value) => {
|
|
491
|
-
const thinkingText = (value && value.thinkingType === 'enabled') ? '开启' : '关闭'
|
|
492
492
|
if (value && value.ok === true && typeof value.description === 'string' && value.description !== '') {
|
|
493
|
-
return [{ type: 'text', text: '图片识别结果:' + value.description
|
|
493
|
+
return [{ type: 'text', text: '图片识别结果:' + value.description }]
|
|
494
494
|
}
|
|
495
495
|
return [{ type: 'text', text: (value && value.ok === true) ? '(图片已识别)' : '(图片识别失败)' }]
|
|
496
496
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wszhoho/dsh-file-attachment",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.9",
|
|
4
4
|
"description": "文件附件:拖拽/粘贴/上传文件(支持多文件);图片与文档统一落盘到 .dsh-file-attachment 并以 @绝对路径 引用发送(文本模型可正常使用);输入框内联显示图片缩略图预览与文件条目,聊天区图片同样渲染为可点击放大的缩略图、文件保持芯片样式;非多模态模型下图片自动调用可配置 VLM 识别生成中文描述回填草稿;文档/代码/配置文件可上传类型可在设置页配置;支持 PC 与移动端浏览器",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|