@krapwoo/ds-viewer 0.1.1 → 0.3.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.
- package/README.md +182 -78
- package/dist/cli/agentsFile.js +31 -0
- package/dist/cli/config.js +26 -1
- package/dist/cli/dev.js +50 -5
- package/dist/cli/doctor.js +492 -0
- package/dist/cli/explain.js +151 -0
- package/dist/cli/githubAction.js +68 -0
- package/dist/cli/glob.js +8 -0
- package/dist/cli/init.js +152 -18
- package/dist/cli/main.js +115 -4
- package/dist/cli/pageIndex.js +27 -1
- package/dist/cli/props.js +170 -21
- package/dist/cli/staticPage.js +356 -0
- package/dist/cli/sync.js +28 -5
- package/dist/cli/workspace.js +49 -21
- package/dist/native/catalog/comparison.js +283 -0
- package/dist/native/catalog/tokens.js +119 -0
- package/dist/native/catalog/types.js +1 -0
- package/native/catalog/CatalogShell.tsx +6 -1
- package/native/catalog/CatalogSidebar.tsx +98 -5
- package/native/catalog/ComparisonGrid.tsx +9 -7
- package/native/catalog/SpacingScaleGallery.tsx +1 -1
- package/native/catalog/TokenRow.tsx +3 -2
- package/native/catalog/catalogNavigation.ts +1 -1
- package/native/catalog/comparison.ts +63 -5
- package/native/catalog/index.ts +8 -6
- package/native/catalog/logoLayout.ts +12 -0
- package/native/catalog/pageApi.ts +21 -3
- package/native/catalog/tokens.ts +5 -0
- package/native/catalog/types.ts +9 -6
- package/package.json +12 -6
- package/starter-kit/WHEN_TO_USE.md +124 -0
- package/starter-kit/components/AnimatedChevron/AnimatedChevron.catalog.tsx +23 -0
- package/starter-kit/components/AnimatedChevron/AnimatedChevron.tsx +46 -0
- package/starter-kit/components/AnimatedChevron/index.ts +2 -0
- package/starter-kit/components/Avatar/Avatar.catalog.tsx +54 -0
- package/starter-kit/components/Avatar/Avatar.tsx +74 -0
- package/starter-kit/components/Avatar/index.ts +2 -0
- package/starter-kit/components/Badge/Badge.catalog.tsx +88 -0
- package/starter-kit/components/Badge/Badge.tsx +56 -0
- package/starter-kit/components/Badge/Badge.types.ts +18 -0
- package/starter-kit/components/Badge/index.ts +2 -0
- package/starter-kit/components/Banner/Banner.catalog.tsx +87 -0
- package/starter-kit/components/Banner/Banner.tsx +257 -0
- package/starter-kit/components/Banner/Banner.types.ts +38 -0
- package/starter-kit/components/Banner/index.ts +2 -0
- package/starter-kit/components/BottomSheet/BottomSheet.catalog.tsx +77 -0
- package/starter-kit/components/BottomSheet/BottomSheet.tsx +151 -0
- package/starter-kit/components/BottomSheet/index.ts +2 -0
- package/starter-kit/components/Button/Button.catalog.tsx +99 -0
- package/starter-kit/components/Button/Button.tsx +173 -0
- package/starter-kit/components/Button/Button.types.ts +52 -0
- package/starter-kit/components/Button/index.ts +2 -0
- package/starter-kit/components/ButtonGroup/ButtonGroup.catalog.tsx +39 -0
- package/starter-kit/components/ButtonGroup/ButtonGroup.tsx +84 -0
- package/starter-kit/components/ButtonGroup/index.ts +2 -0
- package/starter-kit/components/Card/Card.catalog.tsx +68 -0
- package/starter-kit/components/Card/Card.tsx +133 -0
- package/starter-kit/components/Card/index.ts +2 -0
- package/starter-kit/components/Checkbox/Checkbox.catalog.tsx +28 -0
- package/starter-kit/components/Checkbox/Checkbox.tsx +104 -0
- package/starter-kit/components/Checkbox/index.ts +2 -0
- package/starter-kit/components/Dialog/Dialog.catalog.tsx +41 -0
- package/starter-kit/components/Dialog/Dialog.tsx +82 -0
- package/starter-kit/components/Dialog/index.ts +2 -0
- package/starter-kit/components/Divider/Divider.catalog.tsx +35 -0
- package/starter-kit/components/Divider/Divider.tsx +20 -0
- package/starter-kit/components/Divider/index.ts +2 -0
- package/starter-kit/components/Dock/Dock.catalog.tsx +71 -0
- package/starter-kit/components/Dock/Dock.tsx +58 -0
- package/starter-kit/components/Dock/index.ts +2 -0
- package/starter-kit/components/Dropdown/Dropdown.catalog.tsx +69 -0
- package/starter-kit/components/Dropdown/Dropdown.tsx +162 -0
- package/starter-kit/components/Dropdown/index.ts +2 -0
- package/starter-kit/components/EmptyState/EmptyState.catalog.tsx +67 -0
- package/starter-kit/components/EmptyState/EmptyState.tsx +88 -0
- package/starter-kit/components/EmptyState/index.ts +2 -0
- package/starter-kit/components/FieldContainer/FieldContainer.catalog.tsx +76 -0
- package/starter-kit/components/FieldContainer/FieldContainer.tsx +99 -0
- package/starter-kit/components/FieldContainer/FieldContainer.types.ts +27 -0
- package/starter-kit/components/FieldContainer/index.ts +2 -0
- package/starter-kit/components/InputClearButton/InputClearButton.catalog.tsx +18 -0
- package/starter-kit/components/InputClearButton/InputClearButton.tsx +63 -0
- package/starter-kit/components/InputClearButton/index.ts +2 -0
- package/starter-kit/components/InputField/InputField.catalog.tsx +76 -0
- package/starter-kit/components/InputField/InputField.tsx +418 -0
- package/starter-kit/components/InputField/index.ts +2 -0
- package/starter-kit/components/List/List.catalog.tsx +30 -0
- package/starter-kit/components/List/List.tsx +36 -0
- package/starter-kit/components/List/index.ts +2 -0
- package/starter-kit/components/ListItem/ListItem.catalog.tsx +89 -0
- package/starter-kit/components/ListItem/ListItem.tsx +165 -0
- package/starter-kit/components/ListItem/index.ts +2 -0
- package/starter-kit/components/Loading/Loading.catalog.tsx +34 -0
- package/starter-kit/components/Loading/Loading.tsx +121 -0
- package/starter-kit/components/Loading/index.ts +2 -0
- package/starter-kit/components/Pill/Pill.catalog.tsx +79 -0
- package/starter-kit/components/Pill/Pill.tsx +181 -0
- package/starter-kit/components/Pill/Pill.types.ts +30 -0
- package/starter-kit/components/Pill/index.ts +2 -0
- package/starter-kit/components/PillRow/PillRow.catalog.tsx +91 -0
- package/starter-kit/components/PillRow/PillRow.tsx +140 -0
- package/starter-kit/components/PillRow/PillRow.types.ts +24 -0
- package/starter-kit/components/PillRow/index.ts +2 -0
- package/starter-kit/components/ProgressDots/ProgressDots.catalog.tsx +22 -0
- package/starter-kit/components/ProgressDots/ProgressDots.tsx +42 -0
- package/starter-kit/components/ProgressDots/index.ts +2 -0
- package/starter-kit/components/Radio/Radio.catalog.tsx +38 -0
- package/starter-kit/components/Radio/Radio.tsx +114 -0
- package/starter-kit/components/Radio/index.ts +2 -0
- package/starter-kit/components/Radio/usePressScale.ts +33 -0
- package/starter-kit/components/SearchField/SearchField.catalog.tsx +28 -0
- package/starter-kit/components/SearchField/SearchField.tsx +98 -0
- package/starter-kit/components/SearchField/index.ts +2 -0
- package/starter-kit/components/SectionHeader/SectionHeader.catalog.tsx +72 -0
- package/starter-kit/components/SectionHeader/SectionHeader.tsx +139 -0
- package/starter-kit/components/SectionHeader/index.ts +2 -0
- package/starter-kit/components/SegmentedToggle/SegmentedToggle.catalog.tsx +31 -0
- package/starter-kit/components/SegmentedToggle/SegmentedToggle.tsx +163 -0
- package/starter-kit/components/SegmentedToggle/index.ts +2 -0
- package/starter-kit/components/SegmentedToggle/useSlideAnim.ts +29 -0
- package/starter-kit/components/Shimmer/Breathing.catalog.tsx +29 -0
- package/starter-kit/components/Shimmer/Shimmer.catalog.tsx +89 -0
- package/starter-kit/components/Shimmer/Shimmer.tsx +158 -0
- package/starter-kit/components/Shimmer/Shimmer.types.ts +27 -0
- package/starter-kit/components/Shimmer/SkeletonGroup.catalog.tsx +39 -0
- package/starter-kit/components/Shimmer/index.ts +2 -0
- package/starter-kit/components/Surface/Surface.catalog.tsx +44 -0
- package/starter-kit/components/Surface/Surface.tsx +45 -0
- package/starter-kit/components/Surface/index.ts +2 -0
- package/starter-kit/components/Switch/Switch.catalog.tsx +33 -0
- package/starter-kit/components/Switch/Switch.tsx +129 -0
- package/starter-kit/components/Switch/index.ts +2 -0
- package/starter-kit/components/TextArea/TextArea.catalog.tsx +29 -0
- package/starter-kit/components/TextArea/TextArea.tsx +99 -0
- package/starter-kit/components/TextArea/index.ts +2 -0
- package/starter-kit/components/Toast/Toast.catalog.tsx +61 -0
- package/starter-kit/components/Toast/Toast.tsx +144 -0
- package/starter-kit/components/Toast/index.ts +2 -0
- package/starter-kit/components/Tooltip/Tooltip.catalog.tsx +121 -0
- package/starter-kit/components/Tooltip/Tooltip.tsx +195 -0
- package/starter-kit/components/Tooltip/index.ts +2 -0
- package/starter-kit/components/TopNav/TopNav.catalog.tsx +71 -0
- package/starter-kit/components/TopNav/TopNav.tsx +77 -0
- package/starter-kit/components/TopNav/index.ts +2 -0
- package/starter-kit/components/UnderlineTabs/UnderlineTabs.catalog.tsx +30 -0
- package/starter-kit/components/UnderlineTabs/UnderlineTabs.tsx +156 -0
- package/starter-kit/components/UnderlineTabs/index.ts +2 -0
- package/starter-kit/components/index.ts +36 -0
- package/starter-kit/components/statusColors.ts +27 -0
- package/starter-kit/icons/Icon.native.tsx +90 -0
- package/starter-kit/icons/index.ts +10 -0
- package/starter-kit/icons/paths.ts +623 -0
- package/starter-kit/icons/types.ts +69 -0
- package/starter-kit/pages/Colors.catalog.tsx +55 -0
- package/starter-kit/pages/Font.catalog.tsx +45 -0
- package/starter-kit/pages/Icons.catalog.tsx +51 -0
- package/starter-kit/pages/Manifest.catalog.tsx +24 -0
- package/starter-kit/pages/Motion.catalog.tsx +101 -0
- package/starter-kit/pages/Radius.catalog.tsx +35 -0
- package/starter-kit/pages/ReportIssue.catalog.tsx +148 -0
- package/starter-kit/pages/SavedTrips.catalog.tsx +247 -0
- package/starter-kit/pages/Shadow.catalog.tsx +34 -0
- package/starter-kit/pages/Spacing.catalog.tsx +12 -0
- package/starter-kit/pages/TripPlannerForm.catalog.tsx +48 -0
- package/starter-kit/pages/Typography.catalog.tsx +17 -0
- package/starter-kit/tokens/index.ts +50 -0
- package/starter-kit/tokens/motion.ts +90 -0
- package/starter-kit/tokens/palette.ts +111 -0
- package/starter-kit/tokens/scales.ts +106 -0
- package/starter-kit/tokens/semantic.ts +98 -0
- package/starter-kit/tokens/shadow.ts +36 -0
- package/starter-kit/tokens/typography.ts +72 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
-
import { View, Text, ScrollView, Pressable, StyleSheet } from 'react-native';
|
|
3
|
-
import {
|
|
2
|
+
import { View, Text, ScrollView, Pressable, StyleSheet, Image } from 'react-native';
|
|
3
|
+
import type { ImageSourcePropType, LayoutChangeEvent, NativeSyntheticEvent, ImageLoadEventData } from 'react-native';
|
|
4
|
+
import { CATALOG_COLOR, CATALOG_LAYOUT, CATALOG_RADIUS, CATALOG_SPACE, CATALOG_TYPE } from './tokens';
|
|
5
|
+
import { logoLayout } from './logoLayout';
|
|
4
6
|
import { filterGroups, hashForId } from './catalogNavigation';
|
|
5
7
|
import type { NavGroup } from './types';
|
|
6
8
|
import { CatalogSearchInput } from './CatalogSearchInput';
|
|
@@ -48,25 +50,108 @@ function NavItem<TId extends string>({ id, active, onPress }: { id: TId; active:
|
|
|
48
50
|
*/
|
|
49
51
|
export function CatalogSidebar<TId extends string>({
|
|
50
52
|
logo,
|
|
53
|
+
logoImageSource,
|
|
51
54
|
caption,
|
|
52
55
|
groups,
|
|
53
56
|
active,
|
|
54
57
|
onPress,
|
|
55
58
|
}: {
|
|
56
59
|
logo: string;
|
|
60
|
+
/** Optional logo image, validated and bundled by `cli/workspace.ts` from the project's `logo`
|
|
61
|
+
* config field. Visual spec: docs/design/2026-10-08-ds-viewer-sidebar-logo-approved.html
|
|
62
|
+
* ("C · Adaptive", approved 2026-10-08) — a mark (aspect ratio ≤ 2) sits beside the name; a
|
|
63
|
+
* wordmark (> 2) replaces the name text, carrying it as its own accessible label instead. */
|
|
64
|
+
logoImageSource?: ImageSourcePropType;
|
|
57
65
|
caption: string;
|
|
58
66
|
groups: NavGroup<TId>[];
|
|
59
67
|
active: TId | undefined;
|
|
60
68
|
onPress: (id: TId) => void;
|
|
61
69
|
}) {
|
|
62
70
|
const [query, setQuery] = useState('');
|
|
71
|
+
// A bundled local image (the only kind `cli/workspace.ts` ever produces — see its doc comment
|
|
72
|
+
// above) already carries its own `width`/`height` on the required module object, both on native
|
|
73
|
+
// and on react-native-web (confirmed by reading `AssetRegistry`/Metro's asset plugin output) — no
|
|
74
|
+
// need to wait for a load event for the common case. `onLoad` below is kept only as a fallback for
|
|
75
|
+
// a source with no static dimensions (e.g. a bare `{ uri }`); the mock prober stops rendering as
|
|
76
|
+
// soon as either resolves an aspect.
|
|
77
|
+
const staticSource = typeof logoImageSource === 'object' && logoImageSource !== null && !Array.isArray(logoImageSource)
|
|
78
|
+
? (logoImageSource as { width?: number; height?: number })
|
|
79
|
+
: undefined;
|
|
80
|
+
const staticAspect = staticSource?.width && staticSource?.height ? staticSource.width / staticSource.height : undefined;
|
|
81
|
+
const [loadedAspect, setLoadedAspect] = useState<number | undefined>(undefined);
|
|
82
|
+
const [logoFailed, setLogoFailed] = useState(false);
|
|
83
|
+
const [nameWrapped, setNameWrapped] = useState(false);
|
|
63
84
|
const filtered = filterGroups(groups, query);
|
|
64
85
|
|
|
86
|
+
const logoAspect = staticAspect ?? loadedAspect;
|
|
87
|
+
const attemptingLogo = Boolean(logoImageSource) && !logoFailed;
|
|
88
|
+
const header = attemptingLogo ? logoLayout(logoAspect) : 'text';
|
|
89
|
+
|
|
90
|
+
// Fallback only (see the comment above `staticSource`) — and native-only in practice: on web,
|
|
91
|
+
// `ImageLoader.load`'s `onLoad` fires after `HTMLImageElement.decode()` resolves, by which point
|
|
92
|
+
// Chrome has already nulled the underlying DOM event's `target`, so `native.source` below reads
|
|
93
|
+
// `undefined` on react-native-web (confirmed empirically — `event.target` reads non-null only
|
|
94
|
+
// when read synchronously inside `onload`, before any `await`) and this handler never sets
|
|
95
|
+
// `loadedAspect` there. Native's real `Image` reports `source.{width,height}` on its `onLoad`
|
|
96
|
+
// nativeEvent instead, which this still reads correctly. Kept rather than removed (Minor finding,
|
|
97
|
+
// Fable's implementation review) because a non-bundled source with no static dimensions (e.g. a
|
|
98
|
+
// bare `{ uri }`) is still possible on native, even though `cli/workspace.ts` only ever generates
|
|
99
|
+
// a bundled `require()` today.
|
|
100
|
+
const onLogoLoad = (e: NativeSyntheticEvent<ImageLoadEventData>) => {
|
|
101
|
+
const native = e.nativeEvent as unknown as { source?: { width: number; height: number } };
|
|
102
|
+
const width = native.source?.width;
|
|
103
|
+
const height = native.source?.height;
|
|
104
|
+
if (width && height) setLoadedAspect(width / height);
|
|
105
|
+
};
|
|
106
|
+
const onLogoError = () => setLogoFailed(true);
|
|
107
|
+
// Wrap detection for the "mark" row: the name Text's own measured height, not `onTextLayout`
|
|
108
|
+
// (react-native-web's `Text` doesn't implement it) and not a character-count heuristic (wraps at
|
|
109
|
+
// a different length per name). More than one line of `CATALOG_TYPE.brand` means it wrapped.
|
|
110
|
+
const NAME_LINE_HEIGHT = Math.round(CATALOG_TYPE.brand * 1.3);
|
|
111
|
+
const onNameLayout = (e: LayoutChangeEvent) => {
|
|
112
|
+
setNameWrapped(e.nativeEvent.layout.height > NAME_LINE_HEIGHT * 1.5);
|
|
113
|
+
};
|
|
114
|
+
|
|
65
115
|
return (
|
|
66
116
|
<View role="navigation" aria-label="Catalog pages" style={styles.sidebar}>
|
|
67
117
|
<ScrollView style={styles.scroll} contentContainerStyle={styles.content} showsVerticalScrollIndicator={false}>
|
|
68
|
-
<
|
|
69
|
-
|
|
118
|
+
<View style={styles.header}>
|
|
119
|
+
{/* Invisible — exists only so a configured logo's `onLoad`/`onError` can fire at all
|
|
120
|
+
while `header` is still `'text'` (the aspect isn't known yet). Once `logoAspect`
|
|
121
|
+
resolves (or the image fails), `header` switches and this prober stops rendering. */}
|
|
122
|
+
{attemptingLogo && logoAspect === undefined && (
|
|
123
|
+
<Image source={logoImageSource} style={styles.logoProbe} onLoad={onLogoLoad} onError={onLogoError} />
|
|
124
|
+
)}
|
|
125
|
+
{header === 'wordmark' && (
|
|
126
|
+
<>
|
|
127
|
+
<Image
|
|
128
|
+
source={logoImageSource}
|
|
129
|
+
accessibilityRole="image"
|
|
130
|
+
accessibilityLabel={logo}
|
|
131
|
+
resizeMode="contain"
|
|
132
|
+
style={[styles.wordmark, { width: Math.min(CATALOG_LAYOUT.logoWordmarkHeight * (logoAspect ?? 0), 228) }]}
|
|
133
|
+
onLoad={onLogoLoad}
|
|
134
|
+
onError={onLogoError}
|
|
135
|
+
/>
|
|
136
|
+
<Text style={[styles.subtitle, styles.subtitleAfterWordmark]}>{caption}</Text>
|
|
137
|
+
</>
|
|
138
|
+
)}
|
|
139
|
+
{header === 'mark' && (
|
|
140
|
+
<View style={[styles.inlineHeader, nameWrapped && styles.inlineHeaderTop]}>
|
|
141
|
+
<Image source={logoImageSource} resizeMode="contain" style={styles.mark} onLoad={onLogoLoad} onError={onLogoError} />
|
|
142
|
+
<View style={styles.inlineText}>
|
|
143
|
+
<Text style={styles.logo} onLayout={onNameLayout}>{logo}</Text>
|
|
144
|
+
<Text style={styles.subtitle}>{caption}</Text>
|
|
145
|
+
</View>
|
|
146
|
+
</View>
|
|
147
|
+
)}
|
|
148
|
+
{header === 'text' && (
|
|
149
|
+
<>
|
|
150
|
+
<Text style={styles.logo}>{logo}</Text>
|
|
151
|
+
<Text style={styles.subtitle}>{caption}</Text>
|
|
152
|
+
</>
|
|
153
|
+
)}
|
|
154
|
+
</View>
|
|
70
155
|
|
|
71
156
|
<CatalogSearchInput value={query} onChangeText={setQuery} placeholder="Filter components…" />
|
|
72
157
|
|
|
@@ -97,8 +182,16 @@ const styles = StyleSheet.create({
|
|
|
97
182
|
paddingHorizontal: CATALOG_LAYOUT.sidebarPaddingX,
|
|
98
183
|
paddingBottom: CATALOG_LAYOUT.sidebarPaddingTop,
|
|
99
184
|
},
|
|
185
|
+
header: { marginBottom: 20 },
|
|
100
186
|
logo: { fontSize: CATALOG_TYPE.brand, fontWeight: '800', color: CATALOG_COLOR.text },
|
|
101
|
-
subtitle: { fontSize: CATALOG_TYPE.sm, color: CATALOG_COLOR.textMuted, marginTop: 2
|
|
187
|
+
subtitle: { fontSize: CATALOG_TYPE.sm, color: CATALOG_COLOR.textMuted, marginTop: 2 },
|
|
188
|
+
subtitleAfterWordmark: { marginTop: CATALOG_LAYOUT.logoCaptionGap },
|
|
189
|
+
mark: { width: CATALOG_LAYOUT.logoMark, height: CATALOG_LAYOUT.logoMark },
|
|
190
|
+
wordmark: { height: CATALOG_LAYOUT.logoWordmarkHeight, alignSelf: 'flex-start' },
|
|
191
|
+
logoProbe: { position: 'absolute', width: 1, height: 1, opacity: 0 },
|
|
192
|
+
inlineHeader: { flexDirection: 'row', alignItems: 'center', gap: CATALOG_SPACE.md },
|
|
193
|
+
inlineHeaderTop: { alignItems: 'flex-start' },
|
|
194
|
+
inlineText: { flex: 1, minWidth: 0 },
|
|
102
195
|
empty: { fontSize: CATALOG_TYPE.sm, color: CATALOG_COLOR.textMuted, paddingHorizontal: CATALOG_LAYOUT.navItemPaddingX, paddingVertical: 8 },
|
|
103
196
|
groupLabel: {
|
|
104
197
|
fontSize: CATALOG_TYPE.xs,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { View, Text, ScrollView, StyleSheet } from 'react-native';
|
|
3
3
|
import { CATALOG_COLOR, CATALOG_RADIUS, CATALOG_TYPE } from './tokens';
|
|
4
|
-
import { COLUMN_MIN_WIDTH, MATRIX_LAYOUT, cellKey, gridWidthBounds, indexCells, validateComparison } from './comparison';
|
|
4
|
+
import { COLUMN_MIN_WIDTH, MATRIX_LAYOUT, axisItems, cellKey, gridWidthBounds, indexCells, validateComparison } from './comparison';
|
|
5
5
|
import type { ComparisonDef, SpecimenSize } from './types';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -14,7 +14,9 @@ export function ComparisonGrid({ def, size, sectionId }: { def: ComparisonDef; s
|
|
|
14
14
|
for (const issue of validateComparison({ ...def, size })) console.warn(`[Catalog] ${sectionId}: ${issue}`);
|
|
15
15
|
}
|
|
16
16
|
const cells = indexCells(def);
|
|
17
|
-
const
|
|
17
|
+
const rows = axisItems(def.rows);
|
|
18
|
+
const columns = axisItems(def.columns);
|
|
19
|
+
const bounds = gridWidthBounds(columns.length, size);
|
|
18
20
|
const column = {
|
|
19
21
|
flexGrow: 1,
|
|
20
22
|
flexShrink: 1,
|
|
@@ -22,7 +24,7 @@ export function ComparisonGrid({ def, size, sectionId }: { def: ComparisonDef; s
|
|
|
22
24
|
minWidth: COLUMN_MIN_WIDTH[size],
|
|
23
25
|
maxWidth: MATRIX_LAYOUT.columnMaxWidth,
|
|
24
26
|
};
|
|
25
|
-
const lastColumn =
|
|
27
|
+
const lastColumn = columns.length - 1;
|
|
26
28
|
|
|
27
29
|
return (
|
|
28
30
|
<ScrollView horizontal style={styles.scroller} contentContainerStyle={styles.scrollContent}>
|
|
@@ -35,18 +37,18 @@ export function ComparisonGrid({ def, size, sectionId }: { def: ComparisonDef; s
|
|
|
35
37
|
<View role="columnheader" style={[styles.cell, styles.rowHeader, styles.headerCell]}>
|
|
36
38
|
<Text style={styles.headerText}>{def.rowLabel}</Text>
|
|
37
39
|
</View>
|
|
38
|
-
{
|
|
40
|
+
{columns.map((c, ci) => (
|
|
39
41
|
<View key={c.key} role="columnheader" style={[styles.cell, column, styles.headerCell, ci === lastColumn && styles.lastColumn]}>
|
|
40
42
|
<Text style={styles.headerText}>{c.label}</Text>
|
|
41
43
|
</View>
|
|
42
44
|
))}
|
|
43
45
|
</View>
|
|
44
|
-
{
|
|
45
|
-
<View key={row.key} role="row" style={[styles.row, ri ===
|
|
46
|
+
{rows.map((row, ri) => (
|
|
47
|
+
<View key={row.key} role="row" style={[styles.row, ri === rows.length - 1 && styles.lastRow]}>
|
|
46
48
|
<View role="rowheader" style={[styles.cell, styles.rowHeader, styles.bodyRow]}>
|
|
47
49
|
<Text style={styles.rowHeaderText}>{row.label}</Text>
|
|
48
50
|
</View>
|
|
49
|
-
{
|
|
51
|
+
{columns.map((c, ci) => {
|
|
50
52
|
const cell = cells.get(cellKey(row.key, c.key));
|
|
51
53
|
return (
|
|
52
54
|
<View key={c.key} role="cell" style={[styles.cell, column, styles.bodyRow, styles.specimenCell, ci === lastColumn && styles.lastColumn]}>
|
|
@@ -8,7 +8,7 @@ import { CATALOG_TYPE, CATALOG_COLOR, CATALOG_SPACE, CATALOG_RADIUS } from './to
|
|
|
8
8
|
* framework's own, and any host app's). Generic over the step-name type so it works for any scale
|
|
9
9
|
* (the host app's `DS_SPACING`, this framework's own `CATALOG_SPACE`, or a third-party one).
|
|
10
10
|
*/
|
|
11
|
-
export function SpacingScaleGallery<TStep extends string>({
|
|
11
|
+
export function SpacingScaleGallery<TStep extends string | number>({
|
|
12
12
|
steps,
|
|
13
13
|
values,
|
|
14
14
|
useNotes,
|
|
@@ -4,8 +4,9 @@ import { CATALOG_TYPE, CATALOG_COLOR, CATALOG_SPACE } from './tokens';
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Wraps one token's rendered example with a grounded "when to use this" note beneath it — the
|
|
7
|
-
* shared shape behind every token-gallery row (Spacing, Type Scale, …) in both
|
|
8
|
-
* and
|
|
7
|
+
* shared shape behind every token-gallery row (Spacing, Type Scale, …) in both the starter kit's
|
|
8
|
+
* own token pages and the catalog framework's own `kit-host/viewer-pages/` pages. The row's own
|
|
9
|
+
* content (a spacing bar, a type sample, …) is
|
|
9
10
|
* freeform `children`; only the value-plus-use-note stacking is standardized here.
|
|
10
11
|
*
|
|
11
12
|
* Draws a 1px `CATALOG_COLOR.border` bottom divider by default (the same divider PropsTable's rows use) so a stack of TokenRows
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* runner. CatalogShell, CatalogSidebar, and SectionBlock all derive page order from here, so the
|
|
4
4
|
* sidebar order, previous/next order, and fragment fallback can never drift apart.
|
|
5
5
|
*/
|
|
6
|
-
import type { NavGroup } from './types';
|
|
6
|
+
import type { NavGroup } from './types.ts';
|
|
7
7
|
|
|
8
8
|
/** THE canonical within-group ordering of section ids. */
|
|
9
9
|
export function sortIds<TId extends string>(ids: readonly TId[]): TId[] {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* It also decides whether two blocks sit side by side (choosePlacement) and how many columns the
|
|
9
9
|
* Props box uses (propsColumns). ComparisonGrid, ComparisonGroups, and ComparisonList render it.
|
|
10
10
|
*/
|
|
11
|
-
import type { ComparisonDef, PreviewWidths, SectionDef, SpecimenSize, VariantSlot } from './types';
|
|
11
|
+
import type { ComparisonAxisItem, ComparisonDef, GridAxis, PreviewWidths, SectionDef, SpecimenSize, VariantSlot } from './types.ts';
|
|
12
|
+
import type React from 'react';
|
|
12
13
|
|
|
13
14
|
/** Approved geometry. `cellPadding` equals CATALOG_SPACE.lg (asserted in tests). The laptop
|
|
14
15
|
* content width is a 1280px viewport minus the 264px sidebar and 32px side padding. */
|
|
@@ -35,6 +36,61 @@ export const PROPS_COLUMN_GAP = 32;
|
|
|
35
36
|
/** Smallest column or cell width per specimen size. Wide specimens are fixed at phone width. */
|
|
36
37
|
export const COLUMN_MIN_WIDTH: Record<SpecimenSize, number> = { compact: 160, regular: 240, wide: 402 };
|
|
37
38
|
|
|
39
|
+
/** `Array.isArray`'s built-in type guard narrows to a mutable `any[]`, which a `readonly T[]`
|
|
40
|
+
* union member is never assignable to — so the `else` branch of `Array.isArray(axis) ? ... :
|
|
41
|
+
* ...` keeps `axis`'s full union type instead of narrowing away the array member. This explicit
|
|
42
|
+
* predicate sidesteps that (never caught before Task 4: this file had never been typechecked by
|
|
43
|
+
* `tsc` until `cli/doctor.ts`'s import pulled it into the root `tsconfig.json`'s program). */
|
|
44
|
+
function isBoundAxis(axis: GridAxis): axis is { prop: string; items: readonly ComparisonAxisItem[] } {
|
|
45
|
+
return !Array.isArray(axis);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A `GridAxis`'s own items, whether it's a plain array or bound to a prop — every reader of
|
|
49
|
+
* `ComparisonDef.rows`/`columns` goes through this (never `Array.isArray` directly), so a future
|
|
50
|
+
* third axis shape only needs to change this one function. */
|
|
51
|
+
export function axisItems(axis: GridAxis): ComparisonAxisItem[] {
|
|
52
|
+
return isBoundAxis(axis) ? [...axis.items] : [...axis];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The prop name a `GridAxis` is bound to, or undefined for a plain, unbound axis. */
|
|
56
|
+
export function axisProp(axis: GridAxis): string | undefined {
|
|
57
|
+
return isBoundAxis(axis) ? axis.prop : undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Shared page-authoring helper (design §1 "Page shape", §4 "Binding examples to props"). Every
|
|
62
|
+
* grid comparison in the starter kit builds its `ComparisonDef` with this instead of its own local
|
|
63
|
+
* copy, so `cli/staticPage.ts`'s static reader has exactly one call shape to recognize anywhere a
|
|
64
|
+
* page's `comparison` field is produced by a function call rather than written as a plain object
|
|
65
|
+
* literal. `rows`/`columns` accept either a plain item array (unbound) or `{ prop, items }` (bound)
|
|
66
|
+
* — the cell grid itself is unaffected either way, since cells are always keyed by each axis's own
|
|
67
|
+
* items regardless of whether that axis names a prop. Lives here, beside `axisItems`/`axisProp`,
|
|
68
|
+
* rather than in its own `grid.ts` — every other file in this module has only *type-only* relative
|
|
69
|
+
* imports; a bare, no-extension value import would be the first of its kind in `native/catalog`.
|
|
70
|
+
*
|
|
71
|
+
* Each cell is a real instance with exactly the props its row and column name. Never build these by
|
|
72
|
+
* multiplying `variants` with `states` — those are pre-rendered nodes and cannot be combined.
|
|
73
|
+
*/
|
|
74
|
+
export function grid(
|
|
75
|
+
rowLabel: string,
|
|
76
|
+
columnLabel: string,
|
|
77
|
+
rows: GridAxis,
|
|
78
|
+
columns: GridAxis,
|
|
79
|
+
cell: (row: string, column: string) => React.ReactNode,
|
|
80
|
+
size?: SpecimenSize,
|
|
81
|
+
): ComparisonDef {
|
|
82
|
+
const rowItems = axisItems(rows);
|
|
83
|
+
const columnItems = axisItems(columns);
|
|
84
|
+
return {
|
|
85
|
+
rowLabel,
|
|
86
|
+
columnLabel,
|
|
87
|
+
rows,
|
|
88
|
+
columns,
|
|
89
|
+
cells: rowItems.flatMap((row) => columnItems.map((column) => ({ rowKey: row.key, columnKey: column.key, node: cell(row.key, column.key) }))),
|
|
90
|
+
size,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
38
94
|
/** How many grid columns fit beside the row header on a 1280px laptop without scrolling. */
|
|
39
95
|
export function gridColumnLimit(size: SpecimenSize): number {
|
|
40
96
|
return Math.floor((MATRIX_LAYOUT.laptopContentWidth - MATRIX_LAYOUT.rowHeaderWidth) / COLUMN_MIN_WIDTH[size]);
|
|
@@ -94,17 +150,19 @@ export function indexCells(def: ComparisonDef): Map<string, ComparisonDef['cells
|
|
|
94
150
|
/** Human-readable problems in a stable order: axis duplicates, column fit, each cell in
|
|
95
151
|
* declaration order, then missing coordinates in row-major order. Empty when valid. */
|
|
96
152
|
export function validateComparison(def: ComparisonDef): string[] {
|
|
97
|
-
|
|
153
|
+
const rowItems = axisItems(def.rows);
|
|
154
|
+
const columnItems = axisItems(def.columns);
|
|
155
|
+
if (rowItems.length === 0 || columnItems.length === 0) {
|
|
98
156
|
return ['Comparison needs at least one row and one column.'];
|
|
99
157
|
}
|
|
100
158
|
const issues: string[] = [];
|
|
101
159
|
const rowKeys = new Set<string>();
|
|
102
|
-
for (const row of
|
|
160
|
+
for (const row of rowItems) {
|
|
103
161
|
if (rowKeys.has(row.key)) issues.push(`Duplicate row key "${row.key}".`);
|
|
104
162
|
rowKeys.add(row.key);
|
|
105
163
|
}
|
|
106
164
|
const columnKeys = new Set<string>();
|
|
107
|
-
for (const column of
|
|
165
|
+
for (const column of columnItems) {
|
|
108
166
|
if (columnKeys.has(column.key)) issues.push(`Duplicate column key "${column.key}".`);
|
|
109
167
|
columnKeys.add(column.key);
|
|
110
168
|
}
|
|
@@ -149,7 +207,7 @@ export function validateComparison(def: ComparisonDef): string[] {
|
|
|
149
207
|
export function remainingStates(states: VariantSlot | undefined, comparison: ComparisonDef | undefined): VariantSlot | undefined {
|
|
150
208
|
if (!states) return undefined;
|
|
151
209
|
if (!comparison) return states;
|
|
152
|
-
const covered = new Set([...comparison.rows.map((row) => row.key), ...comparison.columns.map((column) => column.key)]);
|
|
210
|
+
const covered = new Set([...axisItems(comparison.rows).map((row) => row.key), ...axisItems(comparison.columns).map((column) => column.key)]);
|
|
153
211
|
const items = states.items.filter((item) => !covered.has(item.key));
|
|
154
212
|
return items.length > 0 ? { ...states, items } : undefined;
|
|
155
213
|
}
|
package/native/catalog/index.ts
CHANGED
|
@@ -6,17 +6,18 @@
|
|
|
6
6
|
* token group, each with a `render()` that uses that app's real components) and a `NavGroup[]`
|
|
7
7
|
* (how to bucket those sections in the sidebar), then render a single `<CatalogShell />`.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* A page is written with `defineCatalogPage()` from this barrel and placed next to its component
|
|
10
|
+
* (or standalone, for tokens/recipes) — see the starter kit under `starter-kit/` for worked
|
|
11
|
+
* examples, and `kit-host/viewer-pages/` for pages documenting this framework's own pieces. This
|
|
12
|
+
* barrel itself stays free of any specific component (including its own) so it can be copied into
|
|
13
|
+
* a different app's repo as-is.
|
|
14
14
|
*/
|
|
15
15
|
export { CatalogShell } from './CatalogShell';
|
|
16
16
|
export { CatalogSidebar } from './CatalogSidebar';
|
|
17
17
|
export { CatalogSearchInput } from './CatalogSearchInput';
|
|
18
18
|
export { SectionBlock } from './SectionBlock';
|
|
19
19
|
export { ComparisonGrid } from './ComparisonGrid';
|
|
20
|
+
export { grid, axisItems, axisProp } from './comparison';
|
|
20
21
|
export { ComparisonGroups } from './ComparisonGroups';
|
|
21
22
|
export { ComparisonList } from './ComparisonList';
|
|
22
23
|
export { ReferenceDetails } from './ReferenceDetails';
|
|
@@ -32,7 +33,8 @@ export { buildComponentManifest } from './manifest';
|
|
|
32
33
|
export type { ComponentManifestEntry, ManifestExample } from './manifest';
|
|
33
34
|
export { defineCatalogPage, buildCatalogSections } from './pageApi';
|
|
34
35
|
export type { CatalogPage, CatalogPageInput, GeneratedComponent, GeneratedPropRecord } from './pageApi';
|
|
35
|
-
export type { PropDef, SectionDef, NavGroup, SpecimenSize, ComparisonDef, ComparisonCell, ComparisonAxisItem, PreviewWidths } from './types';
|
|
36
|
+
export type { PropDef, SectionDef, NavGroup, SpecimenSize, ComparisonDef, ComparisonCell, ComparisonAxisItem, GridAxis, PreviewWidths } from './types';
|
|
37
|
+
export type { ListGroup, ListItem } from './comparison';
|
|
36
38
|
export {
|
|
37
39
|
CATALOG_TYPE,
|
|
38
40
|
CATALOG_TYPE_USE,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Sidebar logo header layout (design `docs/design/2026-10-08-ds-viewer-sidebar-logo-approved.html`,
|
|
2
|
+
* "C · Adaptive", approved 2026-10-08): the configured logo image's own aspect ratio decides the
|
|
3
|
+
* layout — no config field chooses it. An unknown or invalid aspect falls back to the safer
|
|
4
|
+
* text-only header rather than guessing (`CatalogSidebar.tsx` renders this case until the image's
|
|
5
|
+
* `onLoad` reports a real size, and whenever there is no logo, it failed to load, or its file is
|
|
6
|
+
* missing). */
|
|
7
|
+
export const LOGO_WORDMARK_MIN_ASPECT = 2;
|
|
8
|
+
|
|
9
|
+
export function logoLayout(aspect: number | undefined): 'text' | 'mark' | 'wordmark' {
|
|
10
|
+
if (aspect === undefined || !Number.isFinite(aspect) || aspect <= 0) return 'text';
|
|
11
|
+
return aspect > LOGO_WORDMARK_MIN_ASPECT ? 'wordmark' : 'mark';
|
|
12
|
+
}
|
|
@@ -59,16 +59,25 @@ export interface GeneratedComponent {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function mergePropNotes(component: GeneratedComponent, propNotes: Record<string, string> | undefined): PropDef[] {
|
|
62
|
-
|
|
62
|
+
const own = component.props.map((prop) => ({
|
|
63
63
|
name: prop.name,
|
|
64
64
|
type: prop.type,
|
|
65
65
|
required: prop.required,
|
|
66
66
|
default: prop.default,
|
|
67
67
|
desc: propNotes?.[prop.name] ? `${prop.desc} ${propNotes[prop.name]}`.trim() : prop.desc,
|
|
68
68
|
}));
|
|
69
|
+
// Design §3: props inherited from node_modules (e.g. `extends TextInputProps`) are summarised as
|
|
70
|
+
// one row per source instead of being listed individually.
|
|
71
|
+
const inherited = component.inheritedFrom.map((source) => ({
|
|
72
|
+
name: `${source} props`,
|
|
73
|
+
type: 'inherited',
|
|
74
|
+
required: true,
|
|
75
|
+
desc: `Plus all ${source} props, not listed individually.`,
|
|
76
|
+
}));
|
|
77
|
+
return [...own, ...inherited];
|
|
69
78
|
}
|
|
70
79
|
|
|
71
|
-
function orderGroups(groupIds: Map<string,
|
|
80
|
+
function orderGroups<TId extends string>(groupIds: Map<string, TId[]>, groupOrder: string[] | undefined): NavGroup<TId>[] {
|
|
72
81
|
const listed = groupOrder ?? [];
|
|
73
82
|
const labels = [...groupIds.keys()];
|
|
74
83
|
const ordered = [...listed.filter((label) => groupIds.has(label)), ...labels.filter((label) => !listed.includes(label)).sort()];
|
|
@@ -94,7 +103,16 @@ export function buildCatalogSections<TId extends string>(
|
|
|
94
103
|
const groupIds = new Map<string, TId[]>();
|
|
95
104
|
const addToGroup = (group: string, id: TId) => groupIds.set(group, [...(groupIds.get(group) ?? []), id]);
|
|
96
105
|
|
|
106
|
+
// Design §4 "duplicate-page-id" rule's viewer-side equivalent — this is the one place in the
|
|
107
|
+
// whole viewer that already resolves every page's id to build sections/groups, so it costs
|
|
108
|
+
// nothing extra to also notice two pages claiming the same one (`sectionsById` in
|
|
109
|
+
// `CatalogShell.tsx` would otherwise just let the last one silently win).
|
|
110
|
+
const seenIds = new Set<TId>();
|
|
97
111
|
const sections: SectionDef<TId>[] = pages.map((page) => {
|
|
112
|
+
if ((globalThis as { __DEV__?: boolean }).__DEV__) {
|
|
113
|
+
if (seenIds.has(page.id)) console.warn(`[Catalog] Page id "${page.id}" is used by more than one page; only the last one will show. Give one of them its own "id".`);
|
|
114
|
+
seenIds.add(page.id);
|
|
115
|
+
}
|
|
98
116
|
const componentName = page.component ?? page.id;
|
|
99
117
|
const component = componentByName.get(componentName);
|
|
100
118
|
addToGroup(page.group, page.id);
|
|
@@ -127,5 +145,5 @@ export function buildCatalogSections<TId extends string>(
|
|
|
127
145
|
addToGroup('Components', id);
|
|
128
146
|
}
|
|
129
147
|
|
|
130
|
-
return { sections, groups: orderGroups(groupIds, groupOrder)
|
|
148
|
+
return { sections, groups: orderGroups(groupIds, groupOrder) };
|
|
131
149
|
}
|
package/native/catalog/tokens.ts
CHANGED
|
@@ -58,6 +58,11 @@ export const CATALOG_LAYOUT = {
|
|
|
58
58
|
factPaddingY: 9,
|
|
59
59
|
blockLabelGap: 14,
|
|
60
60
|
blockGap: 28,
|
|
61
|
+
/** Sidebar logo header (design `docs/design/2026-10-08-ds-viewer-sidebar-logo-approved.html`,
|
|
62
|
+
* "C · Adaptive", approved 2026-10-08) — see `native/catalog/logoLayout.ts`. */
|
|
63
|
+
logoMark: 32,
|
|
64
|
+
logoWordmarkHeight: 28,
|
|
65
|
+
logoCaptionGap: 6,
|
|
61
66
|
} as const;
|
|
62
67
|
|
|
63
68
|
/** Max width of the main content column. */
|
package/native/catalog/types.ts
CHANGED
|
@@ -65,6 +65,13 @@ export interface ComparisonAxisItem {
|
|
|
65
65
|
label: string;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/** A grid axis's rows or columns: a plain list of items (today's shape, unchanged), or items bound
|
|
69
|
+
* to a real prop so `doctor` can check each item's key against that prop's actual options (design
|
|
70
|
+
* §4 "Binding examples to props"). Only a prop typed as a string-literal union can be bound —
|
|
71
|
+
* binding any other prop is a `doctor` error, never a runtime check here (the viewer renders a
|
|
72
|
+
* bound and an unbound axis identically; see `native/catalog/comparison.ts`'s `axisItems`). */
|
|
73
|
+
export type GridAxis = readonly ComparisonAxisItem[] | { prop: string; items: readonly ComparisonAxisItem[] };
|
|
74
|
+
|
|
68
75
|
/** One authored row × column specimen. Provide exactly one of `node` (a real instance of the
|
|
69
76
|
* documented component) or `unavailableReason` (the combination genuinely does not exist). */
|
|
70
77
|
export interface ComparisonCell {
|
|
@@ -83,8 +90,8 @@ export interface ComparisonDef {
|
|
|
83
90
|
rowLabel: string;
|
|
84
91
|
/** Names the column axis (e.g. "State"). */
|
|
85
92
|
columnLabel: string;
|
|
86
|
-
rows:
|
|
87
|
-
columns:
|
|
93
|
+
rows: GridAxis;
|
|
94
|
+
columns: GridAxis;
|
|
88
95
|
cells: ComparisonCell[];
|
|
89
96
|
/** Defaults to the section's `specimenSize`, then 'regular'. */
|
|
90
97
|
size?: SpecimenSize;
|
|
@@ -188,7 +195,3 @@ export interface NavGroup<TId extends string = string> {
|
|
|
188
195
|
label: string;
|
|
189
196
|
ids: readonly TId[];
|
|
190
197
|
}
|
|
191
|
-
|
|
192
|
-
/** Canonical within-group ordering — implemented in ./catalogNavigation (pure, unit-tested) and
|
|
193
|
-
* re-exported here so existing imports keep working. */
|
|
194
|
-
export { sortIds } from './catalogNavigation';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krapwoo/ds-viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A browsable, always-current catalog for an Expo / React Native design system.",
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"native/catalog",
|
|
28
|
-
"!native/catalog/CatalogExample.tsx",
|
|
29
|
-
"!native/catalog/CatalogFrameworkExample.tsx",
|
|
30
28
|
"!native/catalog/**/__tests__",
|
|
29
|
+
"starter-kit",
|
|
31
30
|
"dist/cli",
|
|
32
31
|
"dist/config",
|
|
32
|
+
"dist/native",
|
|
33
33
|
"config/index.ts",
|
|
34
34
|
"README.md",
|
|
35
35
|
"LICENSE"
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"react": "*",
|
|
40
40
|
"react-dom": "*",
|
|
41
41
|
"react-native": "*",
|
|
42
|
-
"react-native-
|
|
43
|
-
"react-native-
|
|
42
|
+
"react-native-safe-area-context": "*",
|
|
43
|
+
"react-native-web": "*"
|
|
44
44
|
},
|
|
45
45
|
"peerDependenciesMeta": {
|
|
46
46
|
"react-native-safe-area-context": {
|
|
@@ -51,12 +51,18 @@
|
|
|
51
51
|
"typescript": "^6.0.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@types/node": "^22.0.0"
|
|
54
|
+
"@types/node": "^22.0.0",
|
|
55
|
+
"@types/react": "^19.0.0",
|
|
56
|
+
"puppeteer": "^25.12.0"
|
|
55
57
|
},
|
|
56
58
|
"scripts": {
|
|
57
59
|
"build": "tsc -p tsconfig.json",
|
|
58
60
|
"test": "node --experimental-strip-types --test 'cli/**/*.test.ts' 'config/**/*.test.ts' 'native/catalog/__tests__/*.test.ts'",
|
|
59
61
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
62
|
+
"kit:dev": "npm run build && cd kit-host && node node_modules/@krapwoo/ds-viewer/dist/cli/main.js dev",
|
|
63
|
+
"check:catalog": "npm run build && node scripts/checkCatalogConsole.mjs kit-host",
|
|
64
|
+
"check:doctor": "npm run build && cd kit-host && node node_modules/@krapwoo/ds-viewer/dist/cli/main.js doctor --ci",
|
|
65
|
+
"check:types": "tsc --noEmit -p kit-host",
|
|
60
66
|
"release": "node scripts/release.mjs"
|
|
61
67
|
}
|
|
62
68
|
}
|