@lrplrplrp/dsh-live2d 0.1.1 → 0.1.3

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.
@@ -6,8 +6,8 @@
6
6
  "canvasWidth": 300,
7
7
  "canvasHeight": 400,
8
8
  "config": {
9
- "x": -50,
10
- "y": 100,
9
+ "x": 0,
10
+ "y": 0,
11
11
  "scaleX": 2,
12
12
  "scaleY": 2
13
13
  },
package/cordis.patch.yml CHANGED
@@ -7,5 +7,5 @@
7
7
  # pnpm dsh plugin --profile web add link:./dsh-live2d
8
8
 
9
9
  - insert:
10
- - id: dsh-live2d
11
- name: 'dsh-live2d'
10
+ - id: '@lrplrplrp/dsh-live2d'
11
+ name: '@lrplrplrp/dsh-live2d'
package/lib/client.js CHANGED
@@ -11,7 +11,7 @@
11
11
  //
12
12
  // 移除了原项目的:对话框、小飞机游戏、关闭/关于/GitHub 按钮。
13
13
 
14
- window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
14
+ window.__ModuleLoader__.load({ id: '@lrplrplrp/dsh-live2d', factory: (require) => {
15
15
  var module = { exports: {} }
16
16
  var exports = module.exports
17
17
 
@@ -27,6 +27,9 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
27
27
  const POSITION_KEY = 'dsh-live2d.position'
28
28
  const ACTIVE_MODEL_KEY = 'dsh-live2d.activeModel'
29
29
 
30
+ // 容器默认屏幕位置(无本地存档时使用),对应 position:fixed 的 left/top
31
+ const DEFAULT_POSITION = { x: 1240, y: 330 }
32
+
30
33
  // 本地文件路由前缀(由 host 端 index.js 注册)
31
34
  const LIB_BASE = '/plugins/dsh-live2d/lib/'
32
35
  const ASSETS_BASE = '/plugins/dsh-live2d/assets/'
@@ -69,13 +72,13 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
69
72
  url: ASSETS_BASE + 'deepseek_l2d/deepseek.model3.json',
70
73
  canvasWidth: 300,
71
74
  canvasHeight: 400,
72
- config: { x: -50, y: 100, scaleX: 2, scaleY: 2 },
75
+ config: { x: 0, y: 0, scaleX: 1.5, scaleY: 1.5 },
73
76
  animations: {
74
77
  IDLE: { motion: { group: 'Idle', index: 0 }, loop: false },
75
- THINKING: { motion: { group: 'Anima', index: 2 }, loop: false },
78
+ THINKING: { motion: { group: 'Anima', index: 2 }, loop: true },
76
79
  THINK_END: { motion: { group: 'Anima', index: 1 }, loop: false },
77
- WORKING: { motion: { group: 'Anima', index: 0 }, loop: false },
78
- SPEAKING: { motion: { group: 'Anima', index: 0 }, loop: false },
80
+ WORKING: { expression: 'puzzled' },
81
+ SPEAKING: { motion: { group: 'Anima', index: 0 }, loop: true },
79
82
  SUCCESS: { expression: 'happy' },
80
83
  ERROR: { expression: 'unhappy' },
81
84
  },
@@ -204,8 +207,9 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
204
207
  const LIVE2D_CSS = [
205
208
  '#dsh-live2d-container {',
206
209
  ' position: fixed;',
207
- ' bottom: 0;',
208
- ' left: 0;',
210
+ ' top: 330px;',
211
+ ' left: 1240px;',
212
+ ' bottom: auto;',
209
213
  ' z-index: 9999;',
210
214
  // 容器本身不拦截指针事件,画布区域点击完全穿透到页面,不影响输入;
211
215
  // 只有拖拽图标与画布大小手柄单独开启 pointer-events。
@@ -243,52 +247,48 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
243
247
  '#dsh-live2d-container:hover .dsh-live2d-label {',
244
248
  ' opacity: 1;',
245
249
  '}',
246
- // 画布大小拖拽手柄:平时隐藏,鼠标悬停画布(容器)时在右下角出现,与移动图标重叠
247
- '#dsh-live2d-container .dsh-live2d-resize {',
248
- ' position: absolute;',
249
- ' right: 4px;',
250
- ' bottom: 4px;',
251
- ' width: 14px;',
252
- ' height: 14px;',
250
+ // 控制浮层:固定定位,位置由 JS 动态计算——理想位置是画布右下角,
251
+ // 但会被夹在视口内(画布拖出屏幕时,图标停在离画布右下角最近的屏幕边缘)。
252
+ // 浮层本身不拦截指针(pointer-events:none),只有里面的图标可点。
253
+ '#dsh-live2d-controls {',
254
+ ' position: fixed;',
255
+ ' z-index: 10000;',
256
+ ' display: flex;',
257
+ ' align-items: center;',
258
+ ' gap: 8px;',
259
+ ' pointer-events: none;',
260
+ '}',
261
+ // 画布大小拖拽手柄:常驻右下角浮层,与移动图标并排
262
+ '#dsh-live2d-controls .dsh-live2d-resize {',
263
+ ' width: 16px;',
264
+ ' height: 16px;',
253
265
  ' border-right: 2px solid rgba(96,165,250,0.9);',
254
266
  ' border-bottom: 2px solid rgba(96,165,250,0.9);',
255
267
  ' cursor: nwse-resize;',
256
- ' opacity: 0;',
257
- ' transition: opacity 0.2s;',
268
+ ' opacity: 0.7;',
258
269
  ' pointer-events: auto;',
259
- ' z-index: 1;',
260
- '}',
261
- '#dsh-live2d-container.hovering .dsh-live2d-resize {',
262
- ' opacity: 0.9;',
270
+ ' flex: none;',
263
271
  '}',
264
- // 拖拽移动图标:平时隐藏,鼠标悬停画布(容器)时出现,与画布大小手柄重叠显示在上方
265
- '#dsh-live2d-container .dsh-live2d-drag {',
266
- ' position: absolute;',
267
- ' right: 9px;',
268
- ' bottom: 9px;',
269
- ' width: 22px;',
270
- ' height: 22px;',
272
+ // 拖拽移动图标:常驻右下角浮层
273
+ '#dsh-live2d-controls .dsh-live2d-drag {',
274
+ ' width: 26px;',
275
+ ' height: 26px;',
271
276
  ' display: flex;',
272
277
  ' align-items: center;',
273
278
  ' justify-content: center;',
274
279
  ' border-radius: 6px;',
275
- ' background: rgba(31,41,55,0.65);',
276
- ' color: rgba(226,232,240,0.9);',
277
- ' font-size: 14px;',
280
+ ' background: rgba(31,41,55,0.75);',
281
+ ' color: rgba(226,232,240,0.95);',
282
+ ' font-size: 15px;',
278
283
  ' line-height: 1;',
279
284
  ' cursor: grab;',
280
285
  ' user-select: none;',
281
286
  ' pointer-events: auto;',
282
- ' opacity: 0;',
283
- ' transition: opacity 0.2s;',
284
- ' z-index: 2;',
285
- '}',
286
- '#dsh-live2d-container.hovering .dsh-live2d-drag {',
287
- ' opacity: 0.9;',
287
+ ' opacity: 0.85;',
288
+ ' flex: none;',
288
289
  '}',
289
- '#dsh-live2d-container.dragging .dsh-live2d-drag {',
290
+ '#dsh-live2d-controls .dsh-live2d-drag:active {',
290
291
  ' cursor: grabbing;',
291
- ' opacity: 0.9;',
292
292
  '}',
293
293
  ].join('\n')
294
294
 
@@ -691,17 +691,38 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
691
691
  * 会优先满足 bottom,拖动看起来"完全没反应"。
692
692
  */
693
693
  function place(containerEl, x, y) {
694
- // 容器是 position:fixed,left/top 相对视口;因此钳制也必须用视口尺寸,
695
- // 不能用 offsetWidth/offsetHeight(offsetParent 是 overlay 层,原点未必是视口 0,0)。
696
- const rect = containerEl.getBoundingClientRect()
697
- const maxX = Math.max(0, window.innerWidth - rect.width)
698
- const maxY = Math.max(0, window.innerHeight - rect.height)
699
- containerEl.style.left = Math.min(Math.max(x, 0), maxX) + 'px'
700
- containerEl.style.top = Math.min(Math.max(y, 0), maxY) + 'px'
694
+ // 画布可自由定位(允许拖出屏幕),仅写入 left/top;bottom 必须置 auto,
695
+ // 否则 left/top 与 bottom 同时生效时浏览器优先满足 bottom,拖动无反应。
696
+ containerEl.style.left = x + 'px'
697
+ containerEl.style.top = y + 'px'
701
698
  containerEl.style.bottom = 'auto'
702
699
  }
703
700
 
704
- function setupDrag(handleEl, containerEl) {
701
+ // 控制浮层跟随画布右下角:clamp=true 时被夹在视口内(画布在屏幕内时图标贴
702
+ // 画布右下角;画布拖出屏幕时图标停在最近屏幕边缘);clamp=false 时不夹边,
703
+ // 用于拖动画布大小手柄的过程中——否则手柄会被夹在视口底边,鼠标越过后即
704
+ // 收不到 pointermove,表现为"向下拖不动"。
705
+ function updateControls(containerEl, controlsEl, clamp = true) {
706
+ if (!containerEl || !controlsEl) return
707
+ const cr = containerEl.getBoundingClientRect()
708
+ const cs = controlsEl.getBoundingClientRect()
709
+ const w = cs.width || 60
710
+ const h = cs.height || 34
711
+ const OFFSET = 4 // 图标相对画布右下角内缩一点
712
+ const MARGIN = 4 // 离屏幕边缘的最小间距
713
+ let x = cr.right - w - OFFSET
714
+ let y = cr.bottom - h - OFFSET
715
+ if (clamp) {
716
+ x = Math.min(Math.max(x, MARGIN), window.innerWidth - w - MARGIN)
717
+ y = Math.min(Math.max(y, MARGIN), window.innerHeight - h - MARGIN)
718
+ }
719
+ controlsEl.style.left = x + 'px'
720
+ controlsEl.style.top = y + 'px'
721
+ controlsEl.style.right = 'auto'
722
+ controlsEl.style.bottom = 'auto'
723
+ }
724
+
725
+ function setupDrag(handleEl, containerEl, controlsEl) {
705
726
  let pointerId = null
706
727
  let startX = 0
707
728
  let startY = 0
@@ -715,6 +736,9 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
715
736
  const saved = getPosition()
716
737
  if (saved && Number.isFinite(saved.x) && Number.isFinite(saved.y)) {
717
738
  place(containerEl, saved.x, saved.y)
739
+ } else {
740
+ // 无本地存档时使用默认位置
741
+ place(containerEl, DEFAULT_POSITION.x, DEFAULT_POSITION.y)
718
742
  }
719
743
 
720
744
  const finish = () => {
@@ -756,6 +780,7 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
756
780
  curX = origX + (e.clientX - startX)
757
781
  curY = origY + (e.clientY - startY)
758
782
  place(containerEl, curX, curY)
783
+ updateControls(containerEl, controlsEl)
759
784
  e.preventDefault()
760
785
  }
761
786
 
@@ -855,6 +880,7 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
855
880
  function Live2DWidget() {
856
881
  const containerRef = useRef(null)
857
882
  const dragHandleRef = useRef(null)
883
+ const controlsRef = useRef(null)
858
884
  const engineRef = useRef(null)
859
885
  const cleanupDragRef = useRef(null)
860
886
  const lastStateRef = useRef(DSHState.IDLE)
@@ -887,28 +913,19 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
887
913
  engine.init(containerRef.current).then((ok) => {
888
914
  if (ok) {
889
915
  engine.playAnimation('IDLE')
916
+ updateControls(containerRef.current, controlsRef.current)
890
917
  }
891
918
  })
892
919
 
893
- cleanupDragRef.current = setupDrag(dragHandleRef.current, containerRef.current)
920
+ cleanupDragRef.current = setupDrag(dragHandleRef.current, containerRef.current, controlsRef.current)
894
921
 
895
- // 悬停探测:容器 pointer-events:none 导致 CSS :hover 只在子元素上触发,
896
- // 这里用 window 级 mousemove 判断指针是否落在画布矩形内,动态切换 hovering 类,
897
- // 实现"鼠标在画布上时显示两个手柄",同时画布本身不拦截输入。
898
- const onMouseMove = (e) => {
899
- const el = containerRef.current
900
- if (!el) return
901
- const rect = el.getBoundingClientRect()
902
- const inside = e.clientX >= rect.left && e.clientX <= rect.right &&
903
- e.clientY >= rect.top && e.clientY <= rect.bottom
904
- if (inside) el.classList.add('hovering')
905
- else el.classList.remove('hovering')
906
- }
907
- window.addEventListener('mousemove', onMouseMove)
922
+ // 窗口尺寸变化时把图标夹回视口内(画布可能因此相对移出屏幕)
923
+ const onResize = () => updateControls(containerRef.current, controlsRef.current)
924
+ window.addEventListener('resize', onResize)
908
925
 
909
926
  return () => {
910
927
  cleanupDragRef.current?.()
911
- window.removeEventListener('mousemove', onMouseMove)
928
+ window.removeEventListener('resize', onResize)
912
929
  engine.destroy()
913
930
  }
914
931
  }, [])
@@ -941,6 +958,7 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
941
958
  if (!engine || !engine.ready) return
942
959
  const factor = e.deltaY < 0 ? 1.08 : 1 / 1.08
943
960
  engine.zoomBy(factor)
961
+ updateControls(containerRef.current, controlsRef.current)
944
962
  }, [])
945
963
 
946
964
  // 画布大小手柄:拖动右下角改变 canvasWidth/canvasHeight
@@ -953,20 +971,27 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
953
971
 
954
972
  const startX = e.clientX
955
973
  const startY = e.clientY
956
- const rect = containerEl.getBoundingClientRect()
957
- const startW = rect.width
958
- const startH = rect.height
974
+ // 起点尺寸必须取 canvas 自身(CSS 像素),不能取 container 的
975
+ // getBoundingClientRect():container 是 pointer-events:none 且内含一个
976
+ // position:fixed 的控制浮层,某些布局下其 rect 高度会塌缩为 0 / 与画布
977
+ // 不一致,导致 startH 错乱、向下拖时 h 计算异常、画布大小"拖不动"。
978
+ const canvasEl = engine.canvas
979
+ const startW = canvasEl ? canvasEl.offsetWidth : containerEl.getBoundingClientRect().width
980
+ const startH = canvasEl ? canvasEl.offsetHeight : containerEl.getBoundingClientRect().height
959
981
  const pointerId = e.pointerId
960
982
 
961
- try { e.target.setPointerCapture(pointerId) } catch {}
983
+ try { e.currentTarget.setPointerCapture(pointerId) } catch {}
962
984
 
963
985
  const move = (ev) => {
964
986
  if (ev.pointerId !== pointerId) return
965
987
  const dw = ev.clientX - startX
966
988
  const dh = ev.clientY - startY
989
+ // 拖动过程中不夹边(clamp=false):手柄自由跟随画布右下角,向下拖
990
+ // 时不会被夹在视口底边、鼠标越过后收不到 pointermove 而"拖不动"。
967
991
  const w = Math.min(800, Math.max(100, startW + dw))
968
992
  const h = Math.min(800, Math.max(100, startH + dh))
969
993
  engine.setCanvasSize(w, h)
994
+ updateControls(containerEl, controlsRef.current, false)
970
995
  }
971
996
  const up = (ev) => {
972
997
  if (ev.pointerId !== pointerId) return
@@ -974,6 +999,8 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
974
999
  window.removeEventListener('pointermove', move)
975
1000
  window.removeEventListener('pointerup', up)
976
1001
  window.removeEventListener('pointercancel', up)
1002
+ // 松手后把图标夹回视口内(静止状态始终留在网页可见区域)。
1003
+ updateControls(containerEl, controlsRef.current, true)
977
1004
  }
978
1005
  window.addEventListener('pointermove', move)
979
1006
  window.addEventListener('pointerup', up)
@@ -985,18 +1012,25 @@ window.__ModuleLoader__.load({ id: 'dsh-live2d', factory: (require) => {
985
1012
  id: 'dsh-live2d-container',
986
1013
  onWheel: onWheel,
987
1014
  },
988
- // 右下角拖拽移动图标:只有拖动它才能移动整个看板娘,画布本身不再拦截输入
989
- h('div', {
990
- ref: dragHandleRef,
991
- className: 'dsh-live2d-drag',
992
- title: '拖动移动看板娘;鼠标滚轮缩放模型大小',
993
- }, '✥'),
994
- // 画布大小手柄:仅鼠标悬停画布(容器)时显示
1015
+ // 控制浮层:固定钉在视口右下角,独立于画布。画布可以拖出屏幕,
1016
+ // 但这两个图标始终留在页面内可见可点。
995
1017
  h('div', {
996
- className: 'dsh-live2d-resize',
997
- title: '拖动改变画布大小',
998
- onPointerDown: onResizePointerDown,
999
- }),
1018
+ ref: controlsRef,
1019
+ id: 'dsh-live2d-controls',
1020
+ },
1021
+ // 画布大小手柄
1022
+ h('div', {
1023
+ className: 'dsh-live2d-resize',
1024
+ title: '拖动改变画布大小',
1025
+ onPointerDown: onResizePointerDown,
1026
+ }),
1027
+ // 拖拽移动图标:只有拖动它才能移动整个看板娘
1028
+ h('div', {
1029
+ ref: dragHandleRef,
1030
+ className: 'dsh-live2d-drag',
1031
+ title: '拖动移动看板娘;鼠标滚轮缩放模型大小',
1032
+ }, '✥'),
1033
+ ),
1000
1034
  )
1001
1035
  }
1002
1036
 
package/lib/index.js CHANGED
@@ -37,7 +37,7 @@ const PLUGIN_ROOT = resolvedDir ? join(resolvedDir, '..') : null
37
37
  const LIB_DIR = PLUGIN_ROOT ? join(PLUGIN_ROOT, 'lib') : null
38
38
  const ASSETS_DIR = PLUGIN_ROOT ? join(PLUGIN_ROOT, 'assets') : null
39
39
 
40
- export const name = 'dsh-live2d'
40
+ export const name = '@lrplrplrp/dsh-live2d'
41
41
 
42
42
  const STATE_ENDPOINT = '/plugins/dsh-live2d/state'
43
43
  const CONFIG_ENDPOINT = '/plugins/dsh-live2d/config'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lrplrplrp/dsh-live2d",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Live2D 看板娘插件 for DeepSeek Harness — 可拖动的 Live2D 模型,支持 DSH 状态驱动动画、本地模型导入和完整配置页。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",