@floegence/floe-webapp-core 0.86.58 → 0.86.62

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.
Files changed (103) hide show
  1. package/dist/components/chat/blocks/BlockRenderer.js +1 -1
  2. package/dist/components/chat/blocks/CodeDiffBlock.js +56 -56
  3. package/dist/components/chat/blocks/ImageBlock.js +1 -1
  4. package/dist/components/chat/blocks/ToolCallBlock.js +57 -57
  5. package/dist/components/chat/status/ConnectionStatus.js +10 -10
  6. package/dist/components/deck/DeckCell.js +25 -25
  7. package/dist/components/deck/DeckContextMenu.js +20 -20
  8. package/dist/components/deck/DeckGrid.js +1 -1
  9. package/dist/components/deck/DeckTopBar.js +42 -42
  10. package/dist/components/deck/DropZonePreview.js +6 -6
  11. package/dist/components/deck/LayoutSelector.js +53 -53
  12. package/dist/components/deck/WidgetFrame.js +28 -28
  13. package/dist/components/deck/WidgetPalette.js +31 -31
  14. package/dist/components/deck/WidgetTypeSwitcher.js +31 -31
  15. package/dist/components/file-browser/Breadcrumb.js +27 -27
  16. package/dist/components/file-browser/DirectoryTree.js +15 -15
  17. package/dist/components/file-browser/DragPreview.js +58 -58
  18. package/dist/components/file-browser/FileBrowser.js +17 -17
  19. package/dist/components/file-browser/FileBrowserStatusBar.js +18 -18
  20. package/dist/components/file-browser/FileBrowserToolbar.js +27 -27
  21. package/dist/components/file-browser/FileContextMenu.js +47 -47
  22. package/dist/components/file-browser/FileGridView.js +53 -51
  23. package/dist/components/file-browser/FileItemDecorations.js +30 -27
  24. package/dist/components/file-browser/FileListView.js +114 -114
  25. package/dist/components/icons/index.js +1357 -1357
  26. package/dist/components/launchpad/Launchpad.js +14 -14
  27. package/dist/components/launchpad/LaunchpadGrid.js +7 -7
  28. package/dist/components/launchpad/LaunchpadItem.js +11 -11
  29. package/dist/components/launchpad/LaunchpadPagination.js +16 -16
  30. package/dist/components/launchpad/LaunchpadSearch.js +9 -9
  31. package/dist/components/layout/ActivityBar.js +52 -55
  32. package/dist/components/layout/BottomBar.js +17 -17
  33. package/dist/components/layout/MobileNavigationPanel.js +36 -36
  34. package/dist/components/layout/MobileTabBar.js +39 -39
  35. package/dist/components/layout/Panel.js +16 -16
  36. package/dist/components/layout/ResizeHandle.js +19 -19
  37. package/dist/components/layout/Shell.js +50 -50
  38. package/dist/components/layout/Sidebar.js +37 -36
  39. package/dist/components/layout/SidebarPane.js +19 -19
  40. package/dist/components/layout/TopBar.js +16 -16
  41. package/dist/components/layout/TopBarIconButton.js +15 -15
  42. package/dist/components/loading/LoadingOverlay.js +9 -9
  43. package/dist/components/loading/Skeleton.js +11 -11
  44. package/dist/components/ui/Button.js +36 -36
  45. package/dist/components/ui/Card.d.ts +11 -62
  46. package/dist/components/ui/Card.js +64 -237
  47. package/dist/components/ui/Checkbox.js +79 -79
  48. package/dist/components/ui/CommandPalette.js +56 -55
  49. package/dist/components/ui/Dialog.js +91 -93
  50. package/dist/components/ui/DirectoryInput.js +22 -22
  51. package/dist/components/ui/DirectoryPicker.js +8 -8
  52. package/dist/components/ui/Dropdown.js +89 -87
  53. package/dist/components/ui/FeedbackIndicator.js +57 -57
  54. package/dist/components/ui/FileOpenPicker.js +37 -37
  55. package/dist/components/ui/FileSavePicker.js +41 -41
  56. package/dist/components/ui/FloatingWindow.js +266 -261
  57. package/dist/components/ui/Form.js +76 -76
  58. package/dist/components/ui/HighlightBlock.js +14 -14
  59. package/dist/components/ui/Input.js +106 -109
  60. package/dist/components/ui/Pagination.js +86 -86
  61. package/dist/components/ui/Popover.d.ts +33 -0
  62. package/dist/components/ui/Popover.js +133 -0
  63. package/dist/components/ui/ProcessingIndicator.js +344 -345
  64. package/dist/components/ui/Progress.js +94 -97
  65. package/dist/components/ui/QuoteBlock.js +25 -25
  66. package/dist/components/ui/Radio.js +91 -91
  67. package/dist/components/ui/SegmentedControl.js +24 -24
  68. package/dist/components/ui/Stepper.js +68 -60
  69. package/dist/components/ui/Switch.js +25 -25
  70. package/dist/components/ui/Tabs.js +99 -87
  71. package/dist/components/ui/Tag.js +12 -9
  72. package/dist/components/ui/Tooltip.js +30 -30
  73. package/dist/components/ui/floatingPresence.d.ts +7 -0
  74. package/dist/components/ui/floatingPresence.js +80 -39
  75. package/dist/components/ui/index.d.ts +2 -1
  76. package/dist/components/ui/picker/DirectoryFavorites.js +27 -27
  77. package/dist/components/ui/picker/PickerBase.js +107 -107
  78. package/dist/context/NotificationContext.js +4 -4
  79. package/dist/context/ThemeContext.js +71 -70
  80. package/dist/floe.css +205 -43
  81. package/dist/full.js +650 -652
  82. package/dist/language.css +320 -0
  83. package/dist/primitives.css +23 -17
  84. package/dist/standalone.css +1 -0
  85. package/dist/styles/themes/classicSemanticTokens.d.ts +122 -122
  86. package/dist/styles/themes/classicSemanticTokens.js +140 -138
  87. package/dist/styles/themes/index.d.ts +3 -1
  88. package/dist/styles/themes/index.js +10 -10
  89. package/dist/styles/themes/presets.d.ts +2 -2
  90. package/dist/styles/themes/presets.js +34 -34
  91. package/dist/styles/tokens.d.ts +152 -68
  92. package/dist/styles/tokens.js +87 -49
  93. package/dist/styles.css +1 -1
  94. package/dist/surface.css +161 -116
  95. package/dist/themes/dark.css +64 -64
  96. package/dist/themes/light.css +64 -64
  97. package/dist/ui.css +171 -255
  98. package/dist/ui.js +176 -178
  99. package/dist/utils/hotInteraction.d.ts +7 -0
  100. package/dist/utils/hotInteraction.js +12 -12
  101. package/dist/widgets/TerminalWidget.js +28 -28
  102. package/dist/workbench-composition.css +15 -15
  103. package/package.json +1 -1
