New:Socket for Asana Is Now Available.Learn more
Get Started

dsh-vision-recognizer

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dsh-vision-recognizer - npm Package Compare versions

Comparing version
0.1.0
to
0.1.1
+39
-9
client/client.js

@@ -26,2 +26,3 @@ window.__ModuleLoader__.load({ id: "dsh-vision-recognizer", factory: (require) => {

providerLabel: '供应商',
providerHint: '选择图片转译使用的视觉模型供应商。可在下方覆盖模型与接口地址。',
apiKeyLabel: 'API Key',

@@ -32,9 +33,15 @@ apiKeyHint: '留空则读取该供应商的环境变量;本地 Ollama 无需 Key。',

modelLabel: '模型',
modelHint: '视觉模型 ID。留空使用该供应商的默认模型。',
modelPlaceholder: '留空使用供应商默认',
baseUrlLabel: '接口地址',
baseUrlHint: 'OpenAI 兼容接口地址(…/chat/completions 会自动拼接)。留空使用该供应商的默认地址。',
baseUrlPlaceholder: '留空使用供应商默认',
maxTokensLabel: '最大输出 token',
maxTokensHint: '视觉模型单次输出的 token 上限,越大转译越详尽、也越贵。',
timeoutLabel: '超时(毫秒)',
timeoutHint: '请求超时时间。本地/匿名端点会被强制限制在 20 秒内。',
markerLabel: '转译标记',
markerHint: '转译文本的前缀标记,DeepSeek 靠它区分「这是图片转译的内容」。默认 [图片转译]。',
autoOllamaLabel: '自动探测本地 Ollama',
autoOllamaHint: '启动时探测本机 http://localhost:11434 的 Ollama,检测到则作为兜底——图片不出本机、无需 Key。',
save: '保存',

@@ -53,2 +60,3 @@ saving: '保存中…',

providerLabel: 'Provider',
providerHint: 'Vision provider used to transcribe attached images. Model and endpoint can be overridden below.',
apiKeyLabel: 'API key',

@@ -59,9 +67,15 @@ apiKeyHint: 'Leave blank to read the provider env var; local Ollama needs none.',

modelLabel: 'Model',
modelHint: 'Vision model id. Leave blank for the provider default.',
modelPlaceholder: 'Leave blank for the provider default',
baseUrlLabel: 'Endpoint',
baseUrlHint: 'OpenAI-compatible endpoint (…/chat/completions is appended). Leave blank for the provider default.',
baseUrlPlaceholder: 'Leave blank for the provider default',
maxTokensLabel: 'Max output tokens',
maxTokensHint: 'Output token cap for the vision model; larger is more thorough but costs more.',
timeoutLabel: 'Timeout (ms)',
timeoutHint: 'Request timeout. Local/anonymous endpoints are hard-capped at 20s.',
markerLabel: 'Transcription marker',
markerHint: 'Text prefix prepended to each transcription, so DeepSeek can tell image transcriptions apart. Default [图片转译].',
autoOllamaLabel: 'Auto-detect local Ollama',
autoOllamaHint: 'Probe local Ollama at http://localhost:11434 at startup and use it as a fallback — images never leave your machine and no key is needed.',
save: 'Save',

@@ -97,2 +111,5 @@ saving: 'Saving…',

.vrg-status.err{color:var(--dsw-alias-state-error-primary,#dc2626)}
.vrg-tip{position:relative;display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:99px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);color:var(--dsw-alias-label-tertiary,#9ca3af);font-size:10px;font-weight:600;line-height:1;cursor:help;margin-left:6px;vertical-align:middle;user-select:none}
.vrg-tip .vrg-tiptext{position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%) translateY(-2px);background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;padding:8px 10px;font-size:12px;font-weight:400;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328);width:max-content;max-width:280px;white-space:normal;text-align:left;z-index:60;opacity:0;pointer-events:none;transition:opacity .12s ease,transform .12s ease;box-shadow:0 6px 20px rgba(0,0,0,.14)}
.vrg-tip:hover .vrg-tiptext,.vrg-tip:focus-visible .vrg-tiptext{opacity:1;transform:translateX(-50%) translateY(0)}
`

@@ -109,2 +126,9 @@

/** A small "?" circle whose hover/focus reveals a tooltip explaining the field. */
function HelpTip({ text }) {
return h('span', { className: 'vrg-tip', tabIndex: 0, 'aria-label': text },
'?',
h('span', { className: 'vrg-tiptext' }, text))
}
function VisionPanel({ t, locale }) {

@@ -168,3 +192,7 @@ const [data, setData] = useState(null)

})
.then((res) => res.json().then((body) => ({ status: res.status, body })))
.then((res) => res.text().then((text) => {
let body = {}
try { body = text ? JSON.parse(text) : {} } catch { body = { error: text.slice(0, 200) } }
return { status: res.status, body }
}))
.then(({ status: httpStatus, body }) => {

@@ -200,3 +228,3 @@ if (httpStatus === 200 && body.ok) {

h('div', { className: 'vrg-field' },
h('label', { className: 'vrg-label' }, t('providerLabel')),
h('label', { className: 'vrg-label' }, t('providerLabel'), h(HelpTip, { text: t('providerHint') })),
h('select', {

@@ -212,3 +240,4 @@ className: 'vrg-input',

h('span', { className: 'vrg-badge' + (form.apiKeyConfigured ? ' on' : '') },
form.apiKeyConfigured ? t('apiKeyConfigured') : t('apiKeyUnset'))),
form.apiKeyConfigured ? t('apiKeyConfigured') : t('apiKeyUnset')),
h(HelpTip, { text: t('apiKeyHint') })),
h('input', {

@@ -225,3 +254,3 @@ className: 'vrg-input',

h('div', { className: 'vrg-field' },
h('label', { className: 'vrg-label' }, t('modelLabel')),
h('label', { className: 'vrg-label' }, t('modelLabel'), h(HelpTip, { text: t('modelHint') })),
h('input', {

@@ -234,3 +263,3 @@ className: 'vrg-input',

h('div', { className: 'vrg-field' },
h('label', { className: 'vrg-label' }, t('baseUrlLabel')),
h('label', { className: 'vrg-label' }, t('baseUrlLabel'), h(HelpTip, { text: t('baseUrlHint') })),
h('input', {

@@ -244,3 +273,3 @@ className: 'vrg-input',

h('div', { className: 'vrg-field' },
h('label', { className: 'vrg-label' }, t('maxTokensLabel')),
h('label', { className: 'vrg-label' }, t('maxTokensLabel'), h(HelpTip, { text: t('maxTokensHint') })),
h('input', {

@@ -253,3 +282,3 @@ className: 'vrg-input',

h('div', { className: 'vrg-field' },
h('label', { className: 'vrg-label' }, t('timeoutLabel')),
h('label', { className: 'vrg-label' }, t('timeoutLabel'), h(HelpTip, { text: t('timeoutHint') })),
h('input', {

@@ -262,3 +291,3 @@ className: 'vrg-input',

h('div', { className: 'vrg-field' },
h('label', { className: 'vrg-label' }, t('markerLabel')),
h('label', { className: 'vrg-label' }, t('markerLabel'), h(HelpTip, { text: t('markerHint') })),
h('input', {

@@ -275,3 +304,4 @@ className: 'vrg-input',

}),
t('autoOllamaLabel')),
t('autoOllamaLabel'),
h(HelpTip, { text: t('autoOllamaHint') })),
form.provider === 'custom' && h('p', { className: 'vrg-hint' }, t('requiredHint')),

@@ -278,0 +308,0 @@ h('div', { className: 'vrg-actions' },

+17
-27

@@ -157,12 +157,10 @@ /**

const disposers = [
hostCtx.webServer.register({
kind: 'exact',
path: '/dsh-vision-recognizer/config',
handler: async (request, response) => {
if (request.method !== 'GET') {
response.writeHead(405, { allow: 'GET' });
response.end();
return;
}
// Single exact route; the handler dispatches by HTTP method. The webserver
// rejects duplicate (kind, path) registrations, so registering GET and POST
// as two routes would throw and leave the POST handler dead.
return hostCtx.webServer.register({
kind: 'exact',
path: '/dsh-vision-recognizer/config',
handler: async (request, response) => {
if (request.method === 'GET') {
sendJson(response, 200, {

@@ -172,13 +170,5 @@ providers: providerCatalog(),

});
},
}),
hostCtx.webServer.register({
kind: 'exact',
path: '/dsh-vision-recognizer/config',
handler: async (request, response) => {
if (request.method !== 'POST') {
response.writeHead(405, { allow: 'POST' });
response.end();
return;
}
return;
}
if (request.method === 'POST') {
if (!sameOrigin(request)) {

@@ -197,8 +187,8 @@ sendJson(response, 403, { error: 'untrusted origin' });

}
},
}),
];
return () => {
for (const dispose of disposers) dispose();
};
return;
}
response.writeHead(405, { allow: 'GET, POST' });
response.end();
},
});
}

@@ -205,0 +195,0 @@

{
"name": "dsh-vision-recognizer",
"version": "0.1.0",
"version": "0.1.1",
"description": "DeepSeek Harness image recognition plugin: keep DeepSeek as the conversation brain while a configurable vision model (15+ providers, OpenAI-compatible + Anthropic) transcribes attached images. Configurable from Settings → Plugins.",

@@ -5,0 +5,0 @@ "private": false,