dsh-host-router 0.1.0 → 0.1.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/dist/client.cjs CHANGED
@@ -80,12 +80,12 @@ function HostRouterPage() {
80
80
  const hideTip = () => setTip(null);
81
81
  const refreshData = react.default.useCallback(async () => {
82
82
  const [seenR, cfgR] = await Promise.all([jget("/seen"), jget("/config")]);
83
- if (cfgR?.ok?.value) setCfg(cfgR.value);
83
+ if (cfgR?.ok) setCfg(cfgR.value);
84
84
  setSeen(seenR?.ok ? seenR.hosts : []);
85
85
  }, []);
86
86
  const refreshAll = react.default.useCallback(async () => {
87
87
  const [seenR, cfgR] = await Promise.all([jget("/seen"), jget("/config")]);
88
- if (cfgR?.ok?.value) {
88
+ if (cfgR?.ok) {
89
89
  setCfg(cfgR.value);
90
90
  setDraft(new Set(cfgR.value.proxiedHosts ?? []));
91
91
  }
@@ -186,12 +186,12 @@ function HostRouterPage() {
186
186
  }, tip.text));
187
187
  }
188
188
  function apply(ctx) {
189
- ctx.effect(() => ctx.slots.inject("settings.section", () => ctx.slots.register({
189
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
190
190
  name: "settings.section",
191
191
  id: "dsh-host-router",
192
192
  order: 60,
193
193
  label: "网络路由"
194
- }, (props) => react.default.createElement(HostRouterPage, props))), "dsh-host-router: settings page");
194
+ }, (props) => react.default.createElement(HostRouterPage, props)));
195
195
  }
196
196
 
197
197
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"client.cjs","names":["path: string","body?: any","ev: any","text: string","h: string","h: any","txt: string","e: any","ctx: ClientContext","props: any"],"sources":["../src/client/index.ts"],"sourcesContent":["/**\n * dsh-host-router · client 设置页(纯 React)。\n *\n * 挂 settings.section slot:设置页里独立一页「网络路由」。\n * 通信:同源 fetch → host webServer API(/api/dsh-host-router/{seen,scan,save,test,config})。\n *\n * 渲染:纯 React(createElement + hooks),React 管 mount/unmount,切走回来自动重渲染不丢。\n * (之前 useEffect+ref 塞原生 DOM,切走回来组件未 remount 时 DOM 丢失 → 改纯 React。)\n */\nimport React from 'react'\n\ntype SlotsService = any\ntype ClientContext = { slots: SlotsService }\n\nexport const inject = ['slots']\n\nconst API = '/api/dsh-host-router'\n\nconst STYLES = `\n.hr-page{font-family:ui-monospace,monospace;font-size:13px;line-height:1.55;padding:10px 14px;max-width:900px}\n.hr-page h3{margin:0 0 4px;font-size:14px}\n.hr-ep{color:var(--theme-text-secondary,#999);margin:0 0 6px;font-size:12px}\n.hr-hint{color:var(--theme-text-tertiary,#777);font-size:12px;margin:0 0 10px}\n.hr-bar{display:flex;gap:6px;align-items:center;margin-bottom:8px;flex-wrap:wrap}\n.hr-btn{background:var(--theme-accent,#4a9eff);color:#fff;border:none;border-radius:6px;padding:4px 10px;cursor:pointer;font-size:12px;white-space:nowrap}\n.hr-btn.ghost{background:transparent;border:1px solid var(--theme-border,#444);color:var(--theme-text,#ccc)}\n.hr-btn:disabled{opacity:.45;cursor:not-allowed}\n.hr-msg{color:var(--theme-text-secondary,#999);font-size:12px;padding:5px 8px;border-radius:6px;background:var(--theme-input-bg,#111);border:1px solid var(--theme-border,#333);white-space:pre-wrap;max-height:90px;overflow:auto;margin:6px 0}\n.table-wrap{overflow:auto;max-height:55vh;margin-bottom:10px}\ntable{border-collapse:separate;border-spacing:0;width:auto;table-layout:fixed}\nth,td{border-bottom:1px solid var(--theme-border,#333);padding:3px 6px;text-align:left;font-size:12px;white-space:nowrap}\ntd{overflow:hidden;text-overflow:ellipsis}\nth{font-size:11px;color:var(--theme-text-secondary,#999);font-weight:600}\nthead th{position:sticky;top:0;background:var(--theme-bg,#1b1b1c);z-index:1}\ntd.c,th.c{text-align:center}\ntr.stale-row{opacity:.5}\n.st-ok{color:#2ecc71}.st-fail{color:#e74c3c}.st-gray{color:#888}\n.empty{padding:12px;text-align:center;color:var(--theme-text-secondary,#888)}\n.hr-tip{position:fixed;z-index:1000;pointer-events:none;background:var(--theme-input-bg,#111);border:1px solid var(--theme-border,#444);color:var(--theme-text,#ccc);padding:4px 8px;border-radius:5px;font-size:12px;max-width:440px;white-space:normal;word-break:break-all;box-shadow:0 2px 8px rgba(0,0,0,.35)}\n.h4{margin:14px 0 4px;font-size:12px;color:var(--theme-text-secondary,#aaa)}\n`\n\nasync function jget(path: string): Promise<any> { return fetch(API + path).then((r) => r.json()).catch(() => null) }\nasync function jpost(path: string, body?: any): Promise<any> {\n return fetch(API + path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: body ? JSON.stringify(body) : undefined })\n .then((r) => r.json()).catch(() => null)\n}\n\ninterface SeenHost {\n host: string; lastStatus?: number | string; lastMs?: number; count?: number\n owner?: string; source?: string; proxied?: boolean; hits?: number; stale?: boolean; alive?: boolean\n}\ninterface Cfg { proxyHost?: string; proxyPort?: number; proxiedHosts?: string[] }\n\nfunction HostRouterPage() {\n const [cfg, setCfg] = React.useState<Cfg>({})\n const [draft, setDraft] = React.useState<Set<string>>(new Set())\n const [seen, setSeen] = React.useState<SeenHost[]>([])\n const [msg, setMsg] = React.useState('')\n const [busy, setBusy] = React.useState(false)\n const [tip, setTip] = React.useState<{ x: number; y: number; text: string } | null>(null)\n\n // 超长内容浮窗(fixed 定位,不被表格 overflow 裁剪;右缘 clamp 防出屏)。\n const showTip = (ev: any, text: string) => setTip({ x: Math.min(ev.clientX + 12, window.innerWidth - 452), y: ev.clientY + 16, text })\n const hideTip = () => setTip(null)\n\n // 只刷数据(seen/cfg),不重置草稿:轮询与手动刷新不覆盖用户编辑到一半的勾选(2026-08-29)。\n const refreshData = React.useCallback(async () => {\n const [seenR, cfgR] = await Promise.all([jget('/seen'), jget('/config')])\n if (cfgR?.ok?.value) setCfg(cfgR.value)\n setSeen(seenR?.ok ? seenR.hosts : [])\n }, [])\n // 全量刷新:数据 + 草稿同步磁盘白名单(首次加载/清理过期后用)。\n const refreshAll = React.useCallback(async () => {\n const [seenR, cfgR] = await Promise.all([jget('/seen'), jget('/config')])\n if (cfgR?.ok?.value) { setCfg(cfgR.value); setDraft(new Set(cfgR.value.proxiedHosts ?? [])) }\n setSeen(seenR?.ok ? seenR.hosts : [])\n }, [])\n\n React.useEffect(() => {\n refreshAll()\n const t = window.setInterval(refreshData, 5000)\n return () => window.clearInterval(t)\n }, [refreshAll, refreshData])\n\n const toggle = (h: string) => setDraft((d) => { const n = new Set(d); n.has(h) ? n.delete(h) : n.add(h); return n })\n\n const doScan = async () => {\n setMsg('扫描中…'); setBusy(true)\n const r = await jpost('/scan')\n setBusy(false)\n if (!r?.ok) { setMsg('扫描失败'); return }\n setMsg(`扫描完成,${r.hosts.filter((h: any) => h.needsProxy).length} 个直连不通(建议代理)`)\n await refreshData() // 扫描结果已 upsert 进 seen,刷新拉合并表(不动草稿)\n }\n\n const doSave = async () => {\n setBusy(true); setMsg('保存中…')\n const r = await jpost('/save', { proxiedHosts: [...draft] })\n setBusy(false)\n setMsg(r?.ok ? '已保存并即时生效' : '保存失败')\n if (r?.ok) refreshData()\n }\n\n const doTest = async (h: string) => {\n const r = await jpost('/test', { host: h })\n setMsg(`${h}: ${r.status} · ${r.ms}ms · ${r.proxy ? '代理' : '直连'}` + (r.message ? ` · ${r.message}` : ''))\n }\n\n const th = (txt: string, c = true) => React.createElement('th', { className: c ? 'c' : '', key: txt }, txt)\n const endpoint = `${cfg.proxyHost ?? '127.0.0.1'}:${cfg.proxyPort ?? 7897}`\n\n const doCleanStale = async () => {\n setBusy(true); setMsg('清理过期…')\n const r = await jpost('/clean-stale')\n setBusy(false)\n setMsg(r?.ok ? `已清理 ${r.removed?.length ?? 0} 个过期 host:${(r.removed ?? []).join(', ') || '无'}` : '清理失败')\n if (r?.ok) refreshAll() // 磁盘白名单被清理,草稿需同步\n }\n\n return React.createElement('div', { className: 'hr-page' },\n React.createElement('style', null, STYLES),\n React.createElement('h3', null, '网络路由(dsh-host-router)'),\n React.createElement('p', { className: 'hr-ep' }, '代理: ', React.createElement('code', null, endpoint)),\n React.createElement('p', { className: 'hr-hint' }, '勾选走代理,保存即生效。绿=本次扫到在用,灰=本次没扫到(几次重启都灰可删),过期=白名单里但久未用。'),\n React.createElement('div', { className: 'hr-bar' },\n React.createElement('button', { className: 'hr-btn', onClick: doScan, disabled: busy }, '扫描配置'),\n React.createElement('button', { className: 'hr-btn ghost', onClick: doSave, disabled: busy }, '保存勾选'),\n React.createElement('button', { className: 'hr-btn ghost', onClick: doCleanStale, disabled: busy }, '清理过期'),\n React.createElement('button', { className: 'hr-btn ghost', onClick: refreshData }, '刷新'),\n ),\n msg && React.createElement('div', { className: 'hr-msg' }, msg),\n // 合一表:嗅探 + 白名单(没扫到也显示);精简 5 列,tip 浮窗展示超长内容\n React.createElement('table', null,\n React.createElement('colgroup', null,\n React.createElement('col', { style: { width: '200px' } }),\n React.createElement('col', { style: { width: '44px' } }),\n React.createElement('col', { style: { width: '64px' } }),\n React.createElement('col', { style: { width: '68px' } }),\n React.createElement('col', { style: { width: '56px' } }),\n React.createElement('col', { style: { width: '76px' } }),\n React.createElement('col', { style: { width: '44px' } }),\n ),\n React.createElement('thead', null, React.createElement('tr', null,\n th('hostname', false), th('代理'), th('归属'), th('本次命中'), th('状态'), th('耗时'), th('测'))),\n React.createElement('tbody', null,\n seen.length === 0\n ? React.createElement('tr', null, React.createElement('td', { className: 'empty', colSpan: 5 }, '暂无数据,先用 dsh 一会儿或点「扫描配置」'))\n : seen.map((h: any) => {\n const alive = h.alive !== false && (h.count ?? 0) > 0\n return React.createElement('tr', { key: h.host, className: h.stale ? 'stale-row' : undefined },\n React.createElement('td', { onMouseEnter: (e: any) => showTip(e, h.host), onMouseLeave: hideTip }, h.host),\n React.createElement('td', { className: 'c' }, React.createElement('input', { type: 'checkbox', checked: draft.has(h.host), onChange: () => toggle(h.host) })),\n React.createElement('td', { className: 'c', onMouseEnter: (e: any) => showTip(e, `${h.owner ?? '未知'} · ${h.source ?? '-'}`), onMouseLeave: hideTip }, h.owner ?? '未知'),\n React.createElement('td', { className: 'c' }, React.createElement('span', { className: alive ? 'st-ok' : 'st-gray' }, alive ? `●${h.count}` : '○')),\n React.createElement('td', { className: 'c' }, React.createElement('span', { className: h.lastStatus === 'ERR' ? 'st-fail' : 'st-ok' }, String(h.lastStatus ?? '—'))),\n React.createElement('td', { className: 'c' }, h.lastMs != null ? `${h.lastMs}ms` : ''),\n React.createElement('td', { className: 'c' }, React.createElement('button', { className: 'hr-btn ghost', onClick: () => doTest(h.host), style: { padding: '0 6px' } }, '测')),\n )\n })\n ),\n ),\n tip && React.createElement('div', { className: 'hr-tip', style: { left: tip.x, top: tip.y } }, tip.text),\n )\n}\n\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.slots.inject('settings.section', () =>\n ctx.slots.register(\n { name: 'settings.section', id: 'dsh-host-router', order: 60, label: '网络路由' },\n (props: any) => React.createElement(HostRouterPage, props),\n ),\n ), 'dsh-host-router: settings page')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.cjs","names":["path: string","body?: any","ev: any","text: string","h: string","h: any","txt: string","e: any","ctx: ClientContext","props: any"],"sources":["../src/client/index.ts"],"sourcesContent":["/**\n * dsh-host-router · client 设置页(纯 React)。\n *\n * 挂 settings.section slot:设置页里独立一页「网络路由」。\n * 通信:同源 fetch → host webServer API(/api/dsh-host-router/{seen,scan,save,test,config})。\n *\n * 渲染:纯 React(createElement + hooks),React 管 mount/unmount,切走回来自动重渲染不丢。\n * (之前 useEffect+ref 塞原生 DOM,切走回来组件未 remount 时 DOM 丢失 → 改纯 React。)\n */\nimport React from 'react'\n\ntype SlotsService = any\ntype ClientContext = { slots: SlotsService }\n\nexport const inject = ['slots']\n\nconst API = '/api/dsh-host-router'\n\nconst STYLES = `\n.hr-page{font-family:ui-monospace,monospace;font-size:13px;line-height:1.55;padding:10px 14px;max-width:900px}\n.hr-page h3{margin:0 0 4px;font-size:14px}\n.hr-ep{color:var(--theme-text-secondary,#999);margin:0 0 6px;font-size:12px}\n.hr-hint{color:var(--theme-text-tertiary,#777);font-size:12px;margin:0 0 10px}\n.hr-bar{display:flex;gap:6px;align-items:center;margin-bottom:8px;flex-wrap:wrap}\n.hr-btn{background:var(--theme-accent,#4a9eff);color:#fff;border:none;border-radius:6px;padding:4px 10px;cursor:pointer;font-size:12px;white-space:nowrap}\n.hr-btn.ghost{background:transparent;border:1px solid var(--theme-border,#444);color:var(--theme-text,#ccc)}\n.hr-btn:disabled{opacity:.45;cursor:not-allowed}\n.hr-msg{color:var(--theme-text-secondary,#999);font-size:12px;padding:5px 8px;border-radius:6px;background:var(--theme-input-bg,#111);border:1px solid var(--theme-border,#333);white-space:pre-wrap;max-height:90px;overflow:auto;margin:6px 0}\n.table-wrap{overflow:auto;max-height:55vh;margin-bottom:10px}\ntable{border-collapse:separate;border-spacing:0;width:auto;table-layout:fixed}\nth,td{border-bottom:1px solid var(--theme-border,#333);padding:3px 6px;text-align:left;font-size:12px;white-space:nowrap}\ntd{overflow:hidden;text-overflow:ellipsis}\nth{font-size:11px;color:var(--theme-text-secondary,#999);font-weight:600}\nthead th{position:sticky;top:0;background:var(--theme-bg,#1b1b1c);z-index:1}\ntd.c,th.c{text-align:center}\ntr.stale-row{opacity:.5}\n.st-ok{color:#2ecc71}.st-fail{color:#e74c3c}.st-gray{color:#888}\n.empty{padding:12px;text-align:center;color:var(--theme-text-secondary,#888)}\n.hr-tip{position:fixed;z-index:1000;pointer-events:none;background:var(--theme-input-bg,#111);border:1px solid var(--theme-border,#444);color:var(--theme-text,#ccc);padding:4px 8px;border-radius:5px;font-size:12px;max-width:440px;white-space:normal;word-break:break-all;box-shadow:0 2px 8px rgba(0,0,0,.35)}\n.h4{margin:14px 0 4px;font-size:12px;color:var(--theme-text-secondary,#aaa)}\n`\n\nasync function jget(path: string): Promise<any> { return fetch(API + path).then((r) => r.json()).catch(() => null) }\nasync function jpost(path: string, body?: any): Promise<any> {\n return fetch(API + path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: body ? JSON.stringify(body) : undefined })\n .then((r) => r.json()).catch(() => null)\n}\n\ninterface SeenHost {\n host: string; lastStatus?: number | string; lastMs?: number; count?: number\n owner?: string; source?: string; proxied?: boolean; hits?: number; stale?: boolean; alive?: boolean\n}\ninterface Cfg { proxyHost?: string; proxyPort?: number; proxiedHosts?: string[] }\n\nfunction HostRouterPage() {\n const [cfg, setCfg] = React.useState<Cfg>({})\n const [draft, setDraft] = React.useState<Set<string>>(new Set())\n const [seen, setSeen] = React.useState<SeenHost[]>([])\n const [msg, setMsg] = React.useState('')\n const [busy, setBusy] = React.useState(false)\n const [tip, setTip] = React.useState<{ x: number; y: number; text: string } | null>(null)\n\n // 超长内容浮窗(fixed 定位,不被表格 overflow 裁剪;右缘 clamp 防出屏)。\n const showTip = (ev: any, text: string) => setTip({ x: Math.min(ev.clientX + 12, window.innerWidth - 452), y: ev.clientY + 16, text })\n const hideTip = () => setTip(null)\n\n // 只刷数据(seen/cfg),不重置草稿:轮询与手动刷新不覆盖用户编辑到一半的勾选(2026-08-29)。\n const refreshData = React.useCallback(async () => {\n const [seenR, cfgR] = await Promise.all([jget('/seen'), jget('/config')])\n if (cfgR?.ok) setCfg(cfgR.value)\n setSeen(seenR?.ok ? seenR.hosts : [])\n }, [])\n // 全量刷新:数据 + 草稿同步磁盘白名单(首次加载/清理过期后用)。\n const refreshAll = React.useCallback(async () => {\n const [seenR, cfgR] = await Promise.all([jget('/seen'), jget('/config')])\n if (cfgR?.ok) { setCfg(cfgR.value); setDraft(new Set(cfgR.value.proxiedHosts ?? [])) }\n setSeen(seenR?.ok ? seenR.hosts : [])\n }, [])\n\n React.useEffect(() => {\n refreshAll()\n const t = window.setInterval(refreshData, 5000)\n return () => window.clearInterval(t)\n }, [refreshAll, refreshData])\n\n const toggle = (h: string) => setDraft((d) => { const n = new Set(d); n.has(h) ? n.delete(h) : n.add(h); return n })\n\n const doScan = async () => {\n setMsg('扫描中…'); setBusy(true)\n const r = await jpost('/scan')\n setBusy(false)\n if (!r?.ok) { setMsg('扫描失败'); return }\n setMsg(`扫描完成,${r.hosts.filter((h: any) => h.needsProxy).length} 个直连不通(建议代理)`)\n await refreshData() // 扫描结果已 upsert 进 seen,刷新拉合并表(不动草稿)\n }\n\n const doSave = async () => {\n setBusy(true); setMsg('保存中…')\n const r = await jpost('/save', { proxiedHosts: [...draft] })\n setBusy(false)\n setMsg(r?.ok ? '已保存并即时生效' : '保存失败')\n if (r?.ok) refreshData()\n }\n\n const doTest = async (h: string) => {\n const r = await jpost('/test', { host: h })\n setMsg(`${h}: ${r.status} · ${r.ms}ms · ${r.proxy ? '代理' : '直连'}` + (r.message ? ` · ${r.message}` : ''))\n }\n\n const th = (txt: string, c = true) => React.createElement('th', { className: c ? 'c' : '', key: txt }, txt)\n const endpoint = `${cfg.proxyHost ?? '127.0.0.1'}:${cfg.proxyPort ?? 7897}`\n\n const doCleanStale = async () => {\n setBusy(true); setMsg('清理过期…')\n const r = await jpost('/clean-stale')\n setBusy(false)\n setMsg(r?.ok ? `已清理 ${r.removed?.length ?? 0} 个过期 host:${(r.removed ?? []).join(', ') || '无'}` : '清理失败')\n if (r?.ok) refreshAll() // 磁盘白名单被清理,草稿需同步\n }\n\n return React.createElement('div', { className: 'hr-page' },\n React.createElement('style', null, STYLES),\n React.createElement('h3', null, '网络路由(dsh-host-router)'),\n React.createElement('p', { className: 'hr-ep' }, '代理: ', React.createElement('code', null, endpoint)),\n React.createElement('p', { className: 'hr-hint' }, '勾选走代理,保存即生效。绿=本次扫到在用,灰=本次没扫到(几次重启都灰可删),过期=白名单里但久未用。'),\n React.createElement('div', { className: 'hr-bar' },\n React.createElement('button', { className: 'hr-btn', onClick: doScan, disabled: busy }, '扫描配置'),\n React.createElement('button', { className: 'hr-btn ghost', onClick: doSave, disabled: busy }, '保存勾选'),\n React.createElement('button', { className: 'hr-btn ghost', onClick: doCleanStale, disabled: busy }, '清理过期'),\n React.createElement('button', { className: 'hr-btn ghost', onClick: refreshData }, '刷新'),\n ),\n msg && React.createElement('div', { className: 'hr-msg' }, msg),\n // 合一表:嗅探 + 白名单(没扫到也显示);精简 5 列,tip 浮窗展示超长内容\n React.createElement('table', null,\n React.createElement('colgroup', null,\n React.createElement('col', { style: { width: '200px' } }),\n React.createElement('col', { style: { width: '44px' } }),\n React.createElement('col', { style: { width: '64px' } }),\n React.createElement('col', { style: { width: '68px' } }),\n React.createElement('col', { style: { width: '56px' } }),\n React.createElement('col', { style: { width: '76px' } }),\n React.createElement('col', { style: { width: '44px' } }),\n ),\n React.createElement('thead', null, React.createElement('tr', null,\n th('hostname', false), th('代理'), th('归属'), th('本次命中'), th('状态'), th('耗时'), th('测'))),\n React.createElement('tbody', null,\n seen.length === 0\n ? React.createElement('tr', null, React.createElement('td', { className: 'empty', colSpan: 5 }, '暂无数据,先用 dsh 一会儿或点「扫描配置」'))\n : seen.map((h: any) => {\n const alive = h.alive !== false && (h.count ?? 0) > 0\n return React.createElement('tr', { key: h.host, className: h.stale ? 'stale-row' : undefined },\n React.createElement('td', { onMouseEnter: (e: any) => showTip(e, h.host), onMouseLeave: hideTip }, h.host),\n React.createElement('td', { className: 'c' }, React.createElement('input', { type: 'checkbox', checked: draft.has(h.host), onChange: () => toggle(h.host) })),\n React.createElement('td', { className: 'c', onMouseEnter: (e: any) => showTip(e, `${h.owner ?? '未知'} · ${h.source ?? '-'}`), onMouseLeave: hideTip }, h.owner ?? '未知'),\n React.createElement('td', { className: 'c' }, React.createElement('span', { className: alive ? 'st-ok' : 'st-gray' }, alive ? `●${h.count}` : '○')),\n React.createElement('td', { className: 'c' }, React.createElement('span', { className: h.lastStatus === 'ERR' ? 'st-fail' : 'st-ok' }, String(h.lastStatus ?? '—'))),\n React.createElement('td', { className: 'c' }, h.lastMs != null ? `${h.lastMs}ms` : ''),\n React.createElement('td', { className: 'c' }, React.createElement('button', { className: 'hr-btn ghost', onClick: () => doTest(h.host), style: { padding: '0 6px' } }, '测')),\n )\n })\n ),\n ),\n tip && React.createElement('div', { className: 'hr-tip', style: { left: tip.x, top: tip.y } }, tip.text),\n )\n}\n\nexport function apply(ctx: ClientContext): void {\n // slots.inject 必须裸调用(不包 ctx.effect),否则注册时机错位——\n // 切走设置页再切回时组件被重建、draft 状态丢失(对勾消失)。照 dsh-capture 写法。\n ctx.slots.inject('settings.section', () =>\n ctx.slots.register(\n { name: 'settings.section', id: 'dsh-host-router', order: 60, label: '网络路由' },\n (props: any) => React.createElement(HostRouterPage, props),\n ),\n )\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-host-router",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "lib/index.js",
6
6
  "description": "dsh 外挂式网络路由:按 hostname 给 dsh 请求分流——勾选的域名走本地代理(Clash 等),其余直连。内置嗅探,自动扫描 dsh 访问过的 hostname,设置页勾选即生效(保存即热生效,免重启)。纯外挂:dependencies 空,运行时零 require,全靠宿主 ctx 注入;不碰 settings.yaml provider 配置。",
@@ -78,4 +78,4 @@
78
78
  "publishConfig": {
79
79
  "access": "public"
80
80
  }
81
- }
81
+ }