dsh-ide-git 0.5.3 → 0.5.5

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/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-ide-git",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "main": "./src/index.js",
5
5
  "description": "IDE 级 Git 操作面板:分支树 + 提交图谱 + 变更/暂存/提交 + 提交详情。装了 dsh-better-sidebar 时挂成它的原生 tab(右侧栏 + 底部工作台);没装时直接挂 DSH 原生右侧栏,单独安装即可用。JetBrains 风格 Git 工具窗口 for DeepSeek Harness.",
6
6
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ide-git",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "DSH plugin: an IDE-grade Git tool window as a native dsh-better-sidebar tab — branch tree, commit graph, changes, commit details, JetBrains-style actions, in the right sidebar and the bottom panel. | DSH 插件:IDE 级 Git 工具窗口,以 dsh-better-sidebar 原生 Tab 挂载——分支树 / 提交图谱 / 变更与提交详情 / JetBrains 风格操作,右侧栏与底部面板双布局自适应。",
5
5
  "type": "module",
6
6
  "repository": {
package/src/client.js CHANGED
@@ -34,6 +34,10 @@ window.__ModuleLoader__.load({
34
34
  locale registry. */
35
35
  const LOCALE_NS = 'dsh-ide-git'
36
36
  const LANE_WIDTH = 14
37
+ /* Hard cap on the drawn lane columns. Lanes beyond the cap collapse into
38
+ the last column: without the cap a branchy repository pushed the graph
39
+ wider than the panel and shoved the subject column out of view. */
40
+ const GRAPH_MAX_LANES = 7
37
41
  const LANE_COLORS = ['#4d6bfe', '#e2a03f', '#3fb950', '#d2679b', '#59b0d6', '#b083f0', '#d2694a', '#8a9aa8']
38
42
  const AUTO_REFRESH_MS = 12000
39
43
  const REPO_KEY = 'dsh-ide-git.repo.v1'
@@ -3521,7 +3525,23 @@ window.__ModuleLoader__.load({
3521
3525
  /* ============================== commit graph ============================== */
3522
3526
 
3523
3527
  function buildRows(commits) {
3528
+ /* The log is a WINDOW: the oldest visible commits usually have parents
3529
+ that were never loaded. A parent outside the window must not park its
3530
+ hash on a lane — the lane would stay occupied for the rest of the
3531
+ page (the hash can never be consumed), every later branch tip would
3532
+ claim yet another fresh lane, and the drawing degenerated into a
3533
+ bundle of independent parallel lines that pushed the subjects out of
3534
+ the panel. So the pass first learns which hashes are actually on the
3535
+ board, and an edge to anything else simply ends at its commit. */
3536
+ const present = new Set()
3537
+ for (const commit of commits) present.add(commit.hash)
3524
3538
  const lanes = []
3539
+ /* The current branch keeps the LEFTMOST column whenever it is on the
3540
+ board: pre-seeding its hash makes that commit claim lane 0 when its
3541
+ row arrives, so HEAD's line never drifts to the right of a side
3542
+ branch (the rest of the lanes fill newest-first, see below). */
3543
+ const headCommit = commits.find((commit) => Array.isArray(commit.refs) && commit.refs.some((ref) => String(ref).indexOf('HEAD') >= 0))
3544
+ if (headCommit !== undefined) lanes.push(headCommit.hash)
3525
3545
  const rows = []
3526
3546
  for (const commit of commits) {
3527
3547
  const before = lanes.slice()
@@ -3538,24 +3558,30 @@ window.__ModuleLoader__.load({
3538
3558
  a branch that merged back into a lane to its left kept a stub of its
3539
3559
  own colour running on above the junction — a fork that never
3540
3560
  happened. Only a parent that is not on the board yet inherits the
3541
- lane, which is what keeps an ordinary line continuous. */
3561
+ lane, and (v0.5.4) only a parent that is on the board at all: a
3562
+ parent past the loaded window ends the lane at this row instead of
3563
+ holding it for a commit that will never arrive. */
3542
3564
  if (parents.length > 0) {
3543
3565
  const first = lanes.indexOf(parents[0])
3544
3566
  if (first !== -1 && first !== lane) {
3545
3567
  lanes[lane] = null
3546
3568
  parentLanes.push(first)
3547
- } else {
3569
+ } else if (present.has(parents[0])) {
3548
3570
  lanes[lane] = parents[0]
3571
+ } else {
3572
+ lanes[lane] = null
3549
3573
  }
3550
3574
  } else {
3551
3575
  lanes[lane] = null
3552
3576
  }
3553
3577
  for (let index = 1; index < parents.length; index += 1) {
3554
- let slot = lanes.indexOf(parents[index])
3578
+ const parent = parents[index]
3579
+ if (!present.has(parent)) continue
3580
+ let slot = lanes.indexOf(parent)
3555
3581
  if (slot === -1) {
3556
3582
  slot = lanes.indexOf(null)
3557
- if (slot === -1) { lanes.push(parents[index]); slot = lanes.length - 1 }
3558
- else lanes[slot] = parents[index]
3583
+ if (slot === -1) { lanes.push(parent); slot = lanes.length - 1 }
3584
+ else lanes[slot] = parent
3559
3585
  }
3560
3586
  parentLanes.push(slot)
3561
3587
  }
@@ -3573,28 +3599,46 @@ window.__ModuleLoader__.load({
3573
3599
  centre where the lane genuinely starts (a branch tip) or ends (a root).
3574
3600
  v0.1.7 started every non-own lane at 0 and every own lane at height/2, which
3575
3601
  left a visible gap between two consecutive commits on the same lane. */
3576
- function GraphCell(props) {
3577
- const row = props.row
3578
- const height = props.height
3579
- const width = Math.max(1, row.width) * LANE_WIDTH + 8
3602
+ /* Vertical lane segments for one row. A lane that this row OPENS through an
3603
+ edge curve (it was empty above, and the curve lands at the row bottom) must
3604
+ NOT also get a vertical segment from the row centre: the two overlapped and
3605
+ left a floating hook where the line visibly started in mid-air — reported
3606
+ twice by the user as "断开的线/无端点的分叉". The curve alone carries the
3607
+ connection, and the next row's segment starts at its top edge. */
3608
+ function laneSegments(row, height) {
3580
3609
  const middle = height / 2
3581
- const children = []
3582
- const x = (lane) => 8 + lane * LANE_WIDTH
3583
- const color = (lane) => LANE_COLORS[lane % LANE_COLORS.length]
3584
3610
  const depth = Math.max(row.before.length, row.after.length)
3611
+ const segments = []
3585
3612
  for (let lane = 0; lane < depth; lane += 1) {
3586
3613
  const above = lane < row.before.length ? row.before[lane] : null
3587
3614
  const below = lane < row.after.length ? row.after[lane] : null
3588
3615
  const hasTop = above !== null && above !== undefined
3589
3616
  const hasBottom = below !== null && below !== undefined
3590
3617
  if (hasTop === false && hasBottom === false) continue
3618
+ if (hasTop === false && row.parentLanes.indexOf(lane) >= 0) continue
3591
3619
  const y1 = hasTop ? 0 : middle
3592
3620
  const y2 = hasBottom ? height : middle
3593
3621
  if (y1 === y2) continue
3622
+ segments.push({ lane: lane, y1: y1, y2: y2 })
3623
+ }
3624
+ return segments
3625
+ }
3626
+
3627
+ function GraphCell(props) {
3628
+ const row = props.row
3629
+ const height = props.height
3630
+ const width = Math.min(Math.max(1, row.width), GRAPH_MAX_LANES) * LANE_WIDTH + 8
3631
+ const middle = height / 2
3632
+ const children = []
3633
+ /* Lanes beyond the cap share the last column: the geometry stays bounded
3634
+ and the subject column keeps its room. */
3635
+ const x = (lane) => 8 + Math.min(lane, GRAPH_MAX_LANES - 1) * LANE_WIDTH
3636
+ const color = (lane) => LANE_COLORS[lane % LANE_COLORS.length]
3637
+ for (const segment of laneSegments(row, height)) {
3594
3638
  children.push(E('line', {
3595
- key: 'lane-' + lane,
3596
- x1: x(lane), y1: y1, x2: x(lane), y2: y2,
3597
- stroke: color(lane), strokeWidth: 2, strokeLinecap: 'round',
3639
+ key: 'lane-' + segment.lane,
3640
+ x1: x(segment.lane), y1: segment.y1, x2: x(segment.lane), y2: segment.y2,
3641
+ stroke: color(segment.lane), strokeWidth: 2, strokeLinecap: 'round',
3598
3642
  }))
3599
3643
  }
3600
3644
  for (const parentLane of row.parentLanes) {
@@ -3898,20 +3942,56 @@ window.__ModuleLoader__.load({
3898
3942
 
3899
3943
  function RepoSelect(props) {
3900
3944
  const t = props.t
3945
+ /* While the workspace is still being scanned the panel used to print "no
3946
+ repository found" — a verdict nobody had reached yet. A deep workspace
3947
+ scan takes seconds, so that premature verdict sat on screen long enough
3948
+ to look like the final answer. Loading shows loading; "not found" only
3949
+ shows once the scan came back empty. */
3950
+ if (props.loading === true) {
3951
+ return E('span', { className: 'dig-repo-static' },
3952
+ E(Icon, { name: 'folder', size: 12 }),
3953
+ E('span', { className: 'dig-repo-name' }, t('status.loading')))
3954
+ }
3901
3955
  if (props.repos.length <= 1) {
3902
3956
  const only = props.repos.length === 1 ? props.repos[0] : null
3903
3957
  return E('span', { className: 'dig-repo-static', title: only === null ? '' : only.path },
3904
3958
  E(Icon, { name: 'folder', size: 12 }),
3905
3959
  E('span', { className: 'dig-repo-name' }, only === null ? t('repo.empty') : only.name))
3906
3960
  }
3961
+ /* The switcher is a button over a panel-internal menu, not a native
3962
+ <select>: the OS-drawn popup ignores the panel theme entirely (light
3963
+ system colours over a dark translucent panel) and was unreadable. */
3964
+ let current = null
3965
+ for (const repo of props.repos) { if (repo.path === props.value) { current = repo; break } }
3907
3966
  return E('span', { className: 'dig-select-wrap', title: t('repo.switch') },
3908
3967
  E(Icon, { name: 'folder', size: 12 }),
3909
- E('select', {
3910
- className: 'dig-select dig-select-repo',
3911
- value: props.value === null ? '' : props.value,
3912
- onChange: (event) => props.onChange(event.target.value),
3913
- }, props.repos.map((repo) => E('option', { key: repo.path, value: repo.path },
3914
- repo.name + (repo.branch === null || repo.branch === undefined ? '' : ' · ' + repo.branch)))))
3968
+ E('button', {
3969
+ type: 'button',
3970
+ className: 'dig-select dig-select-btn dig-select-repo',
3971
+ onClick: props.onOpenMenu,
3972
+ },
3973
+ E('span', { className: 'dig-repo-name' },
3974
+ current === null ? t('repo.pick') : current.name + (current.branch === null || current.branch === undefined ? '' : ' · ' + current.branch)),
3975
+ E(Icon, { name: 'chevron', size: 12 })))
3976
+ }
3977
+
3978
+ /* The history filters were the LAST native <select>s: their popup is drawn
3979
+ by the OS, so a dark translucent panel still got a light system list —
3980
+ reported on v0.5.4 ("only the one at the top was replaced"). Same
3981
+ treatment as the header switchers: a button over a panel-internal menu. */
3982
+ function FilterSelect(props) {
3983
+ const options = Array.isArray(props.options) ? props.options : []
3984
+ let current = options[0]
3985
+ for (const option of options) { if (option.value === props.value) { current = option; break } }
3986
+ const label = props.label + ': ' + (current === undefined ? '' : current.label)
3987
+ return E('button', {
3988
+ type: 'button',
3989
+ className: 'dig-filter-select' + (props.value === '' ? '' : ' dig-filter-on'),
3990
+ title: props.hint === undefined ? label : props.hint,
3991
+ onClick: (event) => { if (props.onOpenMenu !== undefined) props.onOpenMenu(event, options) },
3992
+ },
3993
+ E('span', { className: 'dig-filter-label' }, label),
3994
+ E(Icon, { name: 'chevron', size: 11 }))
3915
3995
  }
3916
3996
 
3917
3997
  function RepoPicker(props) {
@@ -4336,31 +4416,45 @@ window.__ModuleLoader__.load({
4336
4416
  applyPath('')
4337
4417
  }
4338
4418
 
4339
- const filterBar = props.hideSearch === true ? null : E('div', { className: 'dig-filters' },
4419
+ /* Every filter opens the same in-panel menu; a checked row marks the active
4420
+ value, so the button never has to spell the whole list out. */
4421
+ const openFilterMenu = (event, options, currentValue, onPick) => {
4422
+ if (props.openMenuAt === undefined) return
4423
+ props.openMenuAt(event, options.map((option) => ({
4424
+ id: 'filter:' + String(option.value),
4425
+ icon: option.value === '' ? 'filter' : 'check',
4426
+ tone: option.value === '' ? 'secondary' : 'accent',
4427
+ label: option.label,
4428
+ active: option.value === currentValue,
4429
+ run: () => onPick(option.value),
4430
+ })))
4431
+ }
4432
+ const allOption = { value: '', label: t('filter.all') }
4433
+ const filterBar = props.hideSearch === true ? null : E('div', { className: 'dig-filters' },
4340
4434
  E('input', {
4341
4435
  className: 'dig-input dig-input-compact dig-filter-text', value: text, spellCheck: false,
4342
4436
  placeholder: t('history.filter'),
4343
4437
  onChange: (event) => setText(event.target.value),
4344
4438
  }),
4345
- E('select', {
4346
- className: 'dig-filter-select', value: refName, title: t('filter.branch'),
4347
- onChange: (event) => setRefName(event.target.value),
4348
- }, [E('option', { key: '', value: '' }, t('filter.branch') + ': ' + t('filter.all'))].concat(
4349
- refOptions.map((name) => E('option', { key: name, value: name }, name)))),
4350
- E('select', {
4351
- className: 'dig-filter-select', value: author, title: t('filter.user'),
4352
- onChange: (event) => setAuthor(event.target.value),
4353
- }, [E('option', { key: '', value: '' }, t('filter.user') + ': ' + t('filter.all'))].concat(
4354
- authors.map((name) => E('option', { key: name, value: name }, name)))),
4355
- E('select', {
4356
- className: 'dig-filter-select', value: since, title: t('filter.date'),
4357
- onChange: (event) => setSince(event.target.value),
4358
- },
4359
- E('option', { value: '' }, t('filter.date') + ': ' + t('filter.all')),
4360
- E('option', { value: 'today' }, t('filter.today')),
4361
- E('option', { value: 'week' }, t('filter.week')),
4362
- E('option', { value: 'month' }, t('filter.month')),
4363
- E('option', { value: 'year' }, t('filter.year'))),
4439
+ E(FilterSelect, {
4440
+ label: t('filter.branch'), value: refName, hint: t('filter.branch'),
4441
+ options: [allOption].concat(refOptions.map((name) => ({ value: name, label: name }))),
4442
+ onOpenMenu: (event, options) => openFilterMenu(event, options, refName, setRefName),
4443
+ }),
4444
+ E(FilterSelect, {
4445
+ label: t('filter.user'), value: author, hint: t('filter.user'),
4446
+ options: [allOption].concat(authors.map((name) => ({ value: name, label: name }))),
4447
+ onOpenMenu: (event, options) => openFilterMenu(event, options, author, setAuthor),
4448
+ }),
4449
+ E(FilterSelect, {
4450
+ label: t('filter.date'), value: since, hint: t('filter.date'),
4451
+ options: [allOption,
4452
+ { value: 'today', label: t('filter.today') },
4453
+ { value: 'week', label: t('filter.week') },
4454
+ { value: 'month', label: t('filter.month') },
4455
+ { value: 'year', label: t('filter.year') }],
4456
+ onOpenMenu: (event, options) => openFilterMenu(event, options, since, setSince),
4457
+ }),
4364
4458
  E('form', {
4365
4459
  className: 'dig-filter-path',
4366
4460
  onSubmit: (event) => { event.preventDefault(); applyPath(pathDraft.trim()) },
@@ -4550,7 +4644,10 @@ window.__ModuleLoader__.load({
4550
4644
  }, [repoRoot])
4551
4645
 
4552
4646
  useEffect(() => {
4553
- if (cwd === undefined) return undefined
4647
+ /* No workspace attached: there is nothing to scan, so land the empty
4648
+ result straight away instead of leaving repoState null (which reads
4649
+ as "still loading" and would spin forever). */
4650
+ if (cwd === undefined) { setRepoState({ repos: [], isRepo: false }); return undefined }
4554
4651
  let cancelled = false
4555
4652
  void (async () => {
4556
4653
  try {
@@ -4860,15 +4957,47 @@ window.__ModuleLoader__.load({
4860
4957
  const repoOptions = repoState === null ? [] : repoState.repos
4861
4958
  const dirty = summary === null ? 0 : summary.changes.staged.length + summary.changes.unstaged.length + summary.changes.untracked.length + summary.changes.conflicted.length
4862
4959
 
4960
+ /* Both header switchers are menu-driven now (see RepoSelect): the
4961
+ popovers reuse openMenuAt, so they clamp to the panel box and share
4962
+ the opaque, frosted menu surface instead of the OS select popup. */
4963
+ const openRepoMenu = useCallback((event) => {
4964
+ openMenuAt(event, repoOptions.map((repo) => ({
4965
+ id: 'repo:' + repo.path,
4966
+ icon: 'folder',
4967
+ tone: 'accent',
4968
+ label: repo.name + (repo.branch === null || repo.branch === undefined ? '' : ' · ' + repo.branch),
4969
+ active: repo.path === repoRoot,
4970
+ run: () => { if (repo.path !== repoRoot) pickRepo(repo.path) },
4971
+ })))
4972
+ }, [openMenuAt, repoOptions, repoRoot, pickRepo])
4973
+
4974
+ const openBranchSwitchMenu = useCallback((event) => {
4975
+ const local = branches === null ? [] : branches.local
4976
+ openMenuAt(event, local.map((entry) => ({
4977
+ id: 'switch:' + entry.name,
4978
+ icon: entry.head === true ? 'star' : 'branch',
4979
+ tone: entry.head === true ? 'warn' : 'accent',
4980
+ label: entry.name,
4981
+ active: entry.head === true,
4982
+ run: () => { if (entry.head !== true) void checkout(entry) },
4983
+ })))
4984
+ }, [openMenuAt, branches, checkout])
4985
+
4863
4986
  const topBar = E('div', { className: 'dig-topbar' },
4864
- E(RepoSelect, { t: t, repos: repoOptions, value: repoRoot, onChange: pickRepo }),
4987
+ E(RepoSelect, {
4988
+ t: t, repos: repoOptions, value: repoRoot,
4989
+ loading: repoState === null,
4990
+ onOpenMenu: openRepoMenu,
4991
+ }),
4865
4992
  branches === null || branches.local.length === 0 ? null : E('span', { className: 'dig-select-wrap', title: t('branch.switch') },
4866
4993
  E(Icon, { name: 'branch', size: 12 }),
4867
- E('select', {
4868
- className: 'dig-select dig-select-branch',
4869
- value: branches.branch,
4870
- onChange: (event) => { void run('checkout', { branch: event.target.value }) },
4871
- }, branches.local.map((entry) => E('option', { key: entry.name, value: entry.name }, entry.name)))),
4994
+ E('button', {
4995
+ type: 'button',
4996
+ className: 'dig-select dig-select-btn dig-select-branch',
4997
+ onClick: openBranchSwitchMenu,
4998
+ },
4999
+ E('span', { className: 'dig-repo-name' }, branches.branch),
5000
+ E(Icon, { name: 'chevron', size: 12 }))),
4872
5001
  summary === null || summary.upstream === null ? null : E('span', { className: 'dig-track' },
4873
5002
  (summary.ahead > 0 ? '↑' + summary.ahead : '') + (summary.behind > 0 ? ' ↓' + summary.behind : '')),
4874
5003
  operation === null ? null : E('span', { className: 'dig-opchip', title: t('operation.hint') }, t('operation.' + operation)),
@@ -4918,7 +5047,7 @@ window.__ModuleLoader__.load({
4918
5047
  })
4919
5048
  : E(HistoryList, {
4920
5049
  t: t, commits: commits, hasMore: hasMore, busy: busy, selectedHash: selectedHash,
4921
- hideSearch: compact, pathFilter: pathFilter,
5050
+ hideSearch: compact, pathFilter: pathFilter, openMenuAt: openMenuAt,
4922
5051
  onPathFilter: (value) => setPathFilter(value),
4923
5052
  onSelect: (commit) => { void selectCommit(commit) },
4924
5053
  onMenu: commitMenu,
@@ -5098,7 +5227,12 @@ window.__ModuleLoader__.load({
5098
5227
  /* ---------- body per chrome ---------- */
5099
5228
 
5100
5229
  let body
5101
- if (repoRoot === null) {
5230
+ if (repoState === null) {
5231
+ /* The scan is still running: a neutral loading state, never the
5232
+ "no repository found" verdict (that text is reserved for a scan
5233
+ that actually came back empty). */
5234
+ body = E('div', { className: 'dig-body' }, E('div', { className: 'dig-empty' }, t('status.loading')))
5235
+ } else if (repoRoot === null) {
5102
5236
  body = E('div', { className: 'dig-body' }, E(RepoPicker, {
5103
5237
  t: t, cwd: cwd === undefined ? '' : cwd, repos: repoOptions, onPick: pickRepo,
5104
5238
  }))
@@ -5282,7 +5416,10 @@ window.__ModuleLoader__.load({
5282
5416
  '.dig-select{appearance:none;-webkit-appearance:none;background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-primary);font:inherit;font-weight:500;padding:1px 6px;height:22px;max-width:190px;min-width:0;cursor:pointer;text-overflow:ellipsis}',
5283
5417
  '.dig-select:hover{background:var(--dsw-alias-interactive-bg-hover)}',
5284
5418
  '.dig-select:focus{outline:1px solid var(--dsw-alias-brand-primary);outline-offset:-1px}',
5419
+ '.dig-select-btn{display:inline-flex;align-items:center;gap:4px;text-align:left;white-space:nowrap;overflow:hidden}',
5420
+ '.dig-select-btn .dig-repo-name{flex:1;min-width:0;max-width:none}',
5285
5421
  '.dig-select-repo{max-width:200px}',
5422
+ '.dig-select option,.dig-filter-select option{background:var(--dsw-alias-bg-layer-1,#22262e);color:var(--dsw-alias-label-primary,#e6e8ee)}',
5286
5423
  '.dig-select-branch{max-width:150px}',
5287
5424
  '.dig-repo-static{display:inline-flex;align-items:center;gap:5px;min-width:0;color:var(--dsw-alias-label-secondary)}',
5288
5425
  '.dig-repo-name{color:var(--dsw-alias-label-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px}',
@@ -5415,6 +5552,9 @@ window.__ModuleLoader__.load({
5415
5552
  '.dig-filter-text{flex:1 1 90px;min-width:80px;width:auto}',
5416
5553
  '.dig-filter-select{flex:none;max-width:118px;appearance:none;-webkit-appearance:none;background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);font:inherit;font-weight:500;height:22px;padding:0 4px;cursor:pointer;text-overflow:ellipsis}',
5417
5554
  '.dig-filter-select:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
5555
+ '.dig-filter-select{display:inline-flex;align-items:center;gap:3px}',
5556
+ '.dig-filter-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:96px}',
5557
+ '.dig-filter-on{color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}',
5418
5558
  '.dig-filter-path{flex:0 1 112px;min-width:70px;display:flex}',
5419
5559
  '.dig-load-more{margin:6px auto;display:block;padding:3px 12px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer}',
5420
5560
  '.dig-load-more:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
@@ -5478,7 +5618,7 @@ window.__ModuleLoader__.load({
5478
5618
  '.dig-dialog-text{color:var(--dsw-alias-label-secondary);white-space:pre-wrap}',
5479
5619
  '.dig-dialog-actions{display:flex;justify-content:flex-end;gap:8px}',
5480
5620
  '.dig-dialog-actions .dig-btn-primary{margin-left:0}',
5481
- '.dig-menu{position:absolute;z-index:70;min-width:200px;max-width:calc(100% - 8px);max-height:calc(100% - 8px);overflow:auto;overscroll-behavior:contain;padding:4px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));border:1px solid var(--dsw-alias-border-l2);box-shadow:0 10px 28px rgba(0,0,0,.35);display:flex;flex-direction:column}',
5621
+ '.dig-menu{position:absolute;z-index:70;min-width:200px;max-width:calc(100% - 8px);max-height:calc(100% - 8px);overflow:auto;overscroll-behavior:contain;padding:4px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));-webkit-backdrop-filter:var(--dsh-any-blur-card-panels,blur(14px) saturate(1.2));backdrop-filter:var(--dsh-any-blur-card-panels,blur(14px) saturate(1.2));border:1px solid var(--dsw-alias-border-l2);box-shadow:0 10px 28px rgba(0,0,0,.35);display:flex;flex-direction:column}',
5482
5622
  '.dig-menu-item{display:flex;align-items:center;gap:8px;padding:4px 8px;border:none;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-weight:500;text-align:left;border-radius:5px;cursor:pointer;white-space:nowrap;overflow:hidden}',
5483
5623
  '.dig-menu-icon{display:inline-flex;flex:none}',
5484
5624
  '.dig-menu-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}',