@lijian-ui/dsh-file-manager 0.1.21 → 0.2.0

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.
@@ -53,7 +53,6 @@ export declare class PanelLayoutController {
53
53
  private explorerCol;
54
54
  private explorerHandle;
55
55
  private previewHandle;
56
- private floatingButton;
57
56
  private styleObserver;
58
57
  private sizeObserver;
59
58
  private waitObserver;
@@ -82,9 +81,6 @@ export declare class PanelLayoutController {
82
81
  * the shell has no header (standalone installs, desktop variants).
83
82
  */
84
83
  private findHeaderBottom;
85
- /** Position the floating button: docked at the top-right corner, just
86
- * below the shell header's bottom divider (fallback: the chevron row). */
87
- private positionFloatingButton;
88
84
  /** Apply one store update with transitions disabled for exactly one frame. */
89
85
  private instant;
90
86
  /** Re-write the frame grid and reposition handles + floating button. */
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/client/layout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAiBH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAK7C,iFAAiF;AACjF,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAEpD;AAgBD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBvD;AAED,yEAAyE;AACzE,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,KAAK,CAAA;AACvC,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAChF,MAAM,CAQR;AAED,+EAA+E;AAC/E,qBAAa,qBAAqB;IAiBpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAhBnC,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,UAAU,CAAI;IACtB,4EAA4E;IAC5E,OAAO,CAAC,aAAa,CAA2B;IAChD,wFAAwF;IACxF,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,SAAS,CAAwB;gBACZ,MAAM,EAAE,WAAW;IAEhD,+DAA+D;IAC/D,KAAK,IAAI,IAAI;IAYb,4EAA4E;IAC5E,OAAO,CAAC,MAAM;IAwId,8DAA8D;IAC9D,OAAO,CAAC,YAAY;IAkEpB,uEAAuE;IACvE,cAAc,IAAI,IAAI;IActB,uEAAuE;IACvE,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAOnC;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAWxB;8EAC0E;IAC1E,OAAO,CAAC,sBAAsB;IAa9B,8EAA8E;IAC9E,OAAO,CAAC,OAAO;IAef,wEAAwE;IACxE,OAAO,CAAC,SAAS;IAuEjB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAoBtB,gEAAgE;IAChE,OAAO,CAAC,oBAAoB;IAK5B,yCAAyC;IACzC,OAAO,IAAI,IAAI;CAchB"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/client/layout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAcH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAK7C,iFAAiF;AACjF,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAEpD;AAgBD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBvD;AAED,yEAAyE;AACzE,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,KAAK,CAAA;AACvC,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAChF,MAAM,CAQR;AAED,+EAA+E;AAC/E,qBAAa,qBAAqB;IAiBpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAhBnC,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,aAAa,CAA8B;IAEnD,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,UAAU,CAAI;IACtB,4EAA4E;IAC5E,OAAO,CAAC,aAAa,CAA2B;IAChD,wFAAwF;IACxF,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,SAAS,CAAwB;gBACZ,MAAM,EAAE,WAAW;IAEhD,+DAA+D;IAC/D,KAAK,IAAI,IAAI;IAYb,4EAA4E;IAC5E,OAAO,CAAC,MAAM;IA0Hd,8DAA8D;IAC9D,OAAO,CAAC,YAAY;IAkEpB,uEAAuE;IACvE,cAAc,IAAI,IAAI;IActB,uEAAuE;IACvE,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAOnC;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAYxB,8EAA8E;IAC9E,OAAO,CAAC,OAAO;IAef,wEAAwE;IACxE,OAAO,CAAC,SAAS;IAiFjB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAkBtB,gEAAgE;IAChE,OAAO,CAAC,oBAAoB;IAK5B,yCAAyC;IACzC,OAAO,IAAI,IAAI;CAchB"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lijian-ui/dsh-file-manager",
3
3
  "description": "DSH web GUI right-panel system: a pixel-faithful re-implementation of FileManager's Explorer + Preview columns (file tree, filename search, git changes, multi-tab preview of 10+ formats) driven by real host-side fs/git services, hot-pluggable via a profile bundle without any dsh source changes",
4
- "version": "0.1.21",
4
+ "version": "0.2.0",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -0,0 +1,42 @@
1
+ /**
2
+ * DockItem — a round dock button for the conversation session header.
3
+ * Each plugin owns its own DockItem copy (terminal in dsh-term, file-panel in
4
+ * dsh-file-manager). Hover scales the button up via pure CSS — no JS event
5
+ * coordination, no shared motion value.
6
+ * @module dsh-file-manager/client/DockItem
7
+ */
8
+ import { type ReactElement, type ReactNode } from 'react'
9
+
10
+ const STYLE_ID = 'dsh-dock-item-style'
11
+
12
+ function ensureStyle(): void {
13
+ if (typeof document === 'undefined') return
14
+ if (document.getElementById(STYLE_ID) !== null) return
15
+ const tag = document.createElement('style')
16
+ tag.id = STYLE_ID
17
+ tag.textContent = '.dsh-dock-item{width:32px;height:32px;border:none;border-radius:9999px;background:#000;color:#fff;cursor:pointer;position:relative;transition:transform .15s ease,background .18s ease;display:inline-flex;align-items:center;justify-content:center}.dsh-dock-item:hover{transform:scale(1.15);background:#1a1a1a}.dsh-dock-item-active{background:var(--aion-primary,#6ea0ff)}.dsh-dock-tooltip{position:absolute;top:calc(100% + 6px);left:50%;transform:translateX(-50%);padding:2px 8px;font-size:12px;line-height:1.4;white-space:nowrap;color:#fff;background:rgba(20,20,30,.9);border:1px solid rgba(255,255,255,.12);border-radius:6px;opacity:0;pointer-events:none;transition:opacity .15s}.dsh-dock-item:hover .dsh-dock-tooltip{opacity:1}'
18
+ document.head.appendChild(tag)
19
+ }
20
+
21
+ ensureStyle()
22
+
23
+ export function DockItem(props: {
24
+ active: boolean
25
+ label: string
26
+ onClick: () => void
27
+ children: ReactNode
28
+ }): ReactElement {
29
+ return (
30
+ <button
31
+ type="button"
32
+ className={`dsh-dock-item${props.active ? ' dsh-dock-item-active' : ''}`}
33
+ onClick={props.onClick}
34
+ aria-label={props.label}
35
+ aria-pressed={props.active}
36
+ title={props.label}
37
+ >
38
+ <span className="dsh-dock-tooltip">{props.label}</span>
39
+ {props.children}
40
+ </button>
41
+ )
42
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * FilePanelDockItem — the file-panel dock button rendered into the conversation
3
+ * session header (the `conversation.session.header.utilities` slot).
4
+ *
5
+ * This plugin owns its own dock button independently: dsh-term renders its own
6
+ * terminal button, dsh-file-manager renders this file-panel button. The two
7
+ * are fully decoupled — install either alone and you get just its button;
8
+ * install both and both buttons appear side by side with shared magnification.
9
+ *
10
+ * Clicking broadcasts `dsh-dock:toggle-filepanel` (handled in dsh-file-manager
11
+ * index); the open state is mirrored back through `dsh-dock:filepanel-state`.
12
+ * @module dsh-file-manager/client/FilePanelDockItem
13
+ */
14
+ import { useEffect, useState, type ReactElement } from 'react'
15
+ import { DockItem } from './DockItem.tsx'
16
+
17
+ const EV = {
18
+ toggleFile: 'dsh-dock:toggle-filepanel',
19
+ fileState: 'dsh-dock:filepanel-state',
20
+ } as const
21
+
22
+ export function FilePanelDockItem(): ReactElement {
23
+ const [active, setActive] = useState(false)
24
+ useEffect(() => {
25
+ const onState = (e: Event): void => setActive(Boolean((e as CustomEvent).detail))
26
+ window.addEventListener(EV.fileState, onState)
27
+ return () => window.removeEventListener(EV.fileState, onState)
28
+ }, [])
29
+ return (
30
+ <DockItem
31
+ active={active}
32
+ label="文件面板"
33
+ onClick={() => window.dispatchEvent(new CustomEvent(EV.toggleFile))}
34
+ >
35
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
36
+ <path d="m6 14 1.45-2.9a2 2 0 0 1 1.79-1.1h11.76a2 2 0 0 1 1.94 1.5l1.55 6a2 2 0 0 1-1.94 2.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.5a2 2 0 0 1 1.59.79l.91 1.21a2 2 0 0 0 1.59.79H18a2 2 0 0 1 2 2v2" />
37
+ </svg>
38
+ </DockItem>
39
+ )
40
+ }
@@ -38,6 +38,7 @@ import { appendToDraft, appendFileReferenceChip } from './reference.ts'
38
38
  import { registerFileReferenceSource } from './file-source.ts'
39
39
  import type { FileReferenceSelection } from './preview/selection.ts'
40
40
  import './styles/chip.module.css'
41
+ import { FilePanelDockItem } from './FilePanelDockItem.tsx'
41
42
 
42
43
  declare module '@deepseek-ai/dsh-client-ui-slots' {
43
44
  interface LocaleNamespaceMap {
@@ -80,6 +81,20 @@ const panelApi = new PanelApi()
80
81
 
81
82
  /** Apply the browser half. */
82
83
  export function apply(ctx: ClientContext): void {
84
+ // Session header dock button (file-panel): each plugin owns its own button
85
+ // in conversation.session.header.utilities, so file-manager works standalone
86
+ // without dsh-term. Cross-button magnification is coordinated via window
87
+ // events (see DockItem.tsx).
88
+ ctx.inject(['slots'], (scope: ClientContext) => {
89
+ scope.slots.inject('conversation.session.header.utilities', () =>
90
+ scope.slots.register({
91
+ name: 'conversation.session.header.utilities',
92
+ id: 'filemgr-dock-item',
93
+ order: -2,
94
+ inject: () => ({}),
95
+ }, FilePanelDockItem))
96
+ })
97
+
83
98
  ctx.effect(() => ctx.locale.register(NS, dictionaries), 'dsh-filemgr: dictionaries')
84
99
 
85
100
  // The `file` reference source: registers the codec that serializes file
@@ -34,10 +34,7 @@ import {
34
34
  } from './store.ts'
35
35
  import { writeStoredNumber } from './persist.ts'
36
36
  import { maximizedGridTracks, maximizedOverlay } from './maximize.ts'
37
- import {
38
- FLOATING_BUTTON_HEIGHT_PX, FLOATING_HEADER_GAP_PX,
39
- clampFloatingTop, titlebarAreaHeight, topAlignedFloatingTop,
40
- } from './floating.ts'
37
+
41
38
  import type { LayoutStore } from './store.ts'
42
39
 
43
40
  /** The frame grid element (portals target it). */
@@ -123,7 +120,7 @@ export class PanelLayoutController {
123
120
  private explorerCol: HTMLDivElement | null = null
124
121
  private explorerHandle: HTMLDivElement | null = null
125
122
  private previewHandle: HTMLDivElement | null = null
126
- private floatingButton: HTMLButtonElement | null = null
123
+
127
124
  private styleObserver: MutationObserver | null = null
128
125
  private sizeObserver: ResizeObserver | null = null
129
126
  private waitObserver: MutationObserver | null = null
@@ -191,26 +188,6 @@ export class PanelLayoutController {
191
188
  frame.appendChild(this.explorerHandle)
192
189
  frame.appendChild(this.previewHandle)
193
190
 
194
- // The floating expand button (fixed, top-right corner) — DOM-level,
195
- // no React. Docked exactly where the explorer's collapse chevron sits,
196
- // so collapsing and re-expanding toggle in place (issue #374 follow-up);
197
- // a click toggles the explorer.
198
- this.floatingButton = document.createElement('button')
199
- this.floatingButton.type = 'button'
200
- this.floatingButton.className = 'filemgr-floating-expand'
201
- this.floatingButton.setAttribute('aria-label', 'Expand explorer')
202
- this.floatingButton.innerHTML = '<svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 3l5 5-5 5"/></svg>'
203
- this.floatingButton.addEventListener('click', () => { this.toggleExplorer() })
204
- document.body.appendChild(this.floatingButton)
205
-
206
- // Window Controls Overlay (dsh-desktop, issue #292): re-position when
207
- // the titlebar area changes (button must stay below the window buttons).
208
- const overlay = (navigator as Navigator & { windowControlsOverlay?: EventTarget }).windowControlsOverlay
209
- if (overlay !== undefined) {
210
- const onGeometryChange = (): void => { this.positionFloatingButton() }
211
- overlay.addEventListener('geometrychange', onGeometryChange)
212
- this.disposers.push(() => overlay.removeEventListener('geometrychange', onGeometryChange))
213
- }
214
191
 
215
192
  // Esc restores a maximized panel (issue #315). Editing surfaces own Esc:
216
193
  // while an input/textarea/contenteditable is focused, leave it alone.
@@ -240,6 +217,12 @@ export class PanelLayoutController {
240
217
  // Our own write — keep it (the shell tracks are already mirrored).
241
218
  return
242
219
  }
220
+ // 6 tracks: dsh-term appended its track after our 5 — re-apply to
221
+ // update handle positions (they must offset left by dsh-term's width).
222
+ if (tracks.length === 6 && tracks.includes('[dsh-term]') && this.shellTracks.length === 3) {
223
+ this.applyGrid()
224
+ return
225
+ }
243
226
  }
244
227
  this.styleObserver = new MutationObserver(syncGrid)
245
228
  this.styleObserver.observe(frame, { attributes: true, attributeFilter: ['style'] })
@@ -393,20 +376,6 @@ export class PanelLayoutController {
393
376
  return Number.isFinite(bottom) ? bottom : null
394
377
  }
395
378
 
396
- /** Position the floating button: docked at the top-right corner, just
397
- * below the shell header's bottom divider (fallback: the chevron row). */
398
- private positionFloatingButton(): void {
399
- const el = this.floatingButton
400
- if (el === null) return
401
- const height = window.innerHeight
402
- const titlebar = titlebarAreaHeight()
403
- const headerBottom = this.findHeaderBottom()
404
- const top = headerBottom !== null
405
- ? clampFloatingTop(headerBottom + FLOATING_HEADER_GAP_PX, height, FLOATING_BUTTON_HEIGHT_PX, titlebar)
406
- : topAlignedFloatingTop(height, FLOATING_BUTTON_HEIGHT_PX, titlebar)
407
- el.style.top = `${Math.round(top)}px`
408
- el.style.transform = 'none'
409
- }
410
379
 
411
380
  /** Apply one store update with transitions disabled for exactly one frame. */
412
381
  private instant(fn: () => void): void {
@@ -445,9 +414,25 @@ export class PanelLayoutController {
445
414
  const explorer = this.layout.explorerWidthPx(state)
446
415
  const preview = this.layout.previewWidthPx(state)
447
416
 
417
+ // Detect dsh-term column (appended by the dsh-term plugin after our tracks).
418
+ // When present, preserve its track and offset handles left by its width.
419
+ // Check the grid tokens for [dsh-term] — the col element may exist but not
420
+ // have a track yet (initial mount), so display/width alone are unreliable.
421
+ const gridTokens = parseGridTracks(frame.style.gridTemplateColumns)
422
+ const hasDshTermTrack = gridTokens.includes('[dsh-term]')
423
+ const dshTermCol = hasDshTermTrack ? frame.querySelector<HTMLElement>('[data-dsh-term-col]') : null
424
+ const dshTermOpen = dshTermCol !== null
425
+ && dshTermCol.style.display !== 'none'
426
+ && dshTermCol.getBoundingClientRect().width > 0
427
+ const dshTermWidth = dshTermOpen ? Math.round(dshTermCol!.getBoundingClientRect().width) : 0
428
+
448
429
  // Five tracks: shell sidebar, center, shell details, preview, explorer.
449
- frame.style.gridTemplateColumns =
430
+ // (Six when dsh-term is open — preserve its track so we don't clobber it.)
431
+ const baseGrid =
450
432
  `${this.shellTracks[0]} minmax(0, 1fr) ${this.shellTracks[2]} ${Math.round(preview)}px ${Math.round(explorer)}px`
433
+ frame.style.gridTemplateColumns = dshTermOpen
434
+ ? `${baseGrid} [dsh-term] ${dshTermWidth}px`
435
+ : baseGrid
451
436
 
452
437
  // Column contents follow the tracks (both columns always mounted).
453
438
  if (this.explorerCol !== null) {
@@ -457,15 +442,15 @@ export class PanelLayoutController {
457
442
  this.previewCol.style.visibility = preview > 0 ? 'visible' : 'hidden'
458
443
  }
459
444
 
460
- // Handles: at the left edge of each panel.
445
+ // Handles: at the left edge of each panel (offset left by dsh-term if open).
461
446
  if (this.explorerHandle !== null) {
462
- const left = Math.round(width - explorer)
447
+ const left = Math.round(width - explorer - dshTermWidth)
463
448
  this.explorerHandle.style.left = `${left}px`
464
449
  this.explorerHandle.style.marginLeft = `${-EXPLORER_HANDLE_WIDTH / 2}px`
465
450
  this.explorerHandle.style.display = explorer > 0 && state.root !== '' ? 'block' : 'none'
466
451
  }
467
452
  if (this.previewHandle !== null) {
468
- const left = Math.round(width - explorer - preview)
453
+ const left = Math.round(width - explorer - preview - dshTermWidth)
469
454
  this.previewHandle.style.left = `${left}px`
470
455
  this.previewHandle.style.display = preview > 0 && state.root !== '' ? 'block' : 'none'
471
456
  }
@@ -485,15 +470,9 @@ export class PanelLayoutController {
485
470
  this.detailsHandle = frame.querySelector<HTMLElement>('[data-side="details"]')
486
471
  }
487
472
  if (this.detailsHandle !== null) {
488
- this.detailsHandle.style.left = `${Math.round(width - detailsTrack - preview - explorer)}px`
473
+ this.detailsHandle.style.left = `${Math.round(width - detailsTrack - preview - explorer - dshTermWidth)}px`
489
474
  }
490
475
 
491
- // Floating expand button: visible only when the explorer is collapsed.
492
- if (this.floatingButton !== null) {
493
- const show = state.root !== '' && state.explorerCollapsed
494
- this.floatingButton.style.display = show ? 'flex' : 'none'
495
- this.positionFloatingButton()
496
- }
497
476
  }
498
477
 
499
478
  /**
@@ -515,11 +494,9 @@ export class PanelLayoutController {
515
494
  this.previewCol.style.visibility = target === 'preview' ? 'visible' : 'hidden'
516
495
  this.previewCol.classList.toggle('filemgr-maximized', target === 'preview' && overlay)
517
496
  }
518
- // No drag chrome while maximized: nothing to resize, and the floating
519
- // button only makes sense for the collapsed explorer.
497
+ // No drag chrome while maximized: nothing to resize.
520
498
  if (this.explorerHandle !== null) this.explorerHandle.style.display = 'none'
521
499
  if (this.previewHandle !== null) this.previewHandle.style.display = 'none'
522
- if (this.floatingButton !== null) this.floatingButton.style.display = 'none'
523
500
  }
524
501
 
525
502
  /** Remove the narrow-screen overlay class from both columns. */
@@ -538,7 +515,7 @@ export class PanelLayoutController {
538
515
  this.explorerCol?.remove()
539
516
  this.explorerHandle?.remove()
540
517
  this.previewHandle?.remove()
541
- this.floatingButton?.remove()
518
+
542
519
  if (this.instantTimer !== undefined) clearTimeout(this.instantTimer)
543
520
  if (frameElement === this.frame) frameElement = null
544
521
  this.frame = null
@@ -188,85 +188,13 @@
188
188
  }
189
189
  }
190
190
 
191
- /* Drag handles: 2px visual line, 6px + brand on hover/active. */
191
+ /* Drag handles: invisible, col-resize cursor only (like DSH native sidebar). */
192
192
  :global(.filemgr-explorer-handle),
193
193
  :global(.filemgr-preview-handle) {
194
194
  touch-action: none;
195
- }
196
- :global(.filemgr-explorer-handle::after),
197
- :global(.filemgr-preview-handle::after) {
198
- content: "";
199
- position: absolute;
200
- top: 0;
201
- bottom: 0;
202
- left: 0;
203
- width: 2px;
204
- border-radius: 9999px;
205
- background-color: var(--aion-bg-3);
206
- opacity: 0.9;
207
- transition: width 0.15s cubic-bezier(0.4, 0, 0.2, 1),
208
- background-color 0.15s cubic-bezier(0.4, 0, 0.2, 1);
209
- pointer-events: none;
210
- }
211
- :global(.filemgr-explorer-handle:hover::after),
212
- :global(.filemgr-explorer-handle:active::after),
213
- :global(.filemgr-preview-handle:hover::after),
214
- :global(.filemgr-preview-handle:active::after) {
215
- width: 6px;
216
- background-color: var(--aion-brand);
217
- }
218
- /* The preview handle's line sits at its right edge (its hit zone extends
219
- left); the explorer's at its left edge. */
220
- :global(.filemgr-preview-handle::after) {
221
- left: auto;
222
- right: 0;
223
- opacity: 0.3;
224
- }
225
- :global(.filemgr-preview-handle:hover::after),
226
- :global(.filemgr-preview-handle:active::after) {
227
- opacity: 1;
195
+ cursor: col-resize;
228
196
  }
229
197
 
230
- /* Floating expand button (explorer collapsed). Docked at the viewport's
231
- top-right corner, just below the shell header's bottom divider (the
232
- horizontal line under the "Session log" row) — the layout controller
233
- sets an inline top for that, so the button never overlaps the header
234
- chrome (issues #374 / #292). Above the columns (z 30): when the preview
235
- region is open it overlaps the rightmost column track, so anything
236
- lower would be painted under the opaque column and stop receiving
237
- clicks (issue #234 follow-up). Full-screen overlay drawers must render
238
- at the ROOT stacking context (z 100~1000) to cover it.
239
- The top below is only the pre-JS fallback (the chevron row); the
240
- controller overwrites it inline. */
241
- :global(.filemgr-floating-expand) {
242
- position: fixed;
243
- top: calc(6px + env(titlebar-area-height, 0px));
244
- right: 8px;
245
- width: 24px;
246
- height: 24px;
247
- display: none;
248
- align-items: center;
249
- justify-content: center;
250
- border: none;
251
- border-radius: 4px;
252
- background-color: var(--aion-bg-2);
253
- color: var(--aion-text-secondary);
254
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
255
- cursor: pointer;
256
- z-index: 100;
257
- transition: background-color 0.15s cubic-bezier(0.4, 0, 0.2, 1);
258
- }
259
- :global(.filemgr-floating-expand:hover) {
260
- background-color: var(--aion-bg-3);
261
- color: var(--aion-text-primary);
262
- }
263
- :global(.filemgr-floating-expand:active) {
264
- background-color: var(--aion-bg-active);
265
- }
266
- :global(.filemgr-floating-expand:focus-visible) {
267
- outline: 2px solid var(--aion-primary);
268
- outline-offset: 2px;
269
- }
270
198
 
271
199
  /* The collapse chevron inside the explorer tab bar (top-right). It rides
272
200
  the explorer column's stacking context (the column stacks at z 30), so