Merge pull request #354 from omdsh-dev/feat/free-window
feat(ui): 自由窗口——tab 拖出侧边栏成为可移动/缩放/置顶的浮动窗口 (v0.16.0)
This commit is contained in:
@@ -304,6 +304,148 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
/* ── Free windows ──────────────────────────────────────────────────────── */
|
||||
|
||||
/* One floating tab: the same card surface as the panels, at viewport
|
||||
coordinates inside the panel host (immune to shell transforms). z-index
|
||||
42: above the panels (40) so a window drags across them, below the
|
||||
toggle cluster (45) and the app's overlay stack (100+, issue #52).
|
||||
Stacking among windows is the floats array order (siblings share the
|
||||
z-index; DOM order wins). Deliberately NO box transition — the header/
|
||||
resize drags write left/top/width/height per frame. Elevated surface,
|
||||
so unlike the flat panels it earns a shadow token. */
|
||||
.floatWindow {
|
||||
position: absolute;
|
||||
z-index: 42;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* Re-enable interaction inside the click-transparent panel host. */
|
||||
pointer-events: auto;
|
||||
background: var(--dsw-alias-bg-layer-1);
|
||||
border: 1px solid var(--dsw-alias-border-l2);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--dsw-shadow-lv3);
|
||||
overflow: hidden;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.floatWindowDragging {
|
||||
will-change: left, top, width, height;
|
||||
}
|
||||
|
||||
/* The header: the move/dock-back drag handle, mirroring the tab strip's
|
||||
chrome (34px, hairline bottom, secondary label). */
|
||||
.floatHeader {
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
height: 34px;
|
||||
padding: 0 4px 0 10px;
|
||||
font: var(--dsw-font-xxs-12);
|
||||
color: var(--dsw-alias-label-secondary);
|
||||
background: var(--dsw-alias-bg-layer-1);
|
||||
border-bottom: 1px solid var(--dsw-alias-border-l1);
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.floatWindowDragging .floatHeader {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.floatTitle {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Mirrors .tabClose. */
|
||||
.floatClose {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
color: var(--dsw-alias-label-tertiary);
|
||||
cursor: pointer;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.floatClose:hover {
|
||||
background: var(--dsw-alias-interactive-bg-hover);
|
||||
color: var(--dsw-alias-label-primary);
|
||||
}
|
||||
|
||||
/* Mirrors .paneContent's column-flex contract: the tab cell must hand its
|
||||
child a definite WIDTH via align-stretch. A row flex (the default) sizes
|
||||
the main axis (width) from content — intrinsic-zero hosts (e.g. a plugin
|
||||
tab UI) collapse to a 0px-wide sliver and paint nothing (the blank
|
||||
whale-report window). */
|
||||
.floatContent {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* The SE resize corner (mirror of .cornerHandle). */
|
||||
.floatResize {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
z-index: 2;
|
||||
cursor: nwse-resize;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.floatResize:hover {
|
||||
background: var(--dsw-alias-interactive-bg-hover-accent);
|
||||
}
|
||||
|
||||
/* A pane highlighted as the dock target while a float header drag hovers it
|
||||
(the attribute is written directly by the drag at pointer cadence;
|
||||
skin-addressable via [data-dsh-pane][data-dsh-float-dock-over]). */
|
||||
.pane[data-dsh-float-dock-over] {
|
||||
outline: 2px dashed var(--dsw-alias-interactive-bg-hover-accent);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* The drag-out hint over the conversation column: a dashed frame marking
|
||||
the drop zone, pointer-transparent so it never disturbs the drag it
|
||||
describes. z-index 46: above the windows (42) for unambiguous feedback,
|
||||
still below the app's overlay stack. */
|
||||
.floatDropHint {
|
||||
position: absolute;
|
||||
z-index: 46;
|
||||
pointer-events: none;
|
||||
border: 2px dashed var(--dsw-alias-interactive-bg-hover-accent);
|
||||
border-radius: 8px;
|
||||
background: color-mix(in srgb, var(--dsw-alias-interactive-bg-hover-accent) 12%, transparent);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.floatDropHintLabel {
|
||||
padding: 4px 12px;
|
||||
border-radius: 999px;
|
||||
font: var(--dsw-font-xxs-12);
|
||||
color: var(--dsw-alias-label-primary);
|
||||
background: var(--dsw-alias-bg-layer-1);
|
||||
border: 1px solid var(--dsw-alias-border-l2);
|
||||
}
|
||||
|
||||
/* ── Desktop drag-region opt-out ──────────────────────────────────────── */
|
||||
|
||||
/* Frameless desktop shells make the top strip of the web content draggable
|
||||
@@ -315,7 +457,8 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
|
||||
browsers and in shells without drag regions — safe everywhere. */
|
||||
.toggleCluster,
|
||||
.toggleButton,
|
||||
.tabBar {
|
||||
.tabBar,
|
||||
.floatHeader {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
@@ -755,7 +898,7 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
|
||||
overflow-y: auto;
|
||||
/* Deep trees truncate their labels instead of scrolling horizontally. */
|
||||
overflow-x: hidden;
|
||||
padding: 2px 6px 8px;
|
||||
padding: 4px 8px 8px;
|
||||
}
|
||||
|
||||
/* One tree row: the app's session-row cell (34px, radius 8, inset, hover
|
||||
@@ -1075,7 +1218,7 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 8px;
|
||||
padding: 6px 8px;
|
||||
border-bottom: 1px solid var(--dsw-alias-border-l1);
|
||||
}
|
||||
|
||||
@@ -1094,6 +1237,7 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
|
||||
.editorPathInput {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid var(--dsw-alias-border-l1);
|
||||
@@ -1176,7 +1320,7 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
gap: 6px;
|
||||
padding: 6px 8px;
|
||||
border-bottom: 1px solid var(--dsw-alias-border-l1);
|
||||
}
|
||||
@@ -1309,7 +1453,7 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
|
||||
|
||||
.editorBanner {
|
||||
flex: none;
|
||||
padding: 4px 12px;
|
||||
padding: 4px 8px;
|
||||
font: var(--dsw-font-xxxs-11);
|
||||
color: var(--dsw-alias-state-warn-label);
|
||||
background: var(--dsw-alias-state-warn-tertiary);
|
||||
@@ -1512,6 +1656,12 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Code lines get the same 12/16 inset as the markdown preview, so toggling
|
||||
preview/edit never jumps the text's resting position. */
|
||||
.editorCm :global(.cm-scroller) {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.editorCm :global(.cm-editor.cm-focused) {
|
||||
outline: none;
|
||||
}
|
||||
@@ -1567,7 +1717,7 @@ body[data-dsh-sidebar-dragging] .bottomPanel {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: 10px 14px;
|
||||
padding: 12px 16px;
|
||||
font: var(--dsw-font-xs-13);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user