@floegence/floe-webapp-core 0.52.2 → 0.52.4

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.
@@ -1,65 +1,65 @@
1
- const d = "data-floe-dialog-surface-host", y = d, p = "data-floe-surface-portal-layer", v = "data-floe-surface-floating-layer", _ = "data-floe-dialog-surface-boundary", g = 1600;
2
- let r = null, o = null;
3
- function E(t) {
1
+ const g = "data-floe-dialog-surface-host", b = g, E = "data-floe-surface-portal-layer", P = "data-floe-surface-floating-layer", x = "data-floe-dialog-surface-boundary", T = 1600;
2
+ let u = null, r = null;
3
+ function w(t) {
4
4
  return typeof Element < "u" && t instanceof Element ? t : typeof Node < "u" && t instanceof Node ? t.parentElement : null;
5
5
  }
6
- function T(t) {
6
+ function R(t) {
7
7
  const e = t?.activeElement;
8
8
  return e instanceof Element ? e : null;
9
9
  }
10
- function l(t) {
10
+ function c(t) {
11
11
  const e = typeof document < "u" ? document : null;
12
- r = {
13
- target: E(t),
14
- activeElement: T(e),
12
+ u = {
13
+ target: w(t),
14
+ activeElement: R(e),
15
15
  recordedAt: Date.now()
16
16
  };
17
17
  }
18
- function c(t) {
19
- l(t.target);
18
+ function a(t) {
19
+ c(t.target);
20
20
  }
21
- function f(t) {
22
- l(t.target);
21
+ function s(t) {
22
+ c(t.target);
23
23
  }
24
- function a(t) {
25
- l(t.target);
24
+ function h(t) {
25
+ c(t.target);
26
26
  }
27
- function s() {
28
- R();
27
+ function p() {
28
+ S();
29
29
  }
30
- function R() {
31
- typeof document > "u" || o !== document && (o && (o.removeEventListener("pointerdown", c, !0), o.removeEventListener("focusin", f, !0), o.removeEventListener("contextmenu", a, !0)), document.addEventListener("pointerdown", c, !0), document.addEventListener("focusin", f, !0), document.addEventListener("contextmenu", a, !0), o = document);
30
+ function S() {
31
+ typeof document > "u" || r !== document && (r && (r.removeEventListener("pointerdown", a, !0), r.removeEventListener("focusin", s, !0), r.removeEventListener("contextmenu", h, !0)), document.addEventListener("pointerdown", a, !0), document.addEventListener("focusin", s, !0), document.addEventListener("contextmenu", h, !0), r = document);
32
32
  }
33
- function w() {
34
- return !r || Date.now() - r.recordedAt > g ? null : r;
33
+ function A() {
34
+ return !u || Date.now() - u.recordedAt > T ? null : u;
35
35
  }
36
- function i(t) {
37
- const e = t?.closest(`[${d}="true"]`);
36
+ function l(t) {
37
+ const e = t?.closest(`[${g}="true"]`);
38
38
  return typeof HTMLElement > "u" ? null : e instanceof HTMLElement ? e : null;
39
39
  }
40
- function A(t) {
40
+ function v(t) {
41
41
  if (!t) return null;
42
- const e = t.closest(`[${p}="true"]`);
42
+ const e = t.closest(`[${E}="true"]`);
43
43
  return typeof HTMLElement > "u" ? null : e instanceof HTMLElement ? e : null;
44
44
  }
45
- function L(t = {}) {
46
- s();
47
- const e = w(), n = i(t.owner ?? null) ?? i(e?.target ?? null) ?? i(e?.activeElement ?? null);
45
+ function C(t = {}) {
46
+ p();
47
+ const e = A(), n = l(t.owner ?? null) ?? l(e?.target ?? null) ?? l(e?.activeElement ?? null);
48
48
  return n ? {
49
49
  host: n,
50
50
  boundaryHost: n,
51
- mountHost: A(n) ?? n,
51
+ mountHost: v(n) ?? n,
52
52
  mode: "surface"
53
53
  } : { host: null, boundaryHost: null, mountHost: null, mode: "global" };
54
54
  }
55
- function u(t) {
55
+ function i(t) {
56
56
  return t.mode === "surface" && !!t.boundaryHost?.isConnected;
57
57
  }
58
- function S(t) {
59
- if (u(t))
58
+ function f(t) {
59
+ if (i(t))
60
60
  return t.mountHost ?? t.boundaryHost ?? void 0;
61
61
  }
62
- function m() {
62
+ function y() {
63
63
  return typeof window > "u" ? {
64
64
  left: 0,
65
65
  top: 0,
@@ -76,9 +76,9 @@ function m() {
76
76
  height: window.innerHeight
77
77
  };
78
78
  }
79
- function b(t) {
80
- if (!u(t) || !t.boundaryHost)
81
- return m();
79
+ function F(t) {
80
+ if (!i(t) || !t.boundaryHost)
81
+ return y();
82
82
  const e = t.boundaryHost.getBoundingClientRect();
83
83
  return {
84
84
  left: e.left,
@@ -89,10 +89,10 @@ function b(t) {
89
89
  height: e.height
90
90
  };
91
91
  }
92
- function h(t) {
93
- const e = S(t);
92
+ function L(t) {
93
+ const e = f(t);
94
94
  if (!e)
95
- return m();
95
+ return y();
96
96
  const n = e.getBoundingClientRect();
97
97
  return {
98
98
  left: n.left,
@@ -103,46 +103,57 @@ function h(t) {
103
103
  height: n.height
104
104
  };
105
105
  }
106
- function P(t, e) {
107
- if (!u(e))
106
+ function _(t) {
107
+ const e = f(t);
108
+ if (!e) return { x: 1, y: 1 };
109
+ const n = e.getBoundingClientRect();
110
+ return {
111
+ x: e.offsetWidth > 0 && n.width > 0 ? n.width / e.offsetWidth : 1,
112
+ y: e.offsetHeight > 0 && n.height > 0 ? n.height / e.offsetHeight : 1
113
+ };
114
+ }
115
+ function m(t, e) {
116
+ if (!i(e))
108
117
  return t;
109
- const n = h(e);
118
+ const n = f(e), o = L(e), d = _(e);
110
119
  return {
111
- x: t.x - n.left,
112
- y: t.y - n.top
120
+ x: (t.x - o.left) / d.x - n.clientLeft + n.scrollLeft,
121
+ y: (t.y - o.top) / d.y - n.clientTop + n.scrollTop
113
122
  };
114
123
  }
115
- function C(t, e) {
116
- if (!u(e))
124
+ function H(t, e) {
125
+ if (!i(e))
117
126
  return t;
118
- const n = h(e);
127
+ const n = m({ x: t.left, y: t.top }, e), o = m({ x: t.right, y: t.bottom }, e);
119
128
  return {
120
- left: t.left - n.left,
121
- top: t.top - n.top,
122
- right: t.right - n.left,
123
- bottom: t.bottom - n.top,
124
- width: t.width,
125
- height: t.height
129
+ left: n.x,
130
+ top: n.y,
131
+ right: o.x,
132
+ bottom: o.y,
133
+ width: o.x - n.x,
134
+ height: o.y - n.y
126
135
  };
127
136
  }
128
- function H() {
129
- r = null;
137
+ function I() {
138
+ u = null;
130
139
  }
131
- typeof document < "u" && s();
140
+ typeof document < "u" && p();
132
141
  export {
133
- _ as DIALOG_SURFACE_BOUNDARY_ATTR,
134
- d as DIALOG_SURFACE_HOST_ATTR,
135
- v as SURFACE_FLOATING_LAYER_ATTR,
136
- y as SURFACE_PORTAL_HOST_ATTR,
137
- p as SURFACE_PORTAL_LAYER_ATTR,
138
- H as __resetSurfacePortalScopeForTests,
139
- s as ensureDialogSurfaceInteractionTracking,
140
- R as ensureSurfacePortalInteractionTracking,
141
- u as isSurfacePortalMode,
142
- P as projectSurfacePortalPosition,
143
- C as projectSurfacePortalRect,
144
- b as resolveSurfacePortalBoundaryRect,
145
- L as resolveSurfacePortalHost,
146
- S as resolveSurfacePortalMount,
147
- h as resolveSurfacePortalMountRect
142
+ x as DIALOG_SURFACE_BOUNDARY_ATTR,
143
+ g as DIALOG_SURFACE_HOST_ATTR,
144
+ P as SURFACE_FLOATING_LAYER_ATTR,
145
+ b as SURFACE_PORTAL_HOST_ATTR,
146
+ E as SURFACE_PORTAL_LAYER_ATTR,
147
+ I as __resetSurfacePortalScopeForTests,
148
+ p as ensureDialogSurfaceInteractionTracking,
149
+ S as ensureSurfacePortalInteractionTracking,
150
+ i as isSurfacePortalMode,
151
+ m as projectSurfacePortalPosition,
152
+ H as projectSurfacePortalRect,
153
+ A as readFreshInteractionSnapshot,
154
+ F as resolveSurfacePortalBoundaryRect,
155
+ C as resolveSurfacePortalHost,
156
+ f as resolveSurfacePortalMount,
157
+ L as resolveSurfacePortalMountRect,
158
+ _ as resolveSurfacePortalScale
148
159
  };
@@ -1,4 +1,5 @@
1
1
  export { Button, type ButtonProps, type ButtonVariant, type ButtonSize } from './Button';
2
+ export { intersectSurfaceRects, type SurfaceFloatingBoundary } from './surfaceFloatingBoundary';
2
3
  export { Tag, type TagProps, type TagVariant, type TagSize } from './Tag';
3
4
  export { Input, Textarea, NumberInput, AffixInput, type InputProps, type InputSize, type TextareaProps, type NumberInputProps, type AffixInputProps, type AffixOption, } from './Input';
4
5
  export { Dialog, ConfirmDialog, type DialogProps, type ConfirmDialogProps } from './Dialog';
@@ -0,0 +1,12 @@
1
+ import { type ResolvedSurfacePortalHost, type SurfacePortalRect } from './surfacePortalScope';
2
+ /** A collision boundary in client coordinates; null means explicitly unavailable. */
3
+ export type SurfaceFloatingBoundary = HTMLElement | SurfacePortalRect | null;
4
+ export declare function intersectSurfaceRects(...rects: readonly SurfacePortalRect[]): SurfacePortalRect;
5
+ export type SurfaceSafeArea = Readonly<{
6
+ top: number;
7
+ right: number;
8
+ bottom: number;
9
+ left: number;
10
+ }>;
11
+ export declare function readSurfaceSafeArea(): SurfaceSafeArea;
12
+ export declare function resolveFloatingBoundary(host: ResolvedSurfacePortalHost, boundary?: SurfaceFloatingBoundary, safeArea?: SurfaceSafeArea): SurfacePortalRect | null;
@@ -0,0 +1,53 @@
1
+ import { resolveMobileKeyboardViewportMetrics as l } from "./mobileKeyboardViewport.js";
2
+ import { isSurfacePortalMode as d, resolveSurfacePortalBoundaryRect as p } from "./dialogSurfaceScope.js";
3
+ function m(...e) {
4
+ const t = Math.max(...e.map((r) => r.left)), i = Math.max(...e.map((r) => r.top)), o = Math.max(t, Math.min(...e.map((r) => r.right))), a = Math.max(i, Math.min(...e.map((r) => r.bottom)));
5
+ return { left: t, top: i, right: o, bottom: a, width: o - t, height: a - i };
6
+ }
7
+ function s() {
8
+ if (typeof document > "u") return { top: 0, right: 0, bottom: 0, left: 0 };
9
+ const e = document.createElement("div");
10
+ e.style.cssText = "position:fixed;visibility:hidden;pointer-events:none;padding:env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px)", document.documentElement.append(e);
11
+ const t = getComputedStyle(e), i = (a) => Math.max(0, Number.parseFloat(a) || 0), o = {
12
+ top: i(t.paddingTop),
13
+ right: i(t.paddingRight),
14
+ bottom: i(t.paddingBottom),
15
+ left: i(t.paddingLeft)
16
+ };
17
+ return e.remove(), o;
18
+ }
19
+ function c(e, t, i = { top: 0, right: 0, bottom: 0, left: 0 }) {
20
+ if (t === null || typeof window > "u" || e.mode === "surface" && !d(e)) return null;
21
+ const o = l(window), a = window.innerHeight - o.bottomPx - o.heightPx, r = {
22
+ left: o.leftPx + i.left,
23
+ top: a + i.top,
24
+ right: o.leftPx + o.widthPx - i.right,
25
+ bottom: a + o.heightPx - i.bottom,
26
+ width: o.widthPx,
27
+ height: o.heightPx
28
+ };
29
+ let n;
30
+ if (t instanceof HTMLElement) {
31
+ if (!t.isConnected || t.closest('[inert], [hidden], [aria-hidden="true"]'))
32
+ return null;
33
+ n = t.getBoundingClientRect();
34
+ } else
35
+ n = t;
36
+ return n && ![
37
+ n.left,
38
+ n.top,
39
+ n.right,
40
+ n.bottom,
41
+ n.width,
42
+ n.height
43
+ ].every(Number.isFinite) ? null : m(
44
+ r,
45
+ p(e),
46
+ ...n ? [n] : []
47
+ );
48
+ }
49
+ export {
50
+ m as intersectSurfaceRects,
51
+ s as readSurfaceSafeArea,
52
+ c as resolveFloatingBoundary
53
+ };
@@ -1 +1 @@
1
- export { DIALOG_SURFACE_HOST_ATTR, SURFACE_PORTAL_HOST_ATTR, SURFACE_PORTAL_LAYER_ATTR, SURFACE_FLOATING_LAYER_ATTR, DIALOG_SURFACE_BOUNDARY_ATTR, ensureSurfacePortalInteractionTracking, resolveSurfacePortalHost, isSurfacePortalMode, resolveSurfacePortalMount, resolveSurfacePortalBoundaryRect, resolveSurfacePortalMountRect, projectSurfacePortalPosition, projectSurfacePortalRect, __resetSurfacePortalScopeForTests, type SurfacePortalInteractionSnapshot, type SurfacePortalHostResolutionOptions, type SurfacePortalMode, type ResolvedSurfacePortalHost, type SurfacePortalBoundaryRect, type SurfacePortalRect, } from './dialogSurfaceScope';
1
+ export { DIALOG_SURFACE_HOST_ATTR, SURFACE_PORTAL_HOST_ATTR, SURFACE_PORTAL_LAYER_ATTR, SURFACE_FLOATING_LAYER_ATTR, DIALOG_SURFACE_BOUNDARY_ATTR, ensureSurfacePortalInteractionTracking, resolveSurfacePortalHost, isSurfacePortalMode, resolveSurfacePortalMount, resolveSurfacePortalBoundaryRect, resolveSurfacePortalMountRect, resolveSurfacePortalScale, projectSurfacePortalPosition, projectSurfacePortalRect, __resetSurfacePortalScopeForTests, type SurfacePortalInteractionSnapshot, type SurfacePortalHostResolutionOptions, type SurfacePortalMode, type ResolvedSurfacePortalHost, type SurfacePortalBoundaryRect, type SurfacePortalRect, } from './dialogSurfaceScope';
@@ -1,39 +1,40 @@
1
- import { ArchiveFileIcon as r, AudioFileIcon as i, BrokenSymlinkIcon as l, CodeFileIcon as n, ConfigFileIcon as c, DocumentFileIcon as F, FileIcon as t, FileItemIcon as I, FolderIcon as m, FolderOpenIcon as p, ImageFileIcon as f, JavaScriptFileIcon as x, ShellScriptFileIcon as s, StyleFileIcon as a, SymlinkFileIcon as d, SymlinkFolderIcon as S, SymlinkFolderOpenIcon as w, TypeScriptFileIcon as B, VideoFileIcon as y, getFileIcon as u, resolveFileItemIcon as v } from "./components/file-browser/FileIcons.js";
2
- import { Breadcrumb as g } from "./components/file-browser/Breadcrumb.js";
3
- import { DirectoryTree as A } from "./components/file-browser/DirectoryTree.js";
1
+ import { ArchiveFileIcon as r, AudioFileIcon as i, BrokenSymlinkIcon as l, CodeFileIcon as n, ConfigFileIcon as c, DocumentFileIcon as t, FileIcon as F, FileItemIcon as I, FolderIcon as m, FolderOpenIcon as p, ImageFileIcon as f, JavaScriptFileIcon as s, ShellScriptFileIcon as x, StyleFileIcon as a, SymlinkFileIcon as d, SymlinkFolderIcon as S, SymlinkFolderOpenIcon as w, TypeScriptFileIcon as B, VideoFileIcon as y, getFileIcon as u, resolveFileItemIcon as v } from "./components/file-browser/FileIcons.js";
2
+ import { Breadcrumb as k } from "./components/file-browser/Breadcrumb.js";
3
+ import { DirectoryTree as h } from "./components/file-browser/DirectoryTree.js";
4
4
  import { FileBrowser as D } from "./components/file-browser/FileBrowser.js";
5
- import { FileBrowserDragPreview as V } from "./components/file-browser/DragPreview.js";
6
- import { FileBrowserProvider as O, useFileBrowser as P } from "./components/file-browser/FileBrowserContext.js";
7
- import { FileBrowserStatusBar as J } from "./components/file-browser/FileBrowserStatusBar.js";
8
- import { FileBrowserToolbar as M } from "./components/file-browser/FileBrowserToolbar.js";
9
- import { FileContextMenu as j } from "./components/file-browser/FileContextMenu.js";
10
- import { FileGridView as z } from "./components/file-browser/FileGridView.js";
11
- import { FileListView as H } from "./components/file-browser/FileListView.js";
5
+ import { FileBrowserDragPreview as T } from "./components/file-browser/DragPreview.js";
6
+ import { FileBrowserProvider as b, useFileBrowser as L } from "./components/file-browser/FileBrowserContext.js";
7
+ import { FileBrowserStatusBar as O } from "./components/file-browser/FileBrowserStatusBar.js";
8
+ import { FileBrowserToolbar as H } from "./components/file-browser/FileBrowserToolbar.js";
9
+ import { FileContextMenu as N } from "./components/file-browser/FileContextMenu.js";
10
+ import { FileGridView as q } from "./components/file-browser/FileGridView.js";
11
+ import { FileListView as E } from "./components/file-browser/FileListView.js";
12
12
  import { classifyArchiveFileName as Q } from "./components/file-browser/archiveFiles.js";
13
+ import { createLongPressContextMenuHandlers as U } from "./components/file-browser/longPressContextMenu.js";
13
14
  export {
14
15
  r as ArchiveFileIcon,
15
16
  i as AudioFileIcon,
16
- g as Breadcrumb,
17
+ k as Breadcrumb,
17
18
  l as BrokenSymlinkIcon,
18
19
  n as CodeFileIcon,
19
20
  c as ConfigFileIcon,
20
- A as DirectoryTree,
21
- F as DocumentFileIcon,
21
+ h as DirectoryTree,
22
+ t as DocumentFileIcon,
22
23
  D as FileBrowser,
23
- V as FileBrowserDragPreview,
24
- O as FileBrowserProvider,
25
- J as FileBrowserStatusBar,
26
- M as FileBrowserToolbar,
27
- j as FileContextMenu,
28
- z as FileGridView,
29
- t as FileIcon,
24
+ T as FileBrowserDragPreview,
25
+ b as FileBrowserProvider,
26
+ O as FileBrowserStatusBar,
27
+ H as FileBrowserToolbar,
28
+ N as FileContextMenu,
29
+ q as FileGridView,
30
+ F as FileIcon,
30
31
  I as FileItemIcon,
31
- H as FileListView,
32
+ E as FileListView,
32
33
  m as FolderIcon,
33
34
  p as FolderOpenIcon,
34
35
  f as ImageFileIcon,
35
- x as JavaScriptFileIcon,
36
- s as ShellScriptFileIcon,
36
+ s as JavaScriptFileIcon,
37
+ x as ShellScriptFileIcon,
37
38
  a as StyleFileIcon,
38
39
  d as SymlinkFileIcon,
39
40
  S as SymlinkFolderIcon,
@@ -41,7 +42,8 @@ export {
41
42
  B as TypeScriptFileIcon,
42
43
  y as VideoFileIcon,
43
44
  Q as classifyArchiveFileName,
45
+ U as createLongPressContextMenuHandlers,
44
46
  u as getFileIcon,
45
47
  v as resolveFileItemIcon,
46
- P as useFileBrowser
48
+ L as useFileBrowser
47
49
  };
package/dist/floe.css CHANGED
@@ -357,67 +357,49 @@
357
357
  contain: layout paint style;
358
358
  }
359
359
 
360
- [data-floe-floating-window-surface='true'] {
361
- background-color: color-mix(in srgb, var(--card) 96%, transparent);
362
- border-color: color-mix(in srgb, var(--border) 88%, var(--foreground) 12%);
360
+ [data-floe-floating-window-surface='true'].floe-floating-window-motion {
361
+ /* Floating windows use an opaque Mica-like stack in every surface mode.
362
+ * Keeping this on the component boundary makes the default (standard)
363
+ * path match the opt-in material without changing other controls. */
364
+ background-color: var(--card);
365
+ border-color: color-mix(in srgb, var(--border) 78%, var(--background) 22%);
366
+ border-radius: 10px;
363
367
  box-shadow:
364
- 0 16px 36px color-mix(in srgb, var(--foreground) 12%, transparent),
365
- 0 3px 10px color-mix(in srgb, var(--foreground) 10%, transparent);
366
- transition:
367
- border-color 140ms ease,
368
- box-shadow 140ms ease,
369
- background-color 140ms ease;
368
+ 0 12px 28px -8px color-mix(in srgb, var(--foreground) 18%, transparent),
369
+ 0 3px 8px -2px color-mix(in srgb, var(--foreground) 12%, transparent),
370
+ inset 0 1px 0 rgb(255 255 255 / 45%);
370
371
  }
371
-
372
- @supports (backdrop-filter: blur(12px)) {
373
- [data-floe-floating-window-surface='true'] {
374
- backdrop-filter: blur(12px);
375
- background-color: color-mix(in srgb, var(--card) 90%, transparent);
376
- }
372
+ [data-floe-floating-window-surface='true'] {
373
+ border-radius: 10px;
377
374
  }
378
375
 
379
376
  [data-floe-floating-window-surface='true'][data-floe-floating-window-state='active'] {
380
- border-color: color-mix(in srgb, var(--border) 62%, var(--ring) 38%);
381
- box-shadow:
382
- 0 22px 48px color-mix(in srgb, var(--foreground) 16%, transparent),
383
- 0 5px 14px color-mix(in srgb, var(--foreground) 12%, transparent),
384
- 0 0 0 1px color-mix(in srgb, var(--ring) 22%, transparent);
377
+ border-color: color-mix(in srgb, var(--border) 60%, var(--ring) 40%);
385
378
  }
386
379
 
387
380
  [data-floe-floating-window-surface='true'][data-floe-floating-window-state='inactive'] {
388
- border-color: color-mix(in srgb, var(--border) 94%, var(--foreground) 6%);
389
- box-shadow:
390
- 0 12px 28px color-mix(in srgb, var(--foreground) 10%, transparent),
391
- 0 2px 6px color-mix(in srgb, var(--foreground) 8%, transparent);
381
+ border-color: color-mix(in srgb, var(--border) 88%, var(--background) 12%);
392
382
  }
393
383
 
394
384
  [data-floe-floating-window-titlebar='true'] {
395
- background:
396
- linear-gradient(
397
- 180deg,
398
- color-mix(in srgb, var(--muted) 78%, var(--card) 22%),
399
- color-mix(in srgb, var(--muted) 60%, var(--card) 40%)
400
- );
401
- border-bottom-color: color-mix(in srgb, var(--border) 84%, var(--foreground) 16%);
402
- transition: background 140ms ease, border-color 140ms ease;
385
+ background-color: color-mix(in srgb, var(--muted) 70%, var(--card) 30%);
386
+ border-bottom-color: color-mix(in srgb, var(--border) 70%, var(--background) 30%);
387
+ transition: none;
388
+ }
389
+
390
+ :root.dark [data-floe-floating-window-surface='true'] {
391
+ box-shadow:
392
+ 0 12px 28px -8px color-mix(in srgb, var(--foreground) 24%, transparent),
393
+ 0 3px 8px -2px color-mix(in srgb, var(--foreground) 18%, transparent),
394
+ inset 0 1px 0 rgb(255 255 255 / 8%);
403
395
  }
404
396
 
405
397
  [data-floe-floating-window-surface='true'][data-floe-floating-window-state='active'] [data-floe-floating-window-titlebar='true'] {
406
- background:
407
- linear-gradient(
408
- 180deg,
409
- color-mix(in srgb, var(--accent) 62%, var(--muted) 38%),
410
- color-mix(in srgb, var(--muted) 52%, var(--card) 48%)
411
- );
398
+ background-color: color-mix(in srgb, var(--accent) 24%, var(--muted) 76%);
412
399
  }
413
400
 
414
401
  [data-floe-floating-window-surface='true'][data-floe-floating-window-state='inactive'] [data-floe-floating-window-titlebar='true'] {
415
- background:
416
- linear-gradient(
417
- 180deg,
418
- color-mix(in srgb, var(--muted) 80%, var(--card) 20%),
419
- color-mix(in srgb, var(--muted) 64%, var(--card) 36%)
420
- );
402
+ background-color: color-mix(in srgb, var(--muted) 56%, var(--card) 44%);
421
403
  }
422
404
 
423
405
  [data-floe-hot-interaction~='resize'] [data-floe-geometry-surface='shell-sidebar'],
@@ -433,6 +415,19 @@
433
415
  }
434
416
  }
435
417
 
418
+ /* Edge-aligned window controls keep their keyboard indicator inside the
419
+ * clipped panel. The outer resize perimeter keeps its existing hit targets. */
420
+ [data-floe-floating-window-control]:focus-visible {
421
+ box-shadow: none;
422
+ outline: 2px solid var(--ring);
423
+ outline-offset: -3px;
424
+ }
425
+ @media (forced-colors: active) {
426
+ [data-floe-floating-window-control]:focus-visible {
427
+ outline-color: Highlight;
428
+ }
429
+ }
430
+
436
431
  /* Animation utilities */
437
432
  @layer utilities {
438
433
  .floe-floating-presence {