@medalsocial/meda 0.3.0 → 1.1.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.
Files changed (128) hide show
  1. package/README.md +26 -0
  2. package/dist/brand/index.d.ts +2 -0
  3. package/dist/brand/index.js +1 -0
  4. package/dist/brand/medal-social-mark.d.ts +7 -0
  5. package/dist/brand/medal-social-mark.js +6 -0
  6. package/dist/brand/public.d.ts +2 -0
  7. package/dist/brand/public.js +1 -0
  8. package/dist/chat/latency-badge.js +7 -3
  9. package/dist/chat/latency-breakdown.js +1 -1
  10. package/dist/chat/tool-call-block.js +1 -1
  11. package/dist/chat/turn-card.js +4 -2
  12. package/dist/components/ui/command.d.ts +19 -0
  13. package/dist/components/ui/command.js +37 -0
  14. package/dist/components/ui/dialog.d.ts +26 -0
  15. package/dist/components/ui/dialog.js +39 -0
  16. package/dist/components/ui/drawer.d.ts +16 -0
  17. package/dist/components/ui/drawer.js +35 -0
  18. package/dist/components/ui/dropdown-menu.d.ts +32 -0
  19. package/dist/components/ui/dropdown-menu.js +68 -0
  20. package/dist/components/ui/tooltip.d.ts +6 -0
  21. package/dist/components/ui/tooltip.js +17 -0
  22. package/dist/index.d.ts +3 -1
  23. package/dist/index.js +3 -1
  24. package/dist/lib/utils.d.ts +2 -0
  25. package/dist/lib/utils.js +5 -0
  26. package/dist/marketing/index.d.ts +5 -0
  27. package/dist/marketing/index.js +4 -0
  28. package/dist/marketing/marketing-callout.d.ts +2 -0
  29. package/dist/marketing/marketing-callout.js +8 -0
  30. package/dist/marketing/marketing-contact.d.ts +2 -0
  31. package/dist/marketing/marketing-contact.js +19 -0
  32. package/dist/marketing/marketing-cta-list.d.ts +2 -0
  33. package/dist/marketing/marketing-cta-list.js +21 -0
  34. package/dist/marketing/marketing-lead-magnet.d.ts +2 -0
  35. package/dist/marketing/marketing-lead-magnet.js +9 -0
  36. package/dist/marketing/public.d.ts +1 -0
  37. package/dist/marketing/public.js +1 -0
  38. package/dist/marketing/types.d.ts +67 -0
  39. package/dist/marketing/types.js +1 -0
  40. package/dist/marketing/utils.d.ts +2 -0
  41. package/dist/marketing/utils.js +6 -0
  42. package/dist/panel/inspector-json.js +5 -3
  43. package/dist/shell/app-shell-auth.d.ts +5 -0
  44. package/dist/shell/app-shell-auth.js +50 -0
  45. package/dist/shell/app-shell-chat.d.ts +6 -0
  46. package/dist/shell/app-shell-chat.js +9 -0
  47. package/dist/shell/app-shell-workspace.d.ts +10 -0
  48. package/dist/shell/app-shell-workspace.js +55 -0
  49. package/dist/shell/app-shell.d.ts +25 -0
  50. package/dist/shell/app-shell.js +26 -0
  51. package/dist/shell/command-palette.d.ts +23 -0
  52. package/dist/shell/command-palette.js +138 -0
  53. package/dist/shell/context-rail.d.ts +31 -0
  54. package/dist/shell/context-rail.js +81 -0
  55. package/dist/shell/extras/index.d.ts +4 -0
  56. package/dist/shell/extras/index.js +4 -0
  57. package/dist/shell/{shell-desktop-panel-dock.js → extras/shell-desktop-panel-dock.js} +1 -1
  58. package/dist/shell/extras/types.d.ts +64 -0
  59. package/dist/shell/extras/types.js +2 -0
  60. package/dist/shell/icon-rail.d.ts +29 -0
  61. package/dist/shell/icon-rail.js +38 -0
  62. package/dist/shell/index.d.ts +19 -15
  63. package/dist/shell/index.js +30 -15
  64. package/dist/shell/internal/mobile-bottom-nav.d.ts +23 -0
  65. package/dist/shell/internal/mobile-bottom-nav.js +79 -0
  66. package/dist/shell/internal/mobile-drawers.d.ts +26 -0
  67. package/dist/shell/internal/mobile-drawers.js +76 -0
  68. package/dist/shell/internal/mobile-header.d.ts +23 -0
  69. package/dist/shell/internal/mobile-header.js +22 -0
  70. package/dist/shell/layout-state.d.ts +32 -0
  71. package/dist/shell/layout-state.js +98 -0
  72. package/dist/shell/motion.d.ts +22 -0
  73. package/dist/shell/motion.js +19 -0
  74. package/dist/shell/resizable-shell.d.ts +45 -0
  75. package/dist/shell/resizable-shell.js +15 -0
  76. package/dist/shell/right-panel.d.ts +15 -0
  77. package/dist/shell/right-panel.js +126 -0
  78. package/dist/shell/shell-header.d.ts +10 -9
  79. package/dist/shell/shell-header.js +57 -14
  80. package/dist/shell/shell-layout-utils.d.ts +2 -2
  81. package/dist/shell/shell-main.d.ts +8 -0
  82. package/dist/shell/shell-main.js +11 -0
  83. package/dist/shell/shell-provider.d.ts +65 -0
  84. package/dist/shell/shell-provider.js +152 -0
  85. package/dist/shell/shell-route-utils.d.ts +1 -1
  86. package/dist/shell/{navigation-area.d.ts → theme-next-themes.d.ts} +1 -1
  87. package/dist/shell/theme-next-themes.js +24 -0
  88. package/dist/shell/theme.d.ts +9 -0
  89. package/dist/shell/theme.js +58 -0
  90. package/dist/shell/types.d.ts +73 -38
  91. package/dist/shell/types.js +1 -0
  92. package/dist/shell/use-shell-viewport.d.ts +2 -0
  93. package/dist/shell/use-shell-viewport.js +40 -0
  94. package/dist/shell/utils.d.ts +1 -1
  95. package/dist/{styles.css → styles/globals.css} +2 -2
  96. package/dist/styles/theme.css +147 -0
  97. package/dist/styles/tokens.css +338 -0
  98. package/dist/timeline/date-switcher.js +3 -1
  99. package/dist/timeline/event-card.js +5 -13
  100. package/dist/timeline/live-indicator.js +3 -1
  101. package/dist/timeline/scrub-bar.js +8 -9
  102. package/dist/timeline/timeline-rail.js +6 -4
  103. package/dist/timeline/timeline-tape.js +7 -8
  104. package/dist/voice/voice-level.js +3 -1
  105. package/dist/voice/voice-orb-scene.js +1 -0
  106. package/dist/voice/voice-orb.js +9 -11
  107. package/dist/voice/voice-status-pill.js +4 -9
  108. package/package.json +57 -6
  109. package/dist/shell/navigation-area.js +0 -4
  110. package/dist/shell/public.d.ts +0 -2
  111. package/dist/shell/public.js +0 -1
  112. package/dist/shell/shell-app-rail.d.ts +0 -18
  113. package/dist/shell/shell-app-rail.js +0 -27
  114. package/dist/shell/shell-frame.d.ts +0 -13
  115. package/dist/shell/shell-frame.js +0 -4
  116. package/dist/shell/shell-module-nav.d.ts +0 -29
  117. package/dist/shell/shell-module-nav.js +0 -16
  118. package/dist/shell/shell-panel-rail.d.ts +0 -9
  119. package/dist/shell/shell-panel-rail.js +0 -17
  120. package/dist/shell/shell-state.d.ts +0 -39
  121. package/dist/shell/shell-state.js +0 -135
  122. /package/dist/shell/{shell-desktop-panel-dock.d.ts → extras/shell-desktop-panel-dock.d.ts} +0 -0
  123. /package/dist/shell/{shell-scrollable-content.d.ts → extras/shell-scrollable-content.d.ts} +0 -0
  124. /package/dist/shell/{shell-scrollable-content.js → extras/shell-scrollable-content.js} +0 -0
  125. /package/dist/shell/{shell-tab-bar.d.ts → extras/shell-tab-bar.d.ts} +0 -0
  126. /package/dist/shell/{shell-tab-bar.js → extras/shell-tab-bar.js} +0 -0
  127. /package/dist/shell/{workbench-layout.d.ts → extras/workbench-layout.d.ts} +0 -0
  128. /package/dist/shell/{workbench-layout.js → extras/workbench-layout.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medalsocial/meda",
3
- "version": "0.3.0",
3
+ "version": "1.1.0",
4
4
  "description": "Shared Meda UI shell and runtime package.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -14,6 +14,9 @@
14
14
  "type": "module",
15
15
  "main": "./dist/index.js",
16
16
  "types": "./dist/index.d.ts",
17
+ "engines": {
18
+ "node": ">=22"
19
+ },
17
20
  "files": [
18
21
  "README.md",
19
22
  "components.json",
@@ -24,6 +27,10 @@
24
27
  "types": "./dist/index.d.ts",
25
28
  "default": "./dist/index.js"
26
29
  },
30
+ "./brand": {
31
+ "types": "./dist/brand/index.d.ts",
32
+ "default": "./dist/brand/index.js"
33
+ },
27
34
  "./shell": {
28
35
  "types": "./dist/shell/index.d.ts",
29
36
  "default": "./dist/shell/index.js"
@@ -36,6 +43,10 @@
36
43
  "types": "./dist/timeline/index.d.ts",
37
44
  "default": "./dist/timeline/index.js"
38
45
  },
46
+ "./marketing": {
47
+ "types": "./dist/marketing/index.d.ts",
48
+ "default": "./dist/marketing/index.js"
49
+ },
39
50
  "./chat": {
40
51
  "types": "./dist/chat/index.d.ts",
41
52
  "default": "./dist/chat/index.js"
@@ -44,19 +55,36 @@
44
55
  "types": "./dist/panel/index.d.ts",
45
56
  "default": "./dist/panel/index.js"
46
57
  },
47
- "./styles.css": "./dist/styles.css"
58
+ "./styles": {
59
+ "default": "./dist/styles/theme.css"
60
+ },
61
+ "./styles.css": {
62
+ "default": "./dist/styles/theme.css"
63
+ },
64
+ "./styles/tokens": {
65
+ "default": "./dist/styles/tokens.css"
66
+ }
48
67
  },
68
+ "sideEffects": [
69
+ "**/*.css",
70
+ "./dist/styles/*.css"
71
+ ],
49
72
  "publishConfig": {
50
73
  "access": "public"
51
74
  },
52
75
  "peerDependencies": {
53
76
  "@react-three/fiber": "^9.0.0",
77
+ "lucide-react": "^1.8.0",
78
+ "next-themes": "^0.4.0",
54
79
  "react": ">=19",
55
80
  "react-dom": ">=19"
56
81
  },
57
82
  "peerDependenciesMeta": {
58
83
  "@react-three/fiber": {
59
84
  "optional": true
85
+ },
86
+ "next-themes": {
87
+ "optional": true
60
88
  }
61
89
  },
62
90
  "dependencies": {
@@ -66,7 +94,6 @@
66
94
  "class-variance-authority": "^0.7.1",
67
95
  "clsx": "^2.1.1",
68
96
  "cmdk": "^1.1.1",
69
- "lucide-react": "^1.8.0",
70
97
  "react-resizable-panels": "^4.10.0",
71
98
  "tailwind-merge": "^3.5.0",
72
99
  "three": "^0.170.0",
@@ -78,6 +105,13 @@
78
105
  "@changesets/cli": "^2.29.0",
79
106
  "@react-three/drei": "^10.7.7",
80
107
  "@react-three/fiber": "^9.6.0",
108
+ "@size-limit/preset-small-lib": "^12.1.0",
109
+ "@storybook/addon-a11y": "^10.3.5",
110
+ "@storybook/addon-docs": "10.3.5",
111
+ "@storybook/addon-mcp": "^0.6.0",
112
+ "@storybook/addon-themes": "^10.3.5",
113
+ "@storybook/addon-vitest": "^10.3.5",
114
+ "@storybook/react-vite": "^10.3.5",
81
115
  "@tailwindcss/vite": "^4.2.4",
82
116
  "@testing-library/jest-dom": "^6.9.1",
83
117
  "@testing-library/react": "^16.3.2",
@@ -85,10 +119,17 @@
85
119
  "@types/react-dom": "^19.2.3",
86
120
  "@types/three": "^0.184.0",
87
121
  "@vitejs/plugin-react": "^6.0.1",
122
+ "@vitest/browser-playwright": "4.1.4",
123
+ "@vitest/coverage-v8": "4.1.4",
124
+ "chromatic": "^16.6.0",
88
125
  "husky": "^9.1.7",
89
126
  "jsdom": "^29.0.2",
127
+ "next-themes": "^0.4.0",
128
+ "playwright": "^1.59.1",
90
129
  "react": "^19.2.4",
91
130
  "react-dom": "^19.2.4",
131
+ "size-limit": "^12.1.0",
132
+ "storybook": "^10.3.5",
92
133
  "tailwindcss": "^4.2.4",
93
134
  "typescript": "~6.0.2",
94
135
  "vite": "^8.0.4",
@@ -103,14 +144,24 @@
103
144
  "format": "biome format --write .",
104
145
  "demo:dev": "vite --config vite.config.demo.ts",
105
146
  "demo:build": "vite build --config vite.config.demo.ts",
106
- "worker:build": "pnpm demo:build && node ./scripts/copy-registry-to-dist.mjs",
107
- "worker:dev": "pnpm worker:build && wrangler dev",
147
+ "worker:build": "pnpm demo:build && pnpm storybook:build && node ./scripts/copy-registry-to-dist.mjs",
148
+ "worker:build:dev": "pnpm demo:build && node ./scripts/copy-registry-to-dist.mjs --skip-storybook",
149
+ "worker:dev": "pnpm worker:build:dev && wrangler dev",
108
150
  "worker:deploy": "pnpm worker:build && wrangler deploy",
109
151
  "test": "vitest run --environment jsdom",
110
152
  "test:run": "vitest run --environment jsdom",
153
+ "test:integration": "vitest run --config vitest.config.integration.ts",
111
154
  "typecheck": "tsc --noEmit",
155
+ "storybook": "storybook dev -p 6006 --no-open",
156
+ "storybook:build": "storybook build -o storybook-static",
157
+ "chromatic": "chromatic --build-script-name=storybook:build --exit-zero-on-changes",
158
+ "size-limit": "size-limit",
159
+ "size-limit:why": "size-limit --why",
112
160
  "version": "changeset version",
113
161
  "release": "pnpm build && changeset publish",
114
- "registry:validate": "node ./registry/scripts/validate-registry.mjs"
162
+ "registry:validate": "node ./registry/scripts/validate-registry.mjs",
163
+ "lint:tokens": "node scripts/lint-tokens.mjs",
164
+ "check:stories": "node scripts/check-stories.mjs",
165
+ "quality": "pnpm lint && pnpm typecheck && pnpm lint:tokens && pnpm test"
115
166
  }
116
167
  }
@@ -1,4 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- export function NavigationArea({ children }) {
3
- return _jsx("div", { className: "relative hidden shrink-0 md:flex", children: children });
4
- }
@@ -1,2 +0,0 @@
1
- export type { ShellCommandDefinition, ShellContentLayout, ShellHostAdapter, ShellModuleDefinition, ShellNavItem, ShellPanelDefinition, ShellRailItem, ShellRenderContext, ShellRouteContext, ShellSearchParamsAdapter, ShellStateContextValue, ShellTab, ShellViewDefinition, ShellViewportBand, } from './index.js';
2
- export { buildShellSectionCommands, buildShellShortcutMap, getDefaultShellPanelView, getResolvedShellPanelWidth, getShellActionsFromMatches, getShellContentLayoutFromMatches, getShellContentMaxWidth, getShellPanelCollection, getShellPanelView, getShellPanelViewsFromMatches, getShellTabsFromMatches, isShellPanelViewAvailable, NavigationArea, resolveShellPanelView, ShellAppRail, ShellDesktopPanelDock, ShellFrame, ShellHeaderFrame, ShellModuleNav, ShellPanelRail, ShellPanelToggle, ShellScrollableContent, ShellStateProvider, ShellTabBar, useShellState, WorkbenchLayout, } from './index.js';
@@ -1 +0,0 @@
1
- export { buildShellSectionCommands, buildShellShortcutMap, getDefaultShellPanelView, getResolvedShellPanelWidth, getShellActionsFromMatches, getShellContentLayoutFromMatches, getShellContentMaxWidth, getShellPanelCollection, getShellPanelView, getShellPanelViewsFromMatches, getShellTabsFromMatches, isShellPanelViewAvailable, NavigationArea, resolveShellPanelView, ShellAppRail, ShellDesktopPanelDock, ShellFrame, ShellHeaderFrame, ShellModuleNav, ShellPanelRail, ShellPanelToggle, ShellScrollableContent, ShellStateProvider, ShellTabBar, useShellState, WorkbenchLayout, } from './index.js';
@@ -1,18 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import type { ShellRailItem } from './types.js';
3
- interface ShellAppRailRenderArgs {
4
- children: ReactNode;
5
- className: string;
6
- isActive: boolean;
7
- item: ShellRailItem;
8
- }
9
- export interface ShellAppRailProps {
10
- mainItems: ShellRailItem[];
11
- utilityItems: ShellRailItem[];
12
- className?: string;
13
- footer?: ReactNode;
14
- isItemActive: (item: ShellRailItem) => boolean;
15
- renderLink: (args: ShellAppRailRenderArgs) => ReactNode;
16
- }
17
- export declare function ShellAppRail({ mainItems, utilityItems, className, footer, isItemActive, renderLink, }: ShellAppRailProps): import("react/jsx-runtime").JSX.Element;
18
- export {};
@@ -1,27 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { ChevronDown, ChevronUp } from 'lucide-react';
3
- import { useState } from 'react';
4
- function joinClasses(...values) {
5
- return values.filter(Boolean).join(' ');
6
- }
7
- function RailDivider({ pinned, onToggle }) {
8
- return (_jsx("div", { className: "group flex w-full items-center justify-center py-4", children: _jsxs("button", { type: "button", "aria-label": pinned ? 'Push items down' : 'Push items up', onClick: onToggle, className: "flex cursor-pointer flex-col items-center gap-0.5", children: [_jsx("span", { className: "hidden h-3.5 w-6 items-center justify-center text-[var(--app-rail-foreground)] transition-colors group-hover:flex", children: pinned ? _jsx(ChevronDown, { size: 14 }) : _jsx(ChevronUp, { size: 14 }) }), _jsx("span", { className: "block h-0.5 w-6 rounded-full bg-[var(--app-rail-foreground)]/30 transition-colors group-hover:bg-[var(--primary)]" })] }) }));
9
- }
10
- function RailSection({ items, isItemActive, renderLink, ariaLabel, className, }) {
11
- return (_jsx("nav", { "aria-label": ariaLabel, className: className, children: items.map((item) => {
12
- const isActive = isItemActive(item);
13
- const Icon = item.icon;
14
- return renderLink({
15
- item,
16
- isActive,
17
- className: joinClasses('flex w-full flex-col items-center justify-center gap-1.5 rounded-2xl px-2 py-2.5 text-center transition-colors', isActive
18
- ? 'bg-[var(--app-rail-item-active-bg)] text-[var(--foreground)]'
19
- : 'text-[var(--app-rail-foreground)] hover:bg-[var(--surface-highlight)] hover:text-[var(--foreground)]'),
20
- children: (_jsxs(_Fragment, { children: [_jsx(Icon, { size: 22 }), _jsx("span", { className: "text-[10px] font-medium leading-none", children: item.label })] })),
21
- });
22
- }) }));
23
- }
24
- export function ShellAppRail({ mainItems, utilityItems, className, footer, isItemActive, renderLink, }) {
25
- const [pinned, setPinned] = useState(false);
26
- return (_jsxs("div", { className: joinClasses('flex w-[var(--app-rail-width)] shrink-0 flex-col items-center bg-[var(--app-rail)] px-2 py-4', className), children: [_jsx(RailSection, { items: mainItems, isItemActive: isItemActive, renderLink: renderLink, ariaLabel: "Main navigation", className: "flex flex-col items-center gap-1.5" }), _jsx("div", { className: joinClasses('transition-[flex-grow] duration-300 ease-in-out', pinned ? 'grow-0' : 'grow') }), _jsx(RailDivider, { pinned: pinned, onToggle: () => setPinned(!pinned) }), _jsx(RailSection, { items: utilityItems, isItemActive: isItemActive, renderLink: renderLink, ariaLabel: "Utility navigation", className: "mb-2 flex flex-col items-center gap-1" }), _jsx("div", { className: joinClasses('transition-[flex-grow] duration-300 ease-in-out', pinned ? 'grow' : 'grow-0') }), footer] }));
27
- }
@@ -1,13 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- export declare function ShellFrame({ header, navigation, mobileSidebar, mobileHeader, tabBar, content, desktopDock, tabletPanel, mobileBottomNav, commandPalette, }: {
3
- header: ReactNode;
4
- navigation: ReactNode;
5
- mobileSidebar?: ReactNode;
6
- mobileHeader?: ReactNode;
7
- tabBar?: ReactNode;
8
- content: ReactNode;
9
- desktopDock?: ReactNode;
10
- tabletPanel?: ReactNode;
11
- mobileBottomNav?: ReactNode;
12
- commandPalette?: ReactNode;
13
- }): import("react/jsx-runtime").JSX.Element;
@@ -1,4 +0,0 @@
1
- import { jsxs as _jsxs } from "react/jsx-runtime";
2
- export function ShellFrame({ header, navigation, mobileSidebar, mobileHeader, tabBar, content, desktopDock, tabletPanel, mobileBottomNav, commandPalette, }) {
3
- return (_jsxs("div", { "data-testid": "shell-frame", className: "flex h-screen w-full flex-1 flex-col overflow-hidden bg-[var(--background)]", children: [header, _jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [navigation, mobileSidebar, _jsxs("div", { className: "flex min-w-0 flex-1 flex-col overflow-hidden", children: [mobileHeader, tabBar, _jsxs("div", { className: "relative min-h-0 flex-1 overflow-hidden", children: [content, desktopDock] })] }), tabletPanel, mobileBottomNav] }), commandPalette] }));
4
- }
@@ -1,29 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import type { ShellModuleDefinition, ShellNavItem } from './types.js';
3
- interface ShellModuleNavRenderArgs {
4
- children: ReactNode;
5
- className: string;
6
- isActive: boolean;
7
- item: ShellNavItem;
8
- }
9
- export interface ShellModuleNavProps {
10
- module: Pick<ShellModuleDefinition, 'description' | 'items' | 'label'>;
11
- ariaLabel: string;
12
- className?: string;
13
- descriptionClassName?: string;
14
- headerClassName?: string;
15
- itemClassName?: string;
16
- activeItemClassName?: string;
17
- inactiveItemClassName?: string;
18
- itemsClassName?: string;
19
- itemIconClassName?: string;
20
- itemIconSize?: number;
21
- itemLabelClassName?: string;
22
- itemDescriptionClassName?: string;
23
- itemShortcutClassName?: string;
24
- titleClassName?: string;
25
- isItemActive: (item: ShellNavItem) => boolean;
26
- renderLink: (args: ShellModuleNavRenderArgs) => ReactNode;
27
- }
28
- export declare function ShellModuleNav({ module, ariaLabel, className, descriptionClassName, headerClassName, itemClassName, activeItemClassName, inactiveItemClassName, itemsClassName, itemIconClassName, itemIconSize, itemLabelClassName, itemDescriptionClassName, itemShortcutClassName, titleClassName, isItemActive, renderLink, }: ShellModuleNavProps): import("react/jsx-runtime").JSX.Element;
29
- export {};
@@ -1,16 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- function joinClasses(...values) {
3
- return values.filter(Boolean).join(' ');
4
- }
5
- export function ShellModuleNav({ module, ariaLabel, className, descriptionClassName, headerClassName, itemClassName, activeItemClassName, inactiveItemClassName, itemsClassName, itemIconClassName, itemIconSize = 18, itemLabelClassName, itemDescriptionClassName, itemShortcutClassName, titleClassName, isItemActive, renderLink, }) {
6
- return (_jsxs("div", { className: className, children: [_jsxs("div", { className: headerClassName, children: [_jsx("div", { className: titleClassName, children: module.label }), module.description ? (_jsx("div", { className: descriptionClassName, children: module.description })) : null] }), _jsx("nav", { "aria-label": ariaLabel, className: itemsClassName, children: module.items.map((item) => {
7
- const isActive = isItemActive(item);
8
- const Icon = item.icon;
9
- return renderLink({
10
- item,
11
- isActive,
12
- className: joinClasses(itemClassName, isActive ? activeItemClassName : inactiveItemClassName),
13
- children: (_jsxs(_Fragment, { children: [_jsx(Icon, { size: itemIconSize, className: itemIconClassName }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: itemLabelClassName, children: item.label }), item.description ? (_jsx("span", { className: itemDescriptionClassName, children: item.description })) : null] }), item.shortcut ? (_jsx("kbd", { className: itemShortcutClassName, children: item.shortcut })) : null] })),
14
- });
15
- }) })] }));
16
- }
@@ -1,9 +0,0 @@
1
- import type { ShellPanelDefinition } from './types.js';
2
- export interface ShellPanelRailProps {
3
- moduleViews: ShellPanelDefinition[];
4
- globalViews: ShellPanelDefinition[];
5
- activePanelView: string | null;
6
- className?: string;
7
- onTogglePanelView: (viewId: string) => void;
8
- }
9
- export declare function ShellPanelRail({ moduleViews, globalViews, activePanelView, className, onTogglePanelView, }: ShellPanelRailProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,17 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- function joinClasses(...values) {
3
- return values.filter(Boolean).join(' ');
4
- }
5
- function PanelRailButtons({ views, activePanelView, onTogglePanelView, }) {
6
- return (_jsx("div", { className: "flex w-full flex-col items-center gap-1", children: views.map(({ id, label, icon: Icon }) => {
7
- const isActive = activePanelView === id;
8
- return (_jsxs("button", { type: "button", "aria-label": label, "aria-pressed": isActive, onClick: () => onTogglePanelView(id), className: joinClasses('flex w-full flex-col items-center gap-1 rounded-xl px-1 py-2 text-[var(--app-rail-foreground)] transition-colors', isActive
9
- ? 'bg-[var(--app-rail-item-active-bg)] text-[var(--foreground)]'
10
- : 'hover:bg-[var(--surface-highlight)] hover:text-[var(--foreground)]'), children: [_jsx(Icon, { size: 16 }), _jsx("span", { className: "text-[9px] font-medium leading-none", children: label })] }, id));
11
- }) }));
12
- }
13
- export function ShellPanelRail({ moduleViews, globalViews, activePanelView, className, onTogglePanelView, }) {
14
- if (moduleViews.length === 0 && globalViews.length === 0)
15
- return null;
16
- return (_jsxs("aside", { "aria-label": "Panel rail", className: joinClasses('flex h-full w-[var(--right-rail-width)] shrink-0 flex-col items-center bg-[var(--app-rail)] px-1.5 py-3', className), children: [_jsx(PanelRailButtons, { views: moduleViews, activePanelView: activePanelView, onTogglePanelView: onTogglePanelView }), _jsx("div", { className: "grow" }), _jsx(PanelRailButtons, { views: globalViews, activePanelView: activePanelView, onTogglePanelView: onTogglePanelView })] }));
17
- }
@@ -1,39 +0,0 @@
1
- import { type ReactNode } from 'react';
2
- export declare const DEFAULT_SIDE_PANEL_WIDTH = 360;
3
- export declare const MIN_SIDE_PANEL_WIDTH = 280;
4
- export declare const MAX_SIDE_PANEL_WIDTH = 520;
5
- export interface ShellSearchParamsAdapter {
6
- searchParams: URLSearchParams;
7
- setSearchParams: (updater: (currentSearchParams: URLSearchParams) => URLSearchParams) => void;
8
- }
9
- export interface ShellStateContextValue {
10
- activePanelView: string | null;
11
- lastPanelView: string | null;
12
- setActivePanelView: (view: string | null) => void;
13
- closePanelView: () => void;
14
- restoreLastPanelView: (fallbackView: string | null) => void;
15
- togglePanelView: (viewId: string) => void;
16
- sidePanelWidth: number;
17
- setSidePanelWidth: (width: number) => void;
18
- activeRail: string;
19
- setActiveRail: (rail: string) => void;
20
- selectedEntityId: string | null;
21
- selectEntity: (entityId: string, options?: {
22
- panelView?: string | null;
23
- }) => void;
24
- clearEntity: () => void;
25
- commandPaletteOpen: boolean;
26
- setCommandPaletteOpen: (open: boolean) => void;
27
- openCommandPalette: () => void;
28
- closeCommandPalette: () => void;
29
- }
30
- export declare function clampSidePanelWidth(width: number): number;
31
- export declare function ShellStateProvider({ children, adapter, initialActiveRail, panelQueryParam, selectionQueryParam, sidePanelWidthStorageKey, }: {
32
- children: ReactNode;
33
- adapter: ShellSearchParamsAdapter;
34
- initialActiveRail?: string;
35
- panelQueryParam?: string;
36
- selectionQueryParam?: string;
37
- sidePanelWidthStorageKey?: string;
38
- }): import("react/jsx-runtime").JSX.Element;
39
- export declare function useShellState(): ShellStateContextValue;
@@ -1,135 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { createContext, useContext, useEffect, useMemo, useRef, useState, } from 'react';
3
- export const DEFAULT_SIDE_PANEL_WIDTH = 360;
4
- export const MIN_SIDE_PANEL_WIDTH = 280;
5
- export const MAX_SIDE_PANEL_WIDTH = 520;
6
- const ShellStateContext = createContext(null);
7
- export function clampSidePanelWidth(width) {
8
- return Math.min(MAX_SIDE_PANEL_WIDTH, Math.max(MIN_SIDE_PANEL_WIDTH, width));
9
- }
10
- function readStoredSidePanelWidth(storageKey) {
11
- if (typeof window === 'undefined')
12
- return DEFAULT_SIDE_PANEL_WIDTH;
13
- const storedValue = typeof window.localStorage?.getItem === 'function'
14
- ? window.localStorage.getItem(storageKey)
15
- : null;
16
- const stored = Number(storedValue);
17
- return Number.isFinite(stored) && stored > 0
18
- ? clampSidePanelWidth(stored)
19
- : DEFAULT_SIDE_PANEL_WIDTH;
20
- }
21
- export function ShellStateProvider({ children, adapter, initialActiveRail = 'home', panelQueryParam = 'panel', selectionQueryParam = 'selection', sidePanelWidthStorageKey = 'meda.side-panel.width', }) {
22
- const { searchParams, setSearchParams } = adapter;
23
- const [sidePanelWidthState, setSidePanelWidthState] = useState(() => readStoredSidePanelWidth(sidePanelWidthStorageKey));
24
- const [activeRail, setActiveRail] = useState(initialActiveRail);
25
- const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
26
- const lastPanelViewRef = useRef(null);
27
- const persistTimerRef = useRef(undefined);
28
- const activePanelView = searchParams.get(panelQueryParam);
29
- const selectedEntityId = searchParams.get(selectionQueryParam);
30
- useEffect(() => {
31
- if (activePanelView) {
32
- lastPanelViewRef.current = activePanelView;
33
- }
34
- }, [activePanelView]);
35
- useEffect(() => {
36
- return () => {
37
- clearTimeout(persistTimerRef.current);
38
- };
39
- }, []);
40
- function updateSearchParams(mutate) {
41
- setSearchParams((currentSearchParams) => {
42
- const nextSearchParams = new URLSearchParams(currentSearchParams);
43
- mutate(nextSearchParams);
44
- return nextSearchParams;
45
- });
46
- }
47
- function setActivePanelView(view) {
48
- if (view) {
49
- lastPanelViewRef.current = view;
50
- }
51
- updateSearchParams((nextSearchParams) => {
52
- if (view) {
53
- nextSearchParams.set(panelQueryParam, view);
54
- }
55
- else {
56
- nextSearchParams.delete(panelQueryParam);
57
- }
58
- });
59
- }
60
- function closePanelView() {
61
- updateSearchParams((nextSearchParams) => {
62
- nextSearchParams.delete(panelQueryParam);
63
- });
64
- }
65
- function restoreLastPanelView(fallbackView) {
66
- const nextView = lastPanelViewRef.current ?? fallbackView;
67
- if (!nextView)
68
- return;
69
- setActivePanelView(nextView);
70
- }
71
- function setSidePanelWidth(width) {
72
- const nextWidth = clampSidePanelWidth(width);
73
- setSidePanelWidthState(nextWidth);
74
- clearTimeout(persistTimerRef.current);
75
- persistTimerRef.current = setTimeout(() => {
76
- if (typeof window !== 'undefined' && typeof window.localStorage?.setItem === 'function') {
77
- window.localStorage.setItem(sidePanelWidthStorageKey, String(nextWidth));
78
- }
79
- }, 300);
80
- }
81
- function togglePanelView(viewId) {
82
- if (activePanelView === viewId) {
83
- closePanelView();
84
- return;
85
- }
86
- setActivePanelView(viewId);
87
- }
88
- function selectEntity(entityId, options) {
89
- updateSearchParams((nextSearchParams) => {
90
- nextSearchParams.set(selectionQueryParam, entityId);
91
- if (options?.panelView) {
92
- nextSearchParams.set(panelQueryParam, options.panelView);
93
- }
94
- });
95
- }
96
- function clearEntity() {
97
- updateSearchParams((nextSearchParams) => {
98
- nextSearchParams.delete(selectionQueryParam);
99
- });
100
- }
101
- function openCommandPalette() {
102
- setCommandPaletteOpen(true);
103
- }
104
- function closeCommandPalette() {
105
- setCommandPaletteOpen(false);
106
- }
107
- // biome-ignore lint/correctness/useExhaustiveDependencies: inner setters/handlers are re-created each render; wrapping all of them in useCallback is a separate refactor — deps here are the observable state values only.
108
- const value = useMemo(() => ({
109
- activePanelView,
110
- lastPanelView: lastPanelViewRef.current,
111
- setActivePanelView,
112
- closePanelView,
113
- restoreLastPanelView,
114
- togglePanelView,
115
- sidePanelWidth: sidePanelWidthState,
116
- setSidePanelWidth,
117
- activeRail,
118
- setActiveRail,
119
- selectedEntityId,
120
- selectEntity,
121
- clearEntity,
122
- commandPaletteOpen,
123
- setCommandPaletteOpen,
124
- openCommandPalette,
125
- closeCommandPalette,
126
- }), [activePanelView, activeRail, commandPaletteOpen, selectedEntityId, sidePanelWidthState]);
127
- return _jsx(ShellStateContext.Provider, { value: value, children: children });
128
- }
129
- export function useShellState() {
130
- const context = useContext(ShellStateContext);
131
- if (!context) {
132
- throw new Error('useShellState must be used within a ShellStateProvider');
133
- }
134
- return context;
135
- }