@omdp/dsh-connector 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.
Files changed (2) hide show
  1. package/client.js +186 -55
  2. package/package.json +1 -1
package/client.js CHANGED
@@ -84,7 +84,25 @@ window.__ModuleLoader__.load({
84
84
  '.pm_err{color:var(--dsw-alias-state-error-primary,#f87171);font-size:12px}' +
85
85
  '.pm_ok{color:var(--dsw-alias-state-success-primary,#4ade80);font-size:12px}' +
86
86
  '.pm_empty{font-size:12px;color:var(--dsw-alias-label-secondary,#888);padding:10px 2px}' +
87
- '.pm_btnrow{display:flex;gap:8px;align-items:center;flex-wrap:wrap}'
87
+ '.pm_btnrow{display:flex;gap:8px;align-items:center;flex-wrap:wrap}' +
88
+ /* market grid & icons */
89
+ '.pm_grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-top:2px}' +
90
+ '@media(max-width:760px){.pm_grid{grid-template-columns:1fr}}' +
91
+ '.pm_mcard{background:var(--dsw-alias-bg-layer-1,rgba(255,255,255,.035));border:1px solid var(--dsw-alias-border-l1,rgba(128,128,128,.22));border-radius:12px;padding:12px 14px;display:flex;flex-direction:column;gap:8px;transition:background .15s,border-color .15s}' +
92
+ '.pm_mcard:hover{background:var(--dsw-alias-bg-layer-2,rgba(255,255,255,.06));border-color:var(--dsw-alias-border-l2,rgba(128,128,128,.4))}' +
93
+ '.pm_mhead{display:flex;align-items:center;gap:10px;min-width:0}' +
94
+ '.pm_icon{width:38px;height:38px;border-radius:10px;flex:none;object-fit:cover;background:var(--dsw-alias-bg-layer-2,#333);border:1px solid var(--dsw-alias-border-l1,rgba(128,128,128,.25))}' +
95
+ '.pm_avatar{width:38px;height:38px;border-radius:10px;flex:none;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:16px;color:#fff;background:linear-gradient(135deg,var(--dsw-alias-brand-primary,#5b8cff),#a06bff);text-transform:uppercase}' +
96
+ '.pm_mtitle{min-width:0;flex:1}' +
97
+ '.pm_mname{font-weight:600;font-size:13.5px;color:var(--dsw-alias-label-primary,#eee);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center;gap:6px}' +
98
+ '.pm_mid{font-size:11px;color:var(--dsw-alias-label-secondary,#9aa);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}' +
99
+ '.pm_stats{display:flex;gap:12px;flex-wrap:wrap}' +
100
+ '.pm_stat{font-size:11.5px;color:var(--dsw-alias-label-secondary,#9aa);display:inline-flex;align-items:center;gap:4px}' +
101
+ '.pm_stat b{font-weight:600;color:var(--dsw-alias-label-primary,#ddd)}' +
102
+ '.pm_chips{display:flex;gap:6px;flex-wrap:wrap}' +
103
+ '.pm_chip{cursor:pointer;font:inherit;font-size:11.5px;padding:4px 12px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.35));background:transparent;color:var(--dsw-alias-label-secondary,#9aa);transition:all .12s}' +
104
+ '.pm_chip:hover{color:var(--dsw-alias-label-primary,#ddd);border-color:var(--dsw-alias-brand-primary,#5b8cff)}' +
105
+ '.pm_chip.active{background:var(--dsw-alias-brand-primary,#5b8cff);border-color:transparent;color:#fff}'
88
106
 
89
107
  if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css="@omdp/dsh-connector/section"]') === null) {
90
108
  var tag = document.createElement('style')
@@ -502,6 +520,52 @@ window.__ModuleLoader__.load({
502
520
 
503
521
  /* ─────────────────────────── marketplace ─────────────────────────── */
504
522
 
523
+ // 12 official ModelScope skill categories (skills-center.md taxonomy).
524
+ var SKILL_CATEGORIES = [
525
+ { id: '', label: '全部' },
526
+ { id: 'developer-tools', label: '开发者工具' },
527
+ { id: 'code-quality-testing', label: '代码质量' },
528
+ { id: 'ai-media', label: 'AI 媒体' },
529
+ { id: 'frontend-development', label: '前端开发' },
530
+ { id: 'cloud-devops', label: '云与运维' },
531
+ { id: 'ai-automation', label: 'AI 自动化' },
532
+ { id: 'analytics', label: '数据分析' },
533
+ { id: 'doc-processing', label: '文档处理' },
534
+ { id: 'skill-management', label: '技能管理' },
535
+ { id: 'mobile-development', label: '移动开发' },
536
+ { id: 'marketing-seo', label: '营销 SEO' },
537
+ { id: 'other', label: '其他' },
538
+ ]
539
+
540
+ // 12438 -> "12.4k", 987654 -> "987.7k", 1.2e7 -> "12.0M"
541
+ function fmtNum(n) {
542
+ var v = Number(n)
543
+ if (!isFinite(v)) return ''
544
+ if (v >= 1e8) return (v / 1e8).toFixed(1).replace(/\.0$/, '') + '亿'
545
+ if (v >= 1e6) return (v / 1e6).toFixed(1).replace(/\.0$/, '') + 'M'
546
+ if (v >= 1e3) return (v / 1e3).toFixed(1).replace(/\.0$/, '') + 'k'
547
+ return String(v)
548
+ }
549
+
550
+ var SKILL_CAT_LABEL = {}
551
+ SKILL_CATEGORIES.forEach(function (c) { if (c.id) SKILL_CAT_LABEL[c.id] = c.label })
552
+ function catLabel(id) { return SKILL_CAT_LABEL[id] || id }
553
+
554
+ function MarketIcon(props) {
555
+ var _a = react.useState(true)
556
+ var ok = _a[0]
557
+ var setOk = _a[1]
558
+ if (props.logo && ok) {
559
+ return createElement('img', {
560
+ className: 'pm_icon',
561
+ src: props.logo,
562
+ alt: '',
563
+ onError: function () { setOk(false) },
564
+ })
565
+ }
566
+ return createElement('div', { className: 'pm_avatar' }, (props.label || '?').charAt(0))
567
+ }
568
+
505
569
  function MarketPane(props) {
506
570
  var _a = react.useState('')
507
571
  var mcpSearch = _a[0]
@@ -530,19 +594,30 @@ window.__ModuleLoader__.load({
530
594
  var _i = react.useState('')
531
595
  var err = _i[0]
532
596
  var setErr = _i[1]
533
-
534
- function findMcp() {
597
+ var _j = react.useState('')
598
+ var category = _j[0]
599
+ var setCategory = _j[1]
600
+ var _k = react.useState('mcp')
601
+ var sub = _k[0]
602
+ var setSub = _k[1]
603
+
604
+ function findMcp(term) {
605
+ var q = (term !== undefined ? term : mcpSearch).trim()
535
606
  setMcpBusy(true)
536
607
  setErr('')
537
- api('/market/mcp?search=' + encodeURIComponent(mcpSearch.trim()), { method: 'GET' }).then(function (r) {
608
+ api('/market/mcp?search=' + encodeURIComponent(q), { method: 'GET' }).then(function (r) {
538
609
  if (r && r.error) { setErr(r.error); setMcpResult(null); return }
539
610
  setMcpResult(r)
540
611
  }).finally(function () { setMcpBusy(false) })
541
612
  }
542
- function findSkills() {
613
+ function findSkills(term, cat) {
614
+ var q = (term !== undefined ? term : skillSearch).trim()
615
+ var c = cat !== undefined ? cat : category
543
616
  setSkillBusy(true)
544
617
  setErr('')
545
- api('/market/skills?search=' + encodeURIComponent(skillSearch.trim()), { method: 'GET' }).then(function (r) {
618
+ var qs = '/market/skills?search=' + encodeURIComponent(q)
619
+ if (c) qs += '&category=' + encodeURIComponent(c)
620
+ api(qs, { method: 'GET' }).then(function (r) {
546
621
  if (r && r.error) { setErr(r.error); setSkillResult(null); return }
547
622
  setSkillResult(r)
548
623
  }).finally(function () { setSkillBusy(false) })
@@ -564,6 +639,13 @@ window.__ModuleLoader__.load({
564
639
  })
565
640
  }
566
641
 
642
+ // Load the default (hot) lists right away so the tab never opens empty.
643
+ react.useEffect(function () {
644
+ findMcp('')
645
+ findSkills('', '')
646
+ // eslint-disable-next-line react-hooks/exhaustive-deps
647
+ }, [])
648
+
567
649
  var mcpRows = mcpResult && mcpResult.items
568
650
  ? mcpResult.items.map(function (s) {
569
651
  var d = mcpDetail[s.id]
@@ -591,33 +673,55 @@ window.__ModuleLoader__.load({
591
673
  '浏览魔搭社区(ModelScope)技能中心与 MCP 广场。列表数据缓存在 DSH 进程内存(30 分钟),不写任何文件;安装/部署请复制命令自行执行。Browse ModelScope Skills Center & MCP Plaza; cached in-process only, nothing saved to disk.'),
592
674
  err ? createElement('div', { className: 'pm_err' }, err) : null,
593
675
 
594
- createElement('div', { className: 'pm_groupHead' }, 'MCP 市场 MCP Plaza'),
595
- createElement('div', { className: 'pm_search' },
596
- createElement('input', {
597
- placeholder: '搜索 MCP 服务,如 map / github / fetch Search…',
598
- value: mcpSearch,
599
- onChange: function (e) { setMcpSearch(e.target.value) },
600
- onKeyDown: function (e) { if (e.key === 'Enter') findMcp() },
601
- }),
602
- createElement('button', { className: 'pm_btn primary', disabled: mcpBusy, onClick: findMcp }, mcpBusy ? '搜索中…' : '搜索'),
603
- ),
604
- mcpRows.length ? mcpRows : (mcpResult ? createElement('div', { className: 'pm_empty' }, '没有匹配的 MCP 服务。No matching MCP servers.') : null),
605
-
606
- createElement('div', { className: 'pm_groupHead', style: { marginTop: '18px' } }, 'Skills 市场 Skills Center'),
607
- createElement('div', { className: 'pm_search' },
608
- createElement('input', {
609
- placeholder: '搜索技能,如 code-review / deploy Search…',
610
- value: skillSearch,
611
- onChange: function (e) { setSkillSearch(e.target.value) },
612
- onKeyDown: function (e) { if (e.key === 'Enter') findSkills() },
613
- }),
614
- createElement('button', { className: 'pm_btn primary', disabled: skillBusy, onClick: findSkills }, skillBusy ? '搜索中…' : '搜索'),
676
+ createElement('div', { className: 'pm_chips', style: { margin: '2px 0 4px' } },
677
+ createElement('button', { className: 'pm_chip' + (sub === 'mcp' ? ' active' : ''), onClick: function () { setSub('mcp') } }, 'MCP 市场'),
678
+ createElement('button', { className: 'pm_chip' + (sub === 'skills' ? ' active' : ''), onClick: function () { setSub('skills') } }, 'Skills 市场'),
615
679
  ),
616
- skillRows.length ? skillRows : (skillResult ? createElement('div', { className: 'pm_empty' }, '没有匹配的技能。No matching skills.') : null),
680
+
681
+ sub === 'mcp'
682
+ ? createElement('div', { className: 'pm_section' },
683
+ createElement('div', { className: 'pm_search' },
684
+ createElement('input', {
685
+ placeholder: '搜索 MCP 服务,如 map / github / fetch Search…',
686
+ value: mcpSearch,
687
+ onChange: function (e) { setMcpSearch(e.target.value) },
688
+ onKeyDown: function (e) { if (e.key === 'Enter') findMcp() },
689
+ }),
690
+ createElement('button', { className: 'pm_btn primary', disabled: mcpBusy, onClick: function () { findMcp() } }, mcpBusy ? '搜索中…' : '搜索'),
691
+ ),
692
+ createElement('div', { className: 'pm_grid' }, mcpRows),
693
+ mcpResult && mcpResult.items && !mcpResult.items.length
694
+ ? createElement('div', { className: 'pm_empty' }, '没有匹配的 MCP 服务。No matching MCP servers.')
695
+ : null,
696
+ )
697
+ : createElement('div', { className: 'pm_section' },
698
+ createElement('div', { className: 'pm_search' },
699
+ createElement('input', {
700
+ placeholder: '搜索技能,如 code-review / deploy Search…',
701
+ value: skillSearch,
702
+ onChange: function (e) { setSkillSearch(e.target.value) },
703
+ onKeyDown: function (e) { if (e.key === 'Enter') findSkills() },
704
+ }),
705
+ createElement('button', { className: 'pm_btn primary', disabled: skillBusy, onClick: function () { findSkills() } }, skillBusy ? '搜索中…' : '搜索'),
706
+ ),
707
+ createElement('div', { className: 'pm_chips' },
708
+ SKILL_CATEGORIES.map(function (c) {
709
+ return createElement('button', {
710
+ key: c.id || 'all',
711
+ className: 'pm_chip' + (category === c.id ? ' active' : ''),
712
+ onClick: function () { setCategory(c.id); findSkills(skillSearch, c.id) },
713
+ }, c.label)
714
+ }),
715
+ ),
716
+ createElement('div', { className: 'pm_grid' }, skillRows),
717
+ skillResult && skillResult.items && !skillResult.items.length
718
+ ? createElement('div', { className: 'pm_empty' }, '没有匹配的技能。No matching skills.')
719
+ : null,
720
+ ),
617
721
  )
618
722
  }
619
723
 
620
- // One MCP market entry with a lazy detail panel.
724
+ // One MCP market entry — 2-col grid card with icon, lazy detail panel.
621
725
  function McpMarketItem(props) {
622
726
  var item = props.item
623
727
  var d = props.detail
@@ -632,30 +736,39 @@ window.__ModuleLoader__.load({
632
736
  cfgText = JSON.stringify(sel, null, 2)
633
737
  }
634
738
 
635
- return createElement('div', { className: 'pm_card', key: item.id },
636
- createElement('div', { className: 'pm_cardHead' },
637
- createElement('span', { className: 'pm_name' }, item.name || item.id),
638
- item.configured ? badge('已配置', 'ok') : null,
639
- badge(item.id, 'muted'),
739
+ return createElement('div', { className: 'pm_mcard', key: item.id },
740
+ createElement('div', { className: 'pm_mhead' },
741
+ createElement(MarketIcon, { logo: item.logo, label: (item.name || item.id).charAt(0) }),
742
+ createElement('div', { className: 'pm_mtitle' },
743
+ createElement('div', { className: 'pm_mname' },
744
+ item.name || item.id,
745
+ item.configured ? badge('已配置', 'ok') : null,
746
+ ),
747
+ createElement('div', { className: 'pm_mid' }, item.id),
748
+ ),
640
749
  ),
641
750
  createElement('div', { className: 'pm_desc' }, item.description || ''),
642
- createElement('div', { className: 'pm_meta' },
643
- 'by ' + (item.publisher || '—'),
644
- item.views !== undefined ? ' · ' + item.views + ' 浏览' : '',
751
+ createElement('div', { className: 'pm_stats' },
752
+ createElement('span', { className: 'pm_stat' }, 'by ', createElement('b', null, item.publisher || '—')),
753
+ item.views !== undefined
754
+ ? createElement('span', { className: 'pm_stat' }, '👁 ', createElement('b', null, fmtNum(item.views)))
755
+ : null,
756
+ (item.categories && item.categories.length)
757
+ ? createElement('span', { className: 'pm_stat' }, createElement('b', null, item.categories.slice(0, 2).join(' / ')))
758
+ : null,
645
759
  ),
646
760
  createElement('div', { className: 'pm_actions' },
647
761
  createElement('button', { className: 'pm_btn', onClick: props.onOpen }, d ? '收起' : '详情'),
648
762
  d && configs.length
649
763
  ? createElement('button', { className: 'pm_btn primary', onClick: function () { copied.copy(cfgText) } },
650
- copied.copied || '复制配置 Copy config')
764
+ copied.copied || '复制配置 Copy')
651
765
  : null,
652
766
  ),
653
767
  d ? createElement('div', { className: 'pm_section', style: { marginTop: '4px' } },
654
768
  createElement('div', { className: 'pm_actions' },
655
769
  d.hosted ? badge('Hosted 官方托管', 'brand') : null,
656
770
  d.verified ? badge('已认证 Verified', 'brand') : null,
657
- d.stars !== undefined && d.stars !== null ? badge('★ ' + d.stars, 'muted') : null,
658
- (d.categories && d.categories.length) ? badge(d.categories.slice(0, 3).join(' / '), 'muted') : null,
771
+ d.stars !== undefined && d.stars !== null ? badge('★ ' + fmtNum(d.stars), 'muted') : null,
659
772
  ),
660
773
  (d.envSchema && d.envSchema.length)
661
774
  ? createElement('div', { className: 'pm_meta' },
@@ -671,7 +784,12 @@ window.__ModuleLoader__.load({
671
784
  configs.length
672
785
  ? createElement('div', { className: 'pm_section' },
673
786
  configs.length > 1
674
- ? selectField('配置变体 Config variant', variant === null ? 0 : variant, configs.map(function (_, i) { return 'variant ' + (i + 1) }), function (i) { setVariant(Number(i)) })
787
+ ? createElement('label', null,
788
+ '配置变体 Config variant',
789
+ createElement('select', { value: variant === null ? 0 : variant, onChange: function (e) { setVariant(Number(e.target.value)) } },
790
+ configs.map(function (_, i) { return createElement('option', { value: i, key: i }, 'variant ' + (i + 1)) }),
791
+ ),
792
+ )
675
793
  : null,
676
794
  createElement('div', { className: 'pm_code' }, cfgText),
677
795
  )
@@ -682,7 +800,7 @@ window.__ModuleLoader__.load({
682
800
  )
683
801
  }
684
802
 
685
- // One market skill entry with install commands and local "record source".
803
+ // One market skill entry — grid card, install commands, record-source link.
686
804
  function SkillMarketItem(props) {
687
805
  var item = props.item
688
806
  var d = props.detail
@@ -714,26 +832,34 @@ window.__ModuleLoader__.load({
714
832
  })
715
833
  }
716
834
 
717
- return createElement('div', { className: 'pm_card', key: item.id },
718
- createElement('div', { className: 'pm_cardHead' },
719
- createElement('span', { className: 'pm_name' }, item.name || item.id),
720
- item.installed || alreadyLinked ? badge('已安装', 'ok') : null,
721
- item.developer ? badge('by ' + item.developer, 'muted') : null,
722
- item.category ? badge(item.category, 'muted') : null,
835
+ return createElement('div', { className: 'pm_mcard', key: item.id },
836
+ createElement('div', { className: 'pm_mhead' },
837
+ createElement(MarketIcon, { logo: item.logo, label: (item.name || item.id).charAt(0) }),
838
+ createElement('div', { className: 'pm_mtitle' },
839
+ createElement('div', { className: 'pm_mname' },
840
+ item.name || item.id,
841
+ item.installed || alreadyLinked ? badge('已安装', 'ok') : null,
842
+ d && d.updateAvailable ? badge('有更新', 'warn') : null,
843
+ ),
844
+ createElement('div', { className: 'pm_mid' }, item.id),
845
+ ),
723
846
  ),
724
847
  createElement('div', { className: 'pm_desc' }, item.description || ''),
725
- createElement('div', { className: 'pm_meta' },
726
- item.downloads !== undefined ? item.downloads + ' 下载' : '',
727
- item.views !== undefined ? ' · ' + item.views + ' 浏览' : '',
728
- item.license ? ' · ' + item.license : '',
848
+ createElement('div', { className: 'pm_stats' },
849
+ item.downloads !== undefined
850
+ ? createElement('span', { className: 'pm_stat' }, '⬇ ', createElement('b', null, fmtNum(item.downloads)), ' 下载')
851
+ : createElement('span', { className: 'pm_stat' }, 'by ', createElement('b', null, item.developer || '—')),
852
+ item.views !== undefined
853
+ ? createElement('span', { className: 'pm_stat' }, '👁 ', createElement('b', null, fmtNum(item.views)))
854
+ : null,
855
+ item.category ? createElement('span', { className: 'pm_stat' }, createElement('b', null, catLabel(item.category))) : null,
729
856
  ),
730
857
  createElement('div', { className: 'pm_actions' },
731
858
  createElement('button', { className: 'pm_btn', onClick: props.onOpen }, d ? '收起' : '详情'),
732
- d && d.updateAvailable ? badge('有更新', 'warn') : null,
733
859
  ),
734
860
  d ? createElement('div', { className: 'pm_section', style: { marginTop: '4px' } },
735
861
  createElement('div', { className: 'pm_meta' },
736
- '市场更新时间 Market modified: ' + (d.fileModified || '—'),
862
+ '市场更新 Market modified: ' + (d.fileModified || '—'),
737
863
  d.localSkill ? ' · 本地记录: ' + d.localSkill.sourceUpdated : '',
738
864
  ),
739
865
  installs.length
@@ -750,7 +876,12 @@ window.__ModuleLoader__.load({
750
876
  alreadyLinked
751
877
  ? createElement('div', { className: 'pm_ok' }, '已关联到本地技能 ✓ Linked to a local skill.')
752
878
  : createElement('div', { className: 'pm_btnrow' },
753
- selectField('关联本地技能 Link to local skill', target || '', props.skills.map(function (s) { return s.name }), setTarget),
879
+ createElement('label', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary,#9aa)' } },
880
+ '关联本地技能 Link to local skill',
881
+ createElement('select', { value: target || '', onChange: function (e) { setTarget(e.target.value) } },
882
+ props.skills.map(function (s) { return createElement('option', { value: s.name, key: s.name }, s.name) }),
883
+ ),
884
+ ),
754
885
  createElement('button', { className: 'pm_btn', onClick: recordSource }, '记录来源 Record source'),
755
886
  ),
756
887
  linkErr ? createElement('div', { className: 'pm_err' }, linkErr) : null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omdp/dsh-connector",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Unified DeepSeek Harness connector: edit MCP servers (cordis.patch.yml), user skills (~/.dsh/skills) and browse the ModelScope MCP/Skills marketplaces from one Web UI settings page.",
5
5
  "type": "module",
6
6
  "main": "index.js",