@@ -1,117 +1,118 @@
1
- import { createSignal as u, createEffect as p, createMemo as O, onCleanup as Q } from "solid-js";
2
- import { createSimpleContext as te } from "./createSimpleContext.js";
3
- import { useResolvedFloeConfig as se } from "./FloeConfigContext.js";
4
- import { isFloeSurfaceStyle as K, applySurfaceStyleAttribute as R, isThemeType as ne, getSystemTheme as re, applyTheme as oe, applyShellThemeAttribute as V, syncThemeTokenOverrides as j, resolveThemeTokens as ce } from "../styles/themes/index.js";
5
- import { normalizeShellThemeSelection as B, presetSupportsMode as D } from "../styles/themes/presets.js";
6
- function S(s, r, l) {
7
- if (r.length !== 0)
8
- return s && r.some((a) => a.name === s) ? s : l && r.some((a) => a.name === l) ? l : r[0]?.name;
1
+ import { createSignal as u, createEffect as p, createMemo as z, onCleanup as O } from "solid-js";
2
+ import { createSimpleContext as ee } from "./createSimpleContext.js";
3
+ import { useResolvedFloeConfig as te } from "./FloeConfigContext.js";
4
+ import { isFloeSurfaceStyle as b, applySurfaceStyleAttribute as Q, isThemeType as se, getSystemTheme as re, applyTheme as oe, applyShellThemeAttribute as R, syncThemeTokenOverrides as V, resolveThemeTokens as ne } from "../styles/themes/index.js";
5
+ import { normalizeShellThemeSelection as j, presetSupportsMode as B } from "../styles/themes/presets.js";
6
+ function S(s, o, l) {
7
+ if (o.length !== 0)
8
+ return s && o.some((m) => m.name === s) ? s : l && o.some((m) => m.name === l) ? l : o[0]?.name;
9
9
  }
10
- function ie() {
11
- const s = se(), r = () => s.config.theme.storageKey, l = () => s.config.theme.defaultTheme, a = () => s.config.theme.surfaceStyleStorageKey ?? `${r()}-surface-style`, b = s.config.theme.defaultSurfaceStyle, y = K(b) ? b : "standard", w = s.persist.load(a(), y), [M, G] = u(K(w) ? w : y), H = (e) => {
12
- const t = K(e) ? e : y;
13
- G(t), s.persist.debouncedSave(a(), t);
10
+ function ce() {
11
+ const s = te(), o = () => s.config.theme.storageKey, l = () => s.config.theme.defaultTheme, m = () => s.config.theme.surfaceStyleStorageKey ?? `${o()}-surface-style`, T = "soft-neumorphic", y = s.persist.load(m(), T);
12
+ b(y) || s.persist.remove(m());
13
+ const [w, D] = u(b(y) ? y : T), G = (e) => {
14
+ D(b(e) ? e : T);
14
15
  };
15
- p(() => R(M()));
16
- const I = () => s.config.theme.tokens, c = () => s.config.theme.presets ?? [], T = () => s.config.theme.presetStorageKey ?? `${r()}-preset`, h = () => S(s.config.theme.defaultPreset, c()), m = () => s.config.theme.shellPresets ?? [], $ = () => s.config.theme.shellPresetStorageKey ?? `${r()}-shell-preset`, C = () => s.config.theme.defaultShellPreset ?? {}, E = s.persist.load(r(), l()), J = ne(E) ? E : l(), [v, U] = u(J), W = s.persist.load(T(), h()), [P, A] = u(S(W, c(), h())), X = s.persist.load($(), void 0), [k, F] = u(B(X, m(), C())), [Y, Z] = u(re());
16
+ p(() => Q(w()));
17
+ const H = () => s.config.theme.tokens, c = () => s.config.theme.presets ?? [], v = () => s.config.theme.presetStorageKey ?? `${o()}-preset`, a = () => S(s.config.theme.defaultPreset, c()), h = () => s.config.theme.shellPresets ?? [], M = () => s.config.theme.shellPresetStorageKey ?? `${o()}-shell-preset`, $ = () => s.config.theme.defaultShellPreset ?? {}, C = s.persist.load(o(), l()), I = se(C) ? C : l(), [P, J] = u(I), U = s.persist.load(v(), a()), [k, E] = u(S(U, c(), a())), W = s.persist.load(M(), void 0), [K, A] = u(j(W, h(), $())), [X, Y] = u(re());
17
18
  let g = [];
18
- const N = O(() => {
19
- const e = S(P(), c(), h());
19
+ const F = z(() => {
20
+ const e = S(k(), c(), a());
20
21
  if (e)
21
22
  return c().find((t) => t.name === e);
22
23
  }), f = () => {
23
- const e = v();
24
- return e === "system" ? Y() : e;
25
- }, L = (e) => {
26
- const t = k()[e];
24
+ const e = P();
25
+ return e === "system" ? X() : e;
26
+ }, N = (e) => {
27
+ const t = K()[e];
27
28
  if (t)
28
- return m().find((n) => n.name === t && D(n, e));
29
- }, q = O(() => L(f()));
29
+ return h().find((r) => r.name === t && B(r, e));
30
+ }, L = z(() => N(f()));
30
31
  if (p(() => {
31
- const e = S(P(), c(), h());
32
- P() !== e && A(e);
32
+ const e = S(k(), c(), a());
33
+ k() !== e && E(e);
33
34
  }), p(() => {
34
- const e = k(), t = B(e, m(), C());
35
- (e.light !== t.light || e.dark !== t.dark) && F(t);
35
+ const e = K(), t = j(e, h(), $());
36
+ (e.light !== t.light || e.dark !== t.dark) && A(t);
36
37
  }), typeof window < "u") {
37
- const e = window.matchMedia("(prefers-color-scheme: dark)"), t = (n) => {
38
- Z(n.matches ? "dark" : "light");
38
+ const e = window.matchMedia("(prefers-color-scheme: dark)"), t = (r) => {
39
+ Y(r.matches ? "dark" : "light");
39
40
  };
40
- e.addEventListener("change", t), Q(() => e.removeEventListener("change", t));
41
+ e.addEventListener("change", t), O(() => e.removeEventListener("change", t));
41
42
  }
42
43
  p(() => {
43
- const e = v(), t = f(), n = q();
44
- oe(e), V(n?.name), g = j(ce(t, I(), n?.tokens, N()?.tokens), g), typeof document < "u" && queueMicrotask(() => {
44
+ const e = P(), t = f(), r = L();
45
+ oe(e), R(r?.name), g = V(ne(t, H(), r?.tokens, F()?.tokens), g), typeof document < "u" && queueMicrotask(() => {
45
46
  const i = /* @__PURE__ */ new Set();
46
47
  for (; ; ) {
47
48
  const d = (document.documentElement.getAnimations?.({
48
49
  subtree: !0
49
- }) ?? []).filter((o) => !i.has(o) && "transitionProperty" in o && /(^|-)color$|^(fill|stroke|box-shadow)$/.test(String(o.transitionProperty)));
50
+ }) ?? []).filter((n) => !i.has(n) && "transitionProperty" in n && /(^|-)color$|^(fill|stroke|box-shadow)$/.test(String(n.transitionProperty)));
50
51
  if (d.length === 0) break;
51
- for (const o of d)
52
- i.add(o), o.finish();
52
+ for (const n of d)
53
+ i.add(n), n.finish();
53
54
  }
54
55
  });
55
- }), Q(() => {
56
- R(void 0), V(void 0), g = j(void 0, g);
56
+ }), O(() => {
57
+ Q(void 0), R(void 0), g = V(void 0, g);
57
58
  });
58
59
  const x = (e) => {
59
- U(e), s.persist.debouncedSave(r(), e);
60
- }, _ = () => {
60
+ J(e), s.persist.debouncedSave(o(), e);
61
+ }, Z = () => {
61
62
  const e = f();
62
63
  x(e === "light" ? "dark" : "light");
63
- }, ee = (e) => {
64
- const t = S(e, c(), h());
65
- if (A(t), !t) {
66
- s.persist.remove(T());
64
+ }, _ = (e) => {
65
+ const t = S(e, c(), a());
66
+ if (E(t), !t) {
67
+ s.persist.remove(v());
67
68
  return;
68
69
  }
69
- s.persist.debouncedSave(T(), t);
70
- }, z = (e, t) => {
71
- const n = m().find((o) => o.name === t && D(o, e));
72
- if (!n) return !1;
73
- const i = k();
74
- if (i[e] === n.name) return !0;
70
+ s.persist.debouncedSave(v(), t);
71
+ }, q = (e, t) => {
72
+ const r = h().find((n) => n.name === t && B(n, e));
73
+ if (!r) return !1;
74
+ const i = K();
75
+ if (i[e] === r.name) return !0;
75
76
  const d = {
76
77
  ...i,
77
78
  version: 1,
78
- [e]: n.name
79
+ [e]: r.name
79
80
  };
80
- return F(d), s.persist.debouncedSave($(), d), !0;
81
+ return A(d), s.persist.debouncedSave(M(), d), !0;
81
82
  };
82
83
  return {
83
- surfaceStyle: M,
84
- setSurfaceStyle: H,
85
- theme: v,
84
+ surfaceStyle: w,
85
+ setSurfaceStyle: G,
86
+ theme: P,
86
87
  resolvedTheme: f,
87
88
  setTheme: x,
88
- toggleTheme: _,
89
+ toggleTheme: Z,
89
90
  themePresets: c,
90
- themePreset: N,
91
- setThemePreset: ee,
92
- shellPresets: m,
93
- shellPreset: q,
94
- shellPresetForMode: L,
91
+ themePreset: F,
92
+ setThemePreset: _,
93
+ shellPresets: h,
94
+ shellPreset: L,
95
+ shellPresetForMode: N,
95
96
  setShellPreset: (e) => {
96
- const t = m().find((i) => i.name === e);
97
+ const t = h().find((i) => i.name === e);
97
98
  if (!t) return;
98
- const n = t.mode === "light" || t.mode === "dark" ? t.mode : f();
99
- z(n, e);
99
+ const r = t.mode === "light" || t.mode === "dark" ? t.mode : f();
100
+ q(r, e);
100
101
  },
101
102
  selectShellTheme: (e, t) => {
102
- z(e, t) && x(e);
103
+ q(e, t) && x(e);
103
104
  }
104
105
  };
105
106
  }
106
107
  const {
107
- Provider: Se,
108
- use: ge
109
- } = te({
108
+ Provider: ue,
109
+ use: Se
110
+ } = ee({
110
111
  name: "Theme",
111
- init: ie
112
+ init: ce
112
113
  });
113
114
  export {
114
- Se as ThemeProvider,
115
- ie as createThemeService,
116
- ge as useTheme
115
+ ue as ThemeProvider,
116
+ ce as createThemeService,
117
+ Se as useTheme
117
118
  };
package/dist/floe.css CHANGED
@@ -1,5 +1,6 @@
1
1
  @import './progress-shimmer.css';
2
2
  @import './primitives.css';
3
+ @import './language.css';
3
4
  @import './input-focus.css';
4
5
  @import './surface.css';
5
6
  @import './file-icons.css';
@@ -30,12 +31,63 @@
30
31
  * CSS variables at runtime.
31
32
  */
32
33
 
33
- /* Border radius - these work fine with var() */
34
+ /*
35
+ * Tailwind scale → language scale.
36
+ *
37
+ * Utilities are the vocabulary components actually write, so every Tailwind
38
+ * scale the language owns is mapped onto the language token rather than left at
39
+ * the Tailwind default. Without this, `rounded-xl`, `ease-out`, `shadow-sm` and
40
+ * `text-sm` spoke a second, parallel design system: numerically close enough to
41
+ * look accidental, different enough to break the ladder when either side moved.
42
+ *
43
+ * Each mapping below resolves at computed-value time, so the language file stays
44
+ * the only place a value is written.
45
+ */
34
46
  @theme {
35
- --radius-sm: calc(var(--radius) - 4px);
36
- --radius-md: calc(var(--radius) - 2px);
37
- --radius-lg: var(--radius);
38
- --radius-xl: calc(var(--radius) + 4px);
47
+ /* Radius — the ladder is concentric: xs 4 · sm 6 · md 8 · control 10 ·
48
+ * group 12 · card 16 · panel 20.
49
+ *
50
+ * Named by what the surface is, not by how big it is — `rounded-control` says
51
+ * which rung to read, where `rounded-lg` (Tailwind's 8px scale) could only
52
+ * ever be a guess. The bare `rounded` utility has no `@theme` override in v4,
53
+ * so every call site now names its rung; nothing resolves to a literal. */
54
+ --radius-xs: var(--floe-radius-xs);
55
+ --radius-sm: var(--floe-radius-sm);
56
+ --radius-md: var(--floe-radius-md);
57
+ --radius-control: var(--floe-radius-control);
58
+ --radius-group: var(--floe-radius-group);
59
+ --radius-card: var(--floe-radius-card);
60
+ --radius-panel: var(--floe-radius-panel);
61
+
62
+ /* Motion — `ease-out` and `ease-in-out` were the Tailwind curves, which start
63
+ * abruptly and settle late; the language curves decelerate. */
64
+ --ease-out: var(--floe-ease-out);
65
+ --ease-in-out: var(--floe-ease-in-out);
66
+
67
+ /* Elevation — three steps, so a shadow utility can no longer invent a fourth. */
68
+ --shadow: var(--floe-elevation-raised);
69
+ --shadow-sm: var(--floe-elevation-raised);
70
+ --shadow-md: var(--floe-elevation-float);
71
+ --shadow-lg: var(--floe-elevation-float);
72
+ --shadow-xl: var(--floe-elevation-float-strong);
73
+ --shadow-2xl: var(--floe-elevation-float-strong);
74
+
75
+ /* Type — one step per role: caption 11 · supporting 12 · control and body 13
76
+ * · heading 16 · display 22. Tailwind's 14px control size had no seat on that
77
+ * ladder, which is why control text drifted between 12, 13 and 14px.
78
+ *
79
+ * `text-caption` is the role-named utility. Metadata, badges and axis labels
80
+ * used to hard-code `text-[10px]` or `text-[11px]`; the smaller value sat below
81
+ * the ladder and existed only because a single component needed two pixels. */
82
+ --text-caption: var(--floe-type-caption);
83
+ --text-xs: var(--floe-type-supporting);
84
+ --text-sm: var(--floe-type-control);
85
+ --text-base: var(--floe-type-heading);
86
+ --text-lg: var(--floe-type-display);
87
+ --text-caption--line-height: var(--floe-line-caption);
88
+ --text-xs--line-height: var(--floe-line-caption);
89
+ --text-sm--line-height: var(--floe-line-control);
90
+ --text-base--line-height: var(--floe-line-heading);
39
91
  }
40
92
 
41
93
  /* Custom color utilities using CSS variables */
@@ -403,8 +455,8 @@
403
455
  /* Animation utilities */
404
456
  @layer utilities {
405
457
  .floe-floating-presence {
406
- --floe-floating-enter-duration: 220ms;
407
- --floe-floating-exit-duration: 160ms;
458
+ --floe-floating-enter-duration: 260ms;
459
+ --floe-floating-exit-duration: 180ms;
408
460
  --floe-floating-presence-x: 0;
409
461
  --floe-floating-presence-y: 0;
410
462
  --floe-floating-presence-scale: 1;
@@ -418,16 +470,20 @@
418
470
  transform: translate(var(--floe-floating-presence-x), var(--floe-floating-presence-y)) scale(var(--floe-floating-presence-scale));
419
471
  transform-origin: var(--floe-floating-origin, center);
420
472
  transition:
421
- opacity var(--floe-floating-enter-duration) ease-out,
422
- transform var(--floe-floating-enter-duration) cubic-bezier(0.2, 0.75, 0.25, 1);
423
- will-change: opacity, transform;
473
+ opacity var(--floe-floating-enter-duration) var(--floe-ease-out, cubic-bezier(0.22, 1, 0.36, 1)),
474
+ transform var(--floe-floating-enter-duration) cubic-bezier(0.16, 1, 0.3, 1);
424
475
  }
425
476
 
477
+ /* Promotion is scoped to the frames that move. A settled overlay holding
478
+ * `will-change` keeps a compositor layer alive for as long as it is open,
479
+ * which costs memory and rasterisation on every window, menu and dialog that
480
+ * merely sits on screen. Only the two transitions need it. */
426
481
  .floe-floating-presence[data-floating-presence='entering'] {
427
482
  opacity: 0;
428
483
  --floe-floating-presence-x: var(--floe-floating-enter-x);
429
484
  --floe-floating-presence-y: var(--floe-floating-enter-y);
430
485
  --floe-floating-presence-scale: var(--floe-floating-enter-scale);
486
+ will-change: opacity, transform;
431
487
  }
432
488
 
433
489
  .floe-floating-presence[data-floating-presence='exiting'] {
@@ -436,9 +492,10 @@
436
492
  --floe-floating-presence-x: var(--floe-floating-exit-x);
437
493
  --floe-floating-presence-y: var(--floe-floating-exit-y);
438
494
  --floe-floating-presence-scale: var(--floe-floating-exit-scale);
495
+ will-change: opacity, transform;
439
496
  transition:
440
- opacity var(--floe-floating-exit-duration) ease-in,
441
- transform var(--floe-floating-exit-duration) cubic-bezier(0.4, 0, 1, 1);
497
+ opacity var(--floe-floating-exit-duration) cubic-bezier(0.4, 0, 0.6, 1),
498
+ transform var(--floe-floating-exit-duration) cubic-bezier(0.4, 0, 0.6, 1);
442
499
  }
443
500
 
444
501
  .floe-floating-presence--suspended {
@@ -451,10 +508,11 @@
451
508
  }
452
509
 
453
510
  .floe-floating-dialog-panel {
454
- --floe-floating-enter-y: 0.25rem;
455
- --floe-floating-exit-y: 0.125rem;
456
- --floe-floating-enter-scale: 1;
457
- --floe-floating-exit-scale: 1;
511
+ --floe-floating-enter-y: 0.75rem;
512
+ --floe-floating-exit-y: 0.375rem;
513
+ --floe-floating-enter-scale: 0.965;
514
+ --floe-floating-exit-scale: 0.985;
515
+ --floe-floating-origin: center top;
458
516
  }
459
517
 
460
518
  .floe-side-drawer-overlay {
@@ -505,7 +563,7 @@
505
563
  --floe-floating-exit-y: calc(100% + 1.25rem);
506
564
  --floe-floating-enter-scale: 1;
507
565
  --floe-floating-exit-scale: 1;
508
- border-radius: 1rem;
566
+ border-radius: var(--floe-radius-panel);
509
567
  overflow: hidden;
510
568
  }
511
569
 
@@ -517,12 +575,12 @@
517
575
  }
518
576
 
519
577
  .floe-floating-menu {
520
- --floe-floating-enter-duration: 220ms;
578
+ --floe-floating-enter-duration: 240ms;
521
579
  --floe-floating-exit-duration: 160ms;
522
- --floe-floating-enter-y: -0.25rem;
580
+ --floe-floating-enter-y: -0.375rem;
523
581
  --floe-floating-exit-y: -0.125rem;
524
- --floe-floating-enter-scale: 1;
525
- --floe-floating-exit-scale: 1;
582
+ --floe-floating-enter-scale: 0.97;
583
+ --floe-floating-exit-scale: 0.99;
526
584
  --floe-floating-origin: top;
527
585
  }
528
586
 
@@ -533,29 +591,30 @@
533
591
  }
534
592
 
535
593
  .floe-floating-tooltip {
536
- --floe-floating-enter-duration: 80ms;
537
- --floe-floating-exit-duration: 70ms;
594
+ --floe-floating-enter-duration: 140ms;
595
+ --floe-floating-exit-duration: 90ms;
538
596
  --floe-floating-enter-y: -0.125rem;
539
597
  --floe-floating-exit-y: -0.0625rem;
540
- --floe-floating-enter-scale: 1;
598
+ --floe-floating-enter-scale: 0.96;
541
599
  --floe-floating-exit-scale: 1;
542
600
  }
543
601
 
544
602
  .floe-floating-window-motion {
545
- --floe-floating-enter-y: 4px;
546
- --floe-floating-exit-y: 2px;
547
- --floe-floating-enter-duration: 220ms;
548
- --floe-floating-exit-duration: 160ms;
549
- --floe-floating-enter-scale: 1;
550
- --floe-floating-exit-scale: 1;
603
+ --floe-floating-enter-y: 8px;
604
+ --floe-floating-exit-y: 4px;
605
+ --floe-floating-enter-duration: 260ms;
606
+ --floe-floating-exit-duration: 180ms;
607
+ --floe-floating-enter-scale: 0.98;
608
+ --floe-floating-exit-scale: 0.99;
609
+ --floe-floating-origin: center top;
551
610
  }
552
611
 
553
612
  .animate-in {
554
- animation: animate-in 200ms ease-out;
613
+ animation: animate-in 220ms cubic-bezier(0.16, 1, 0.3, 1);
555
614
  }
556
615
 
557
616
  .animate-out {
558
- animation: animate-out 150ms ease-in;
617
+ animation: animate-out 150ms cubic-bezier(0.4, 0, 0.6, 1);
559
618
  }
560
619
 
561
620
  .slide-in-from-top {
@@ -608,6 +667,106 @@
608
667
  will-change: opacity;
609
668
  }
610
669
  }
670
+
671
+ /*
672
+ * Hover motion.
673
+ *
674
+ * These utilities touch transform, color and shadow only. Width, height,
675
+ * padding, border width and position stay untouched so action groups,
676
+ * toolbars and drag/resize geometry never reflow while the pointer moves.
677
+ * Labels keep their own intrinsic box: lift happens on the control, never
678
+ * by growing the text.
679
+ */
680
+ .floe-hover-lift {
681
+ transition:
682
+ transform var(--floe-duration-fast) var(--floe-ease-out),
683
+ box-shadow var(--floe-duration-base) var(--floe-ease-out),
684
+ background-color var(--floe-duration-fast) var(--floe-ease-in-out),
685
+ border-color var(--floe-duration-fast) var(--floe-ease-in-out),
686
+ color var(--floe-duration-fast) var(--floe-ease-in-out);
687
+ }
688
+
689
+ .floe-hover-lift:hover {
690
+ transform: translateY(var(--floe-hover-lift));
691
+ }
692
+
693
+ .floe-hover-lift:active {
694
+ transform: translateY(0) scale(0.99);
695
+ transition-duration: var(--floe-duration-instant);
696
+ }
697
+
698
+ /* Rows, menu items and list entries shift tint instead of moving, so dense
699
+ * lists stay perceptually still while still feeling responsive. */
700
+ .floe-hover-surface {
701
+ transition:
702
+ background-color var(--floe-duration-fast) var(--floe-ease-in-out),
703
+ color var(--floe-duration-fast) var(--floe-ease-in-out),
704
+ border-color var(--floe-duration-fast) var(--floe-ease-in-out),
705
+ box-shadow var(--floe-duration-base) var(--floe-ease-out);
706
+ }
707
+
708
+ /* Soft highlight for hovered cards and panels: a one-pixel inner hairline
709
+ * plus a whisper of elevation, never a hard ring. */
710
+ .floe-hover-raised {
711
+ transition:
712
+ transform var(--floe-duration-base) var(--floe-ease-out),
713
+ box-shadow var(--floe-duration-base) var(--floe-ease-out),
714
+ border-color var(--floe-duration-fast) var(--floe-ease-in-out);
715
+ }
716
+
717
+ .floe-hover-raised:hover {
718
+ transform: translateY(var(--floe-hover-lift));
719
+ /* The inner line is the hairline step, not a fifth shadow strength; the
720
+ * outer one is the first elevation. Previously this was a bespoke recipe
721
+ * that put two more distances into the language. */
722
+ box-shadow:
723
+ 0 1px 0 0 var(--floe-line-quiet) inset,
724
+ var(--floe-elevation-raised);
725
+ }
726
+
727
+ /* Pointer feedback for icon-only affordances: the icon sits in a rounded
728
+ * well that fills on hover, matching the shell's soft surface language. */
729
+ .floe-hover-well {
730
+ transition:
731
+ background-color var(--floe-duration-fast) var(--floe-ease-in-out),
732
+ color var(--floe-duration-fast) var(--floe-ease-in-out),
733
+ transform var(--floe-duration-fast) var(--floe-ease-out);
734
+ }
735
+
736
+ .floe-hover-well:hover {
737
+ background-color: color-mix(in srgb, var(--accent) 80%, transparent);
738
+ }
739
+
740
+ .floe-hover-well:active {
741
+ transform: scale(0.94);
742
+ transition-duration: var(--floe-duration-instant);
743
+ }
744
+
745
+ /* Text and icon nudges: tiny directional shift that reads as intent without
746
+ * disturbing the layout box. */
747
+ .floe-hover-nudge-x {
748
+ transition: transform var(--floe-duration-fast) var(--floe-ease-out);
749
+ }
750
+
751
+ .floe-hover-nudge-x:hover {
752
+ transform: translateX(2px);
753
+ }
754
+
755
+ @media (prefers-reduced-motion: reduce) {
756
+ .floe-hover-lift,
757
+ .floe-hover-lift:hover,
758
+ .floe-hover-lift:active,
759
+ .floe-hover-surface,
760
+ .floe-hover-raised,
761
+ .floe-hover-raised:hover,
762
+ .floe-hover-well,
763
+ .floe-hover-well:active,
764
+ .floe-hover-nudge-x,
765
+ .floe-hover-nudge-x:hover {
766
+ transform: none;
767
+ transition-duration: 1ms;
768
+ }
769
+ }
611
770
  }
612
771
 
613
772
  @keyframes animate-in {
@@ -737,29 +896,32 @@
737
896
  pointer-events: auto;
738
897
  transform-origin: bottom center;
739
898
  transition:
740
- border-radius 200ms ease,
741
- border-color 180ms ease,
742
- opacity 120ms ease;
899
+ border-radius var(--floe-duration-base) var(--floe-ease-out),
900
+ border-color var(--floe-duration-fast) var(--floe-ease-out),
901
+ opacity var(--floe-duration-fast) var(--floe-ease-out);
743
902
  }
744
903
 
745
904
  .floe-bottom-bar-companion[data-companion-phase='expanding'],
746
905
  .floe-bottom-bar-companion[data-companion-phase='expanded'] {
747
906
  border-radius: var(--floe-bottom-bar-companion-expanded-radius, 0.5rem);
748
907
  border-color: color-mix(in srgb, var(--border) 96%, transparent);
749
- box-shadow:
750
- 0 22px 54px -24px color-mix(in srgb, var(--foreground) 34%, transparent),
751
- 0 6px 18px -12px color-mix(in srgb, var(--foreground) 24%, transparent);
908
+ box-shadow: var(--floe-elevation-float-strong);
752
909
  }
753
910
 
754
911
  .floe-bottom-bar-companion[data-companion-phase='expanding'],
755
912
  .floe-bottom-bar-companion[data-companion-phase='collapsing'] {
756
913
  will-change: left, top, width, height;
914
+ /* Geometry morphs on the slow step with the language curve; the driver
915
+ * re-measures between phases, so a longer duration would only widen the
916
+ * window where the measured target and the visible box disagree. */
757
917
  transition:
758
- left 360ms cubic-bezier(0.22, 0.8, 0.24, 1),
759
- top 360ms cubic-bezier(0.22, 0.8, 0.24, 1),
760
- width 360ms cubic-bezier(0.22, 0.8, 0.24, 1),
761
- height 360ms cubic-bezier(0.22, 0.8, 0.24, 1),
762
- border-radius 200ms ease, border-color 180ms ease, opacity 120ms ease;
918
+ left var(--floe-duration-slow) var(--floe-ease-out),
919
+ top var(--floe-duration-slow) var(--floe-ease-out),
920
+ width var(--floe-duration-slow) var(--floe-ease-out),
921
+ height var(--floe-duration-slow) var(--floe-ease-out),
922
+ border-radius var(--floe-duration-base) var(--floe-ease-out),
923
+ border-color var(--floe-duration-fast) var(--floe-ease-out),
924
+ opacity var(--floe-duration-fast) var(--floe-ease-out);
763
925
  }
764
926
 
765
927
  .floe-bottom-bar-companion[data-companion-visibility='hidden'],