@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.
- package/lib/client.js +90 -64
- package/lib/client.js.map +1 -1
- package/lib/tsconfig.client.tsbuildinfo +1 -1
- package/lib/types/client/DockItem.d.ts +15 -0
- package/lib/types/client/DockItem.d.ts.map +1 -0
- package/lib/types/client/FilePanelDockItem.d.ts +16 -0
- package/lib/types/client/FilePanelDockItem.d.ts.map +1 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/layout.d.ts +0 -4
- package/lib/types/client/layout.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client/DockItem.tsx +42 -0
- package/src/client/FilePanelDockItem.tsx +40 -0
- package/src/client/index.ts +15 -0
- package/src/client/layout.ts +31 -54
- package/src/client/styles/tokens.module.css +2 -74
- package/lib/assets/mermaid.min.js +0 -3587
- package/src/client/floating.ts +0 -63
|
@@ -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;
|
|
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.
|
|
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
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -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
|
package/src/client/layout.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|