@donotdev/ui 0.0.6 → 0.0.7
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/dist/dndev.css +70 -27
- package/dist/index.js +4 -4
- package/dist/internal/layout/components/PerformanceHints.d.ts +64 -0
- package/dist/internal/layout/components/PerformanceHints.d.ts.map +1 -0
- package/dist/internal/layout/components/PerformanceHints.js +87 -0
- package/dist/internal/layout/components/footer/FooterBranding.js +1 -1
- package/dist/providers/NextJsAppProviders.d.ts.map +1 -1
- package/dist/providers/NextJsAppProviders.js +2 -1
- package/dist/providers/ViteAppProviders.d.ts.map +1 -1
- package/dist/providers/ViteAppProviders.js +4 -1
- package/dist/routing/useGoTo.d.ts +3 -4
- package/dist/routing/useGoTo.d.ts.map +1 -1
- package/dist/routing/useGoTo.js +16 -23
- package/dist/styles/index.css +70 -27
- package/package.json +10 -11
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
interface FontPreload {
|
|
3
|
+
/** Path to font file (e.g., '/fonts/Inter-latin.woff2') */
|
|
4
|
+
href: string;
|
|
5
|
+
/** Font MIME type @default 'font/woff2' */
|
|
6
|
+
type?: string;
|
|
7
|
+
/** Cross-origin setting @default 'anonymous' */
|
|
8
|
+
crossOrigin?: 'anonymous' | 'use-credentials';
|
|
9
|
+
}
|
|
10
|
+
interface PerformanceHintsProps {
|
|
11
|
+
/**
|
|
12
|
+
* Additional origins to preconnect to
|
|
13
|
+
* These are merged with defaults
|
|
14
|
+
*/
|
|
15
|
+
preconnects?: string[];
|
|
16
|
+
/**
|
|
17
|
+
* Additional origins to dns-prefetch
|
|
18
|
+
* These are merged with defaults
|
|
19
|
+
*/
|
|
20
|
+
dnsPrefetch?: string[];
|
|
21
|
+
/**
|
|
22
|
+
* Fonts to preload for faster LCP
|
|
23
|
+
* Pass string[] for simple paths, or FontPreload[] for full control
|
|
24
|
+
* @default ['/fonts/Inter-latin.woff2']
|
|
25
|
+
*/
|
|
26
|
+
fontPreloads?: (string | FontPreload)[];
|
|
27
|
+
/**
|
|
28
|
+
* Disable default preconnects (use only custom ones)
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
disableDefaults?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Disable default font preloads
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
disableFontPreloads?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* PerformanceHints component
|
|
40
|
+
*
|
|
41
|
+
* Adds preconnect, dns-prefetch, and font preload hints to improve page load.
|
|
42
|
+
* Automatically includes common third-party origins and critical fonts.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* // Use defaults (preloads Inter-latin.woff2)
|
|
47
|
+
* <PerformanceHints />
|
|
48
|
+
*
|
|
49
|
+
* // Custom font preloads
|
|
50
|
+
* <PerformanceHints fontPreloads={['/fonts/Roboto-400-latin.woff2']} />
|
|
51
|
+
*
|
|
52
|
+
* // Full control over font preload
|
|
53
|
+
* <PerformanceHints
|
|
54
|
+
* fontPreloads={[{ href: '/fonts/Custom.woff2', type: 'font/woff2' }]}
|
|
55
|
+
* />
|
|
56
|
+
*
|
|
57
|
+
* // Disable font preloads (e.g., when using next/font)
|
|
58
|
+
* <PerformanceHints disableFontPreloads />
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare const PerformanceHints: ComponentType<PerformanceHintsProps>;
|
|
62
|
+
export default PerformanceHints;
|
|
63
|
+
export type { FontPreload, PerformanceHintsProps };
|
|
64
|
+
//# sourceMappingURL=PerformanceHints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PerformanceHints.d.ts","sourceRoot":"","sources":["../../../../src/internal/layout/components/PerformanceHints.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAkC3C,UAAU,WAAW;IACnB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,CAAC;CAC/C;AAED,UAAU,qBAAqB;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC;IACxC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,QAAA,MAAM,gBAAgB,EAAE,aAAa,CAAC,qBAAqB,CA6D1D,CAAC;AAEF,eAAe,gBAAgB,CAAC;AAChC,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
// packages/ui/src/internal/layout/components/PerformanceHints.tsx
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview Performance hints for faster page loads
|
|
6
|
+
* @description Adds preconnect, dns-prefetch, and font preload hints
|
|
7
|
+
*
|
|
8
|
+
* - Preconnect establishes early connections (DNS + TCP + TLS) to origins
|
|
9
|
+
* the page will need, saving 100-300ms per origin on slow networks.
|
|
10
|
+
* - Font preload initiates font downloads early, reducing LCP by ~500-1000ms
|
|
11
|
+
*
|
|
12
|
+
* @version 0.0.2
|
|
13
|
+
* @since 0.0.1
|
|
14
|
+
* @author AMBROISE PARK Consulting
|
|
15
|
+
*/
|
|
16
|
+
import { Helmet } from 'react-helmet-async';
|
|
17
|
+
/**
|
|
18
|
+
* Common third-party origins that benefit from preconnect
|
|
19
|
+
* Only includes origins that are commonly used and safe to preconnect
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_PRECONNECTS = [
|
|
22
|
+
// YouTube embeds - thumbnails and iframe
|
|
23
|
+
'https://img.youtube.com',
|
|
24
|
+
'https://www.youtube.com',
|
|
25
|
+
// Google Fonts (if used)
|
|
26
|
+
'https://fonts.googleapis.com',
|
|
27
|
+
'https://fonts.gstatic.com',
|
|
28
|
+
// Google APIs (Firebase, Analytics, etc.)
|
|
29
|
+
'https://www.googleapis.com',
|
|
30
|
+
// Stripe (if used)
|
|
31
|
+
'https://js.stripe.com',
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Origins to dns-prefetch only (less aggressive than preconnect)
|
|
35
|
+
* Use for origins that might be needed but aren't certain
|
|
36
|
+
*/
|
|
37
|
+
const DEFAULT_DNS_PREFETCH = [
|
|
38
|
+
'https://www.google-analytics.com',
|
|
39
|
+
'https://www.googletagmanager.com',
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Default critical fonts to preload
|
|
43
|
+
* Inter-latin is the primary UI font used by the framework
|
|
44
|
+
*/
|
|
45
|
+
const DEFAULT_FONT_PRELOADS = ['/fonts/Inter-latin.woff2'];
|
|
46
|
+
/**
|
|
47
|
+
* PerformanceHints component
|
|
48
|
+
*
|
|
49
|
+
* Adds preconnect, dns-prefetch, and font preload hints to improve page load.
|
|
50
|
+
* Automatically includes common third-party origins and critical fonts.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* // Use defaults (preloads Inter-latin.woff2)
|
|
55
|
+
* <PerformanceHints />
|
|
56
|
+
*
|
|
57
|
+
* // Custom font preloads
|
|
58
|
+
* <PerformanceHints fontPreloads={['/fonts/Roboto-400-latin.woff2']} />
|
|
59
|
+
*
|
|
60
|
+
* // Full control over font preload
|
|
61
|
+
* <PerformanceHints
|
|
62
|
+
* fontPreloads={[{ href: '/fonts/Custom.woff2', type: 'font/woff2' }]}
|
|
63
|
+
* />
|
|
64
|
+
*
|
|
65
|
+
* // Disable font preloads (e.g., when using next/font)
|
|
66
|
+
* <PerformanceHints disableFontPreloads />
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
const PerformanceHints = ({ preconnects = [], dnsPrefetch = [], fontPreloads = [], disableDefaults = false, disableFontPreloads = false, }) => {
|
|
70
|
+
// Merge defaults with custom origins
|
|
71
|
+
const allPreconnects = disableDefaults
|
|
72
|
+
? preconnects
|
|
73
|
+
: [...new Set([...DEFAULT_PRECONNECTS, ...preconnects])];
|
|
74
|
+
const allDnsPrefetch = disableDefaults
|
|
75
|
+
? dnsPrefetch
|
|
76
|
+
: [...new Set([...DEFAULT_DNS_PREFETCH, ...dnsPrefetch])];
|
|
77
|
+
// Normalize font preloads to FontPreload objects
|
|
78
|
+
const normalizeFontPreload = (font) => typeof font === 'string' ? { href: font } : font;
|
|
79
|
+
const customFontPaths = fontPreloads.map((f) => typeof f === 'string' ? f : f.href);
|
|
80
|
+
const allFontPreloads = disableFontPreloads
|
|
81
|
+
? []
|
|
82
|
+
: fontPreloads.length > 0
|
|
83
|
+
? fontPreloads.map(normalizeFontPreload)
|
|
84
|
+
: DEFAULT_FONT_PRELOADS.filter((f) => !customFontPaths.includes(f)).map(normalizeFontPreload);
|
|
85
|
+
return (_jsxs(Helmet, { children: [allFontPreloads.map((font) => (_jsx("link", { rel: "preload", as: "font", href: font.href, type: font.type || 'font/woff2', crossOrigin: font.crossOrigin || 'anonymous' }, `font-${font.href}`))), allPreconnects.map((origin) => (_jsx("link", { rel: "preconnect", href: origin, crossOrigin: "anonymous" }, `preconnect-${origin}`))), allDnsPrefetch.map((origin) => (_jsx("link", { rel: "dns-prefetch", href: origin }, `dns-${origin}`)))] }));
|
|
86
|
+
};
|
|
87
|
+
export default PerformanceHints;
|
|
@@ -20,7 +20,7 @@ import { useAppConfig } from '@donotdev/core';
|
|
|
20
20
|
* @since 0.0.1
|
|
21
21
|
* @author AMBROISE PARK Consulting
|
|
22
22
|
*/
|
|
23
|
-
export const FooterBranding = ({ className, size = '
|
|
23
|
+
export const FooterBranding = ({ className, size = 'sm', }) => {
|
|
24
24
|
const appUrl = useAppConfig('url');
|
|
25
25
|
const isFrameworkSite = appUrl === 'https://donotdev.com';
|
|
26
26
|
const sizeStyles = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NextJsAppProviders.d.ts","sourceRoot":"","sources":["../../src/providers/NextJsAppProviders.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"NextJsAppProviders.d.ts","sourceRoot":"","sources":["../../src/providers/NextJsAppProviders.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAkDxD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,2CA4ChE"}
|
|
@@ -15,6 +15,7 @@ import { QueryProviders, AppConfigProvider } from '@donotdev/core';
|
|
|
15
15
|
import { useConsent } from '@donotdev/core';
|
|
16
16
|
import { NextJsStoresInitializer } from '../internal/initializers/NextJsStoresInitializer';
|
|
17
17
|
import FaviconHead from '../internal/layout/components/FaviconHead';
|
|
18
|
+
import PerformanceHints from '../internal/layout/components/PerformanceHints';
|
|
18
19
|
// import NextJsAutoMetaTags from '../internal/layout/components/NextJsAutoMetaTags'; // Deprecated - metadata now server-side
|
|
19
20
|
import { DnDevLayout } from '../internal/layout/DnDevLayout';
|
|
20
21
|
import { SentryInitializer } from '../internal/providers/SentryInitializer';
|
|
@@ -45,5 +46,5 @@ function ConsentBanner() {
|
|
|
45
46
|
}
|
|
46
47
|
export function NextJsAppProviders(props) {
|
|
47
48
|
const { config = {}, layout, children, serverCookies, customStores } = props;
|
|
48
|
-
return (_jsxs(AppConfigProvider, { config: config, platform: "nextjs", children: [_jsx(SentryInitializer, {}), _jsx(HelmetProvider, { children: _jsxs(NextJsStoresInitializer, { serverCookies: serverCookies, customStores: customStores, children: [_jsx(FaviconHead, {}), _jsx(QueryProviders, { children: _jsxs(UIProviders, { children: [_jsx(DnDevLayout, { layout: layout, children: children }), _jsx(ConsentBanner, {}), _jsx(Suspense, { fallback: null, children: _jsx(PWAUpdateNotification, {}) }), _jsx(Suspense, { fallback: null, children: _jsx(LicenseWatermark, {}) })] }) })] }) })] }));
|
|
49
|
+
return (_jsxs(AppConfigProvider, { config: config, platform: "nextjs", children: [_jsx(SentryInitializer, {}), _jsx(HelmetProvider, { children: _jsxs(NextJsStoresInitializer, { serverCookies: serverCookies, customStores: customStores, children: [_jsx(FaviconHead, {}), _jsx(PerformanceHints, {}), _jsx(QueryProviders, { children: _jsxs(UIProviders, { children: [_jsx(DnDevLayout, { layout: layout, children: children }), _jsx(ConsentBanner, {}), _jsx(Suspense, { fallback: null, children: _jsx(PWAUpdateNotification, {}) }), _jsx(Suspense, { fallback: null, children: _jsx(LicenseWatermark, {}) })] }) })] }) })] }));
|
|
49
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViteAppProviders.d.ts","sourceRoot":"","sources":["../../src/providers/ViteAppProviders.tsx"],"names":[],"mappings":"AAcA,OAAO,aAAa,CAAC;AACrB,OAAO,gBAAgB,CAAC;AACxB,OAAO,sBAAsB,CAAC;AAC9B,OAAO,gBAAgB,CAAC;AACxB,OAAO,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"ViteAppProviders.d.ts","sourceRoot":"","sources":["../../src/providers/ViteAppProviders.tsx"],"names":[],"mappings":"AAcA,OAAO,aAAa,CAAC;AACrB,OAAO,gBAAgB,CAAC;AACxB,OAAO,sBAAsB,CAAC;AAC9B,OAAO,gBAAgB,CAAC;AACxB,OAAO,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AA2BxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,2CAkCxD"}
|
|
@@ -30,6 +30,9 @@ const HelmetProvider = lazy(() => import('react-helmet-async').then((m) => ({ de
|
|
|
30
30
|
const FaviconHead = lazy(() => import('../internal/layout/components/FaviconHead').then((m) => ({
|
|
31
31
|
default: m.default,
|
|
32
32
|
})));
|
|
33
|
+
const PerformanceHints = lazy(() => import('../internal/layout/components/PerformanceHints').then((m) => ({
|
|
34
|
+
default: m.default,
|
|
35
|
+
})));
|
|
33
36
|
/**
|
|
34
37
|
* ViteAppProviders - Vite platform-specific app providers
|
|
35
38
|
*
|
|
@@ -77,5 +80,5 @@ export function ViteAppProviders(props) {
|
|
|
77
80
|
routeGroups,
|
|
78
81
|
layout,
|
|
79
82
|
});
|
|
80
|
-
return (_jsxs(AppConfigProvider, { config: config, platform: "vite", children: [_jsx(SentryInitializer, {}), _jsx(ViteStoresInitializer, { customStores: customStores, children: _jsx(Suspense, { fallback: null, children: _jsxs(HelmetProvider, { children: [_jsx(FaviconHead, {}), _jsx(NavigationProvider, { router: router })] }) }) })] }));
|
|
83
|
+
return (_jsxs(AppConfigProvider, { config: config, platform: "vite", children: [_jsx(SentryInitializer, {}), _jsx(ViteStoresInitializer, { customStores: customStores, children: _jsx(Suspense, { fallback: null, children: _jsxs(HelmetProvider, { children: [_jsx(FaviconHead, {}), _jsx(PerformanceHints, {}), _jsx(NavigationProvider, { router: router })] }) }) })] }));
|
|
81
84
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { type NavigationItem } from './useNavigation';
|
|
2
1
|
/**
|
|
3
2
|
* Hook for navigation with favorites and recents
|
|
4
3
|
* Command component handles search/filtering
|
|
5
4
|
*/
|
|
6
5
|
export declare const useGoTo: () => {
|
|
7
|
-
navigationItems: NavigationItem[];
|
|
8
|
-
favoriteItems: NavigationItem[];
|
|
9
|
-
recentItems: NavigationItem[];
|
|
6
|
+
navigationItems: import("./useNavigation").NavigationItem[];
|
|
7
|
+
favoriteItems: import("./useNavigation").NavigationItem[];
|
|
8
|
+
recentItems: import("./useNavigation").NavigationItem[];
|
|
10
9
|
toggleFavorite: (path: string) => void;
|
|
11
10
|
isFavorite: (path: string) => boolean;
|
|
12
11
|
navigateToItem: (path: string) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGoTo.d.ts","sourceRoot":"","sources":["../../src/routing/useGoTo.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useGoTo.d.ts","sourceRoot":"","sources":["../../src/routing/useGoTo.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,eAAO,MAAM,OAAO;;;;2BA4BT,MAAM;uBAQN,MAAM;2BAcN,MAAM;CAehB,CAAC"}
|
package/dist/routing/useGoTo.js
CHANGED
|
@@ -3,19 +3,15 @@
|
|
|
3
3
|
* @fileoverview useGoTo Hook - Navigation search logic
|
|
4
4
|
* @description Clean hook for Cmd+K navigation with favorites, recents, and filtering
|
|
5
5
|
*
|
|
6
|
-
* @version 0.0.
|
|
6
|
+
* @version 0.0.2
|
|
7
7
|
* @since 0.0.1
|
|
8
8
|
* @author AMBROISE PARK Consulting
|
|
9
9
|
*/
|
|
10
10
|
import { useCallback, useMemo } from 'react';
|
|
11
|
-
import {
|
|
11
|
+
import { useNavigationStore } from '@donotdev/core';
|
|
12
12
|
// Platform-specific hooks via conditional exports
|
|
13
13
|
import { useNavigate } from '@donotdev/ui/routing/hooks';
|
|
14
14
|
import { useNavigationItems } from './useNavigation';
|
|
15
|
-
const FAVORITES_KEY = 'nav_favorites';
|
|
16
|
-
const RECENT_KEY = 'nav_recent';
|
|
17
|
-
const MAX_RECENT = 8;
|
|
18
|
-
const EMPTY_ARRAY = [];
|
|
19
15
|
/**
|
|
20
16
|
* Hook for navigation with favorites and recents
|
|
21
17
|
* Command component handles search/filtering
|
|
@@ -23,15 +19,12 @@ const EMPTY_ARRAY = [];
|
|
|
23
19
|
export const useGoTo = () => {
|
|
24
20
|
const navigate = useNavigate();
|
|
25
21
|
const navigationItems = useNavigationItems() ?? [];
|
|
26
|
-
//
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
defaultValue: EMPTY_ARRAY,
|
|
33
|
-
syncAcrossTabs: false,
|
|
34
|
-
});
|
|
22
|
+
// Navigation store (favorites and recent persisted per computer)
|
|
23
|
+
const favorites = useNavigationStore((state) => state.favorites);
|
|
24
|
+
const recent = useNavigationStore((state) => state.recent);
|
|
25
|
+
const toggleFavoriteStore = useNavigationStore((state) => state.toggleFavorite);
|
|
26
|
+
const isFavoriteStore = useNavigationStore((state) => state.isFavorite);
|
|
27
|
+
const addRecentStore = useNavigationStore((state) => state.addRecent);
|
|
35
28
|
// Get favorite items
|
|
36
29
|
const favoriteItems = useMemo(() => {
|
|
37
30
|
if (!navigationItems.length)
|
|
@@ -45,16 +38,16 @@ export const useGoTo = () => {
|
|
|
45
38
|
const items = navigationItems.filter((item) => recent.includes(item.path));
|
|
46
39
|
return items.slice(0, 5);
|
|
47
40
|
}, [recent, navigationItems]);
|
|
48
|
-
// Toggle favorite
|
|
41
|
+
// Toggle favorite (delegates to navigation store)
|
|
49
42
|
const toggleFavorite = useCallback((path) => {
|
|
50
|
-
|
|
51
|
-
}, [
|
|
52
|
-
// Check if item is favorite
|
|
53
|
-
const isFavorite = useCallback((path) =>
|
|
54
|
-
// Add to recent
|
|
43
|
+
toggleFavoriteStore(path);
|
|
44
|
+
}, [toggleFavoriteStore]);
|
|
45
|
+
// Check if item is favorite (delegates to navigation store)
|
|
46
|
+
const isFavorite = useCallback((path) => isFavoriteStore(path), [isFavoriteStore]);
|
|
47
|
+
// Add to recent (delegates to navigation store)
|
|
55
48
|
const addRecent = useCallback((path) => {
|
|
56
|
-
|
|
57
|
-
}, [
|
|
49
|
+
addRecentStore(path);
|
|
50
|
+
}, [addRecentStore]);
|
|
58
51
|
// Navigate to item
|
|
59
52
|
const navigateToItem = useCallback((path) => {
|
|
60
53
|
addRecent(path);
|
package/dist/styles/index.css
CHANGED
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
Used by: CSS @media queries, JavaScript hooks, responsive components
|
|
19
19
|
Note: CSS variables don't work in @media, so we use hardcoded px values
|
|
20
20
|
=========================== */
|
|
21
|
-
--breakpoint-mobile: 768px;
|
|
22
|
-
--breakpoint-tablet: 1024px;
|
|
23
|
-
--breakpoint-laptop: 1440px;
|
|
21
|
+
--breakpoint-mobile: 768px; /* < 768 = mobile */
|
|
22
|
+
--breakpoint-tablet: 1024px; /* 768-1023 = tablet */
|
|
23
|
+
--breakpoint-laptop: 1440px; /* 1024-1439 = laptop (key layout split) */
|
|
24
24
|
--breakpoint-desktop: 1920px; /* 1440+ = desktop */
|
|
25
25
|
|
|
26
26
|
/* ===========================
|
|
@@ -287,23 +287,39 @@
|
|
|
287
287
|
|
|
288
288
|
[data-density='compact'] {
|
|
289
289
|
/* Base sizes: Fluid responsiveness for all text, zoom-safe */
|
|
290
|
-
--font-size-base: clamp(
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
--font-size-base: clamp(
|
|
291
|
+
0.875rem,
|
|
292
|
+
0.8125rem + 0.25vw,
|
|
293
|
+
0.9375rem
|
|
294
|
+
); /* 14-15px fluid */
|
|
295
|
+
--font-size-lg: clamp(
|
|
296
|
+
1.05rem,
|
|
297
|
+
0.9375rem + 0.5vw,
|
|
298
|
+
1.125rem
|
|
299
|
+
); /* 17-18px fluid */
|
|
300
|
+
--font-size-xl: clamp(
|
|
301
|
+
1.26rem,
|
|
302
|
+
1.125rem + 0.75vw,
|
|
303
|
+
1.375rem
|
|
304
|
+
); /* 20-22px fluid */
|
|
293
305
|
/* Display sizes: Musical scale + fluid responsiveness */
|
|
294
306
|
--font-size-2xl: clamp(
|
|
295
307
|
1.512rem,
|
|
296
308
|
1.375rem + 0.75vw,
|
|
297
309
|
1.625rem
|
|
298
310
|
); /* 24-26px fluid */
|
|
299
|
-
--font-size-3xl: clamp(
|
|
300
|
-
1.814rem,
|
|
301
|
-
1.625rem + 1vw,
|
|
302
|
-
2rem
|
|
303
|
-
); /* 29-32px fluid */
|
|
311
|
+
--font-size-3xl: clamp(1.814rem, 1.625rem + 1vw, 2rem); /* 29-32px fluid */
|
|
304
312
|
--gap-sm: clamp(0.375rem, 0.3125rem + 0.25vw, 0.5rem); /* 6-8px fluid */
|
|
305
|
-
--gap-md: clamp(
|
|
306
|
-
|
|
313
|
+
--gap-md: clamp(
|
|
314
|
+
0.75rem,
|
|
315
|
+
0.625rem + 0.5vw,
|
|
316
|
+
0.875rem
|
|
317
|
+
); /* 12-14px fluid, zoom-safe */
|
|
318
|
+
--gap-lg: clamp(
|
|
319
|
+
1.5rem,
|
|
320
|
+
1.25rem + 1vw,
|
|
321
|
+
1.75rem
|
|
322
|
+
); /* 24-28px fluid, zoom-safe */
|
|
307
323
|
--line-height: 1.2; /* Minor Third - All text */
|
|
308
324
|
}
|
|
309
325
|
|
|
@@ -311,22 +327,34 @@
|
|
|
311
327
|
|
|
312
328
|
[data-density='standard'] {
|
|
313
329
|
/* Base sizes: Fluid responsiveness for all text, zoom-safe */
|
|
314
|
-
--font-size-base: clamp(
|
|
315
|
-
|
|
316
|
-
|
|
330
|
+
--font-size-base: clamp(
|
|
331
|
+
1rem,
|
|
332
|
+
0.9375rem + 0.25vw,
|
|
333
|
+
1.0625rem
|
|
334
|
+
); /* 16-17px fluid */
|
|
335
|
+
--font-size-lg: clamp(
|
|
336
|
+
1.25rem,
|
|
337
|
+
1.125rem + 0.5vw,
|
|
338
|
+
1.375rem
|
|
339
|
+
); /* 20-22px fluid */
|
|
340
|
+
--font-size-xl: clamp(
|
|
341
|
+
1.563rem,
|
|
342
|
+
1.375rem + 0.75vw,
|
|
343
|
+
1.75rem
|
|
344
|
+
); /* 25-28px fluid */
|
|
317
345
|
/* Display sizes: Musical scale + fluid responsiveness */
|
|
318
|
-
--font-size-2xl: clamp(
|
|
319
|
-
1.953rem,
|
|
320
|
-
1.75rem + 1vw,
|
|
321
|
-
2.25rem
|
|
322
|
-
); /* 31-36px fluid */
|
|
346
|
+
--font-size-2xl: clamp(1.953rem, 1.75rem + 1vw, 2.25rem); /* 31-36px fluid */
|
|
323
347
|
--font-size-3xl: clamp(
|
|
324
348
|
2.441rem,
|
|
325
349
|
2rem + 1.5vw,
|
|
326
350
|
3rem
|
|
327
351
|
); /* 39-48px fluid - hero text */
|
|
328
352
|
--gap-sm: clamp(0.5rem, 0.4375rem + 0.25vw, 0.625rem); /* 8-10px fluid */
|
|
329
|
-
--gap-md: clamp(
|
|
353
|
+
--gap-md: clamp(
|
|
354
|
+
1rem,
|
|
355
|
+
0.875rem + 0.5vw,
|
|
356
|
+
1.25rem
|
|
357
|
+
); /* 16-20px fluid, zoom-safe */
|
|
330
358
|
--gap-lg: clamp(2rem, 1.75rem + 1vw, 2.5rem); /* 32-40px fluid, zoom-safe */
|
|
331
359
|
--line-height: 1.25; /* Major Third - All text */
|
|
332
360
|
}
|
|
@@ -581,6 +609,7 @@ h6 {
|
|
|
581
609
|
line-height: var(--line-height);
|
|
582
610
|
color: var(--foreground);
|
|
583
611
|
background: transparent;
|
|
612
|
+
text-wrap: balance; /* Equalize line lengths, prevent orphans */
|
|
584
613
|
}
|
|
585
614
|
|
|
586
615
|
h1 {
|
|
@@ -618,6 +647,7 @@ p {
|
|
|
618
647
|
line-height: var(--line-height);
|
|
619
648
|
color: var(--foreground);
|
|
620
649
|
background: transparent;
|
|
650
|
+
text-wrap: pretty; /* Optimize last line, avoid orphans */
|
|
621
651
|
}
|
|
622
652
|
|
|
623
653
|
ul,
|
|
@@ -627,6 +657,7 @@ ol {
|
|
|
627
657
|
color: var(--foreground);
|
|
628
658
|
background: transparent;
|
|
629
659
|
list-style-position: outside;
|
|
660
|
+
text-wrap: pretty;
|
|
630
661
|
}
|
|
631
662
|
|
|
632
663
|
ul {
|
|
@@ -1070,8 +1101,10 @@ em {
|
|
|
1070
1101
|
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
|
1071
1102
|
border: var(--border-hairline) solid
|
|
1072
1103
|
color-mix(in oklab, var(--card-foreground) 25%, transparent);
|
|
1073
|
-
box-shadow:
|
|
1074
|
-
|
|
1104
|
+
box-shadow:
|
|
1105
|
+
var(--shadow-lg),
|
|
1106
|
+
inset 0 1px 0 0
|
|
1107
|
+
color-mix(in oklab, var(--card-foreground) 10%, transparent);
|
|
1075
1108
|
}
|
|
1076
1109
|
|
|
1077
1110
|
/* Separator style */
|
|
@@ -8312,14 +8345,20 @@ main[role='main'][data-routing-animation='none'] {
|
|
|
8312
8345
|
'header header'
|
|
8313
8346
|
'sidebar main'
|
|
8314
8347
|
'footer footer';
|
|
8315
|
-
grid-template-rows: var(--header-height) 1fr minmax(
|
|
8348
|
+
grid-template-rows: var(--header-height) 1fr minmax(
|
|
8349
|
+
var(--footer-height),
|
|
8350
|
+
auto
|
|
8351
|
+
);
|
|
8316
8352
|
grid-template-columns: var(--sidebar-width) 1fr;
|
|
8317
8353
|
|
|
8318
8354
|
/* Game layout: Grid rows adjust based on breakpoint (footer hidden on tablet/mobile) */
|
|
8319
8355
|
}
|
|
8320
8356
|
|
|
8321
8357
|
[data-layout='game']:root .dndev-layout {
|
|
8322
|
-
grid-template-rows: var(--header-height) 1fr minmax(
|
|
8358
|
+
grid-template-rows: var(--header-height) 1fr minmax(
|
|
8359
|
+
var(--footer-height),
|
|
8360
|
+
auto
|
|
8361
|
+
);
|
|
8323
8362
|
}
|
|
8324
8363
|
|
|
8325
8364
|
/* ===========================
|
|
@@ -8575,10 +8614,14 @@ aside[role='navigation'].sidebar[data-collapsed='true'] .dndev-interactive kbd {
|
|
|
8575
8614
|
|
|
8576
8615
|
/* Force sidebar children to shrink - no blocking resize */
|
|
8577
8616
|
|
|
8578
|
-
aside.sidebar[role='navigation'] .sidebar-
|
|
8617
|
+
aside.sidebar[role='navigation'] .sidebar-top,aside.sidebar[role='navigation'] .sidebar-bottom {
|
|
8579
8618
|
overflow: hidden;
|
|
8580
8619
|
}
|
|
8581
8620
|
|
|
8621
|
+
aside.sidebar[role='navigation'] .sidebar-content {
|
|
8622
|
+
overflow-x: hidden; /* Prevent horizontal scroll, allow vertical */
|
|
8623
|
+
}
|
|
8624
|
+
|
|
8582
8625
|
aside.sidebar[role='navigation'] .dndev-interactive {
|
|
8583
8626
|
overflow: hidden;
|
|
8584
8627
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@donotdev/ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -48,19 +48,18 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@hookform/resolvers": "^5.2.2",
|
|
51
|
-
"marked": "^17.0.1",
|
|
52
51
|
"react-helmet-async": "^2.0.5",
|
|
53
|
-
"react-hook-form": "^7.
|
|
52
|
+
"react-hook-form": "^7.71.0"
|
|
54
53
|
},
|
|
55
54
|
"peerDependencies": {
|
|
56
|
-
"@donotdev/adv-comps": "0.0.
|
|
57
|
-
"@donotdev/auth": "0.0.
|
|
58
|
-
"@donotdev/billing": "0.0.
|
|
59
|
-
"@donotdev/components": "0.0.
|
|
60
|
-
"@donotdev/core": "0.0.
|
|
61
|
-
"@donotdev/crud": "0.0.
|
|
62
|
-
"@donotdev/firebase": "0.0.
|
|
63
|
-
"@donotdev/oauth": "0.0.
|
|
55
|
+
"@donotdev/adv-comps": "0.0.7",
|
|
56
|
+
"@donotdev/auth": "0.0.7",
|
|
57
|
+
"@donotdev/billing": "0.0.7",
|
|
58
|
+
"@donotdev/components": "0.0.7",
|
|
59
|
+
"@donotdev/core": "0.0.7",
|
|
60
|
+
"@donotdev/crud": "0.0.7",
|
|
61
|
+
"@donotdev/firebase": "0.0.7",
|
|
62
|
+
"@donotdev/oauth": "0.0.7",
|
|
64
63
|
"firebase": "^12.5.0",
|
|
65
64
|
"lucide-react": "^0.562.0",
|
|
66
65
|
"react": "^19.2.3",
|