@principal-ai/subsystems-react 0.36.2 → 0.37.1
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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/pierre/PierreWalkthroughCodeView.d.ts +19 -1
- package/dist/pierre/PierreWalkthroughCodeView.d.ts.map +1 -1
- package/dist/pierre/PierreWalkthroughCodeView.js +53 -2
- package/dist/pierre/PierreWalkthroughCodeView.js.map +1 -1
- package/dist/subsystem/ComponentDeclaration.d.ts +13 -1
- package/dist/subsystem/ComponentDeclaration.d.ts.map +1 -1
- package/dist/subsystem/ComponentDeclaration.js +58 -7
- package/dist/subsystem/ComponentDeclaration.js.map +1 -1
- package/dist/subsystem/ConstructsCatalog.d.ts +6 -3
- package/dist/subsystem/ConstructsCatalog.d.ts.map +1 -1
- package/dist/subsystem/ConstructsCatalog.js +206 -217
- package/dist/subsystem/ConstructsCatalog.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts +10 -0
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +108 -31
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/SymbolInspectionCard.d.ts.map +1 -1
- package/dist/subsystem/SymbolInspectionCard.js +1 -1
- package/dist/subsystem/SymbolInspectionCard.js.map +1 -1
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +7 -1
- package/dist/subsystem/model.js.map +1 -1
- package/dist/utils/edgeLabel.d.ts +51 -0
- package/dist/utils/edgeLabel.d.ts.map +1 -0
- package/dist/utils/edgeLabel.js +81 -0
- package/dist/utils/edgeLabel.js.map +1 -0
- package/dist/utils/elkLayout.d.ts +6 -0
- package/dist/utils/elkLayout.d.ts.map +1 -1
- package/dist/utils/elkLayout.js +27 -6
- package/dist/utils/elkLayout.js.map +1 -1
- package/package.json +3 -2
- package/src/index.ts +7 -0
- package/src/pierre/PierreWalkthroughCodeView.tsx +75 -1
- package/src/stories/Subsystem/ComponentGraph/Flows.stories.tsx +222 -0
- package/src/stories/Subsystem/ConstructsCatalog.stories.tsx +443 -0
- package/src/subsystem/ComponentDeclaration.tsx +100 -7
- package/src/subsystem/ConstructsCatalog.tsx +291 -323
- package/src/subsystem/SubsystemComponentGraph.tsx +173 -30
- package/src/subsystem/SymbolInspectionCard.tsx +10 -9
- package/src/subsystem/model.ts +7 -1
- package/src/utils/edgeLabel.test.ts +23 -0
- package/src/utils/edgeLabel.ts +95 -0
- package/src/utils/elkLayout.ts +41 -6
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
type DiffLineAnnotation,
|
|
24
24
|
type LineAnnotation,
|
|
25
25
|
} from '@pierre/diffs/react';
|
|
26
|
+
import type { TokenEventBase } from '@pierre/diffs';
|
|
26
27
|
import { Maximize2 } from 'lucide-react';
|
|
27
28
|
|
|
28
29
|
/** Mirrors Pierre's CodeViewLineSelection (not re-exported from the React entry). */
|
|
@@ -48,6 +49,33 @@ import {
|
|
|
48
49
|
type SnippetSlice,
|
|
49
50
|
} from './sliceSnippet';
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* A token the user clicked or hovered inside a step's snippet. `stepIndex` is
|
|
54
|
+
* the zero-based index of the walkthrough step whose snippet it sits in, so a
|
|
55
|
+
* host can resolve the token against that step's construct endpoints.
|
|
56
|
+
*/
|
|
57
|
+
export interface WalkthroughSymbolQuery {
|
|
58
|
+
stepIndex: number;
|
|
59
|
+
tokenText: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Ctx item id → step index, for CodeView's `walkthroughId:index` item ids. */
|
|
63
|
+
function stepIndexFromItemContext(context: unknown): number | null {
|
|
64
|
+
const id = (context as { item?: { id?: string } } | undefined)?.item?.id;
|
|
65
|
+
if (id == null) return null;
|
|
66
|
+
const index = Number.parseInt(id.split(':').pop() ?? '', 10);
|
|
67
|
+
return Number.isFinite(index) ? index : null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Pointer/dotted-underline affordance for a clickable construct token. */
|
|
71
|
+
function paintSymbolToken(el: HTMLElement | undefined, active: boolean): void {
|
|
72
|
+
if (el == null) return;
|
|
73
|
+
el.style.cursor = active ? 'pointer' : '';
|
|
74
|
+
el.style.textDecorationLine = active ? 'underline' : '';
|
|
75
|
+
el.style.textDecorationStyle = active ? 'dotted' : '';
|
|
76
|
+
el.style.textUnderlineOffset = active ? '2px' : '';
|
|
77
|
+
}
|
|
78
|
+
|
|
51
79
|
export interface PierreWalkthroughCodeViewProps {
|
|
52
80
|
walkthrough: SubsystemWalkthrough;
|
|
53
81
|
/** Focused step; `null` shows all snippets without scrolling to a step. */
|
|
@@ -65,6 +93,15 @@ export interface PierreWalkthroughCodeViewProps {
|
|
|
65
93
|
* but whose endpoint is proposed reads as "planned" rather than "missing".
|
|
66
94
|
*/
|
|
67
95
|
proposedAliases?: ReadonlySet<string>;
|
|
96
|
+
/**
|
|
97
|
+
* Resolve a token to a model construct key (e.g. a component alias) for the
|
|
98
|
+
* step its snippet belongs to. Return `null` when the token doesn't name a
|
|
99
|
+
* construct this flow touches. When both this and `onSymbolClick` are set,
|
|
100
|
+
* matching tokens read as clickable (pointer + dotted underline).
|
|
101
|
+
*/
|
|
102
|
+
resolveSymbol?: (query: WalkthroughSymbolQuery) => string | null;
|
|
103
|
+
/** Fired when a token resolved by `resolveSymbol` is clicked. */
|
|
104
|
+
onSymbolClick?: (symbol: string, query: WalkthroughSymbolQuery) => void;
|
|
68
105
|
}
|
|
69
106
|
|
|
70
107
|
type FileLoadState =
|
|
@@ -157,11 +194,20 @@ export function PierreWalkthroughCodeView({
|
|
|
157
194
|
background,
|
|
158
195
|
onOpenFile,
|
|
159
196
|
proposedAliases,
|
|
197
|
+
resolveSymbol,
|
|
198
|
+
onSymbolClick,
|
|
160
199
|
}: PierreWalkthroughCodeViewProps) {
|
|
161
200
|
const { theme, mode } = useTheme();
|
|
162
201
|
const viewRef = useRef<CodeViewHandle<undefined>>(null);
|
|
163
202
|
const [load, setLoad] = useState<FileLoadState>({ status: 'loading' });
|
|
164
203
|
|
|
204
|
+
// Keep the newest symbol callbacks in refs so the Pierre options object stays
|
|
205
|
+
// stable (host callbacks are recreated per render).
|
|
206
|
+
const resolveSymbolRef = useRef(resolveSymbol);
|
|
207
|
+
resolveSymbolRef.current = resolveSymbol;
|
|
208
|
+
const onSymbolClickRef = useRef(onSymbolClick);
|
|
209
|
+
onSymbolClickRef.current = onSymbolClick;
|
|
210
|
+
|
|
165
211
|
// A hop onto a proposed component is planned work; label its missing file
|
|
166
212
|
// accordingly instead of showing a bare "not found".
|
|
167
213
|
const isProposedStep = useCallback(
|
|
@@ -415,6 +461,33 @@ export function PierreWalkthroughCodeView({
|
|
|
415
461
|
[onOpenFile, walkthrough.steps, load],
|
|
416
462
|
);
|
|
417
463
|
|
|
464
|
+
// Per-token interactions for construct navigation. Only wired when the host
|
|
465
|
+
// supplies both callbacks; providing them also switches Pierre into its
|
|
466
|
+
// token-transformer render path (pointer events land on token spans).
|
|
467
|
+
const symbolHandlers = useMemo((): Partial<CodeViewReactOptions> => {
|
|
468
|
+
if (resolveSymbol == null || onSymbolClick == null) return {};
|
|
469
|
+
const resolveAt = (
|
|
470
|
+
props: TokenEventBase,
|
|
471
|
+
context: unknown,
|
|
472
|
+
): { symbol: string; query: WalkthroughSymbolQuery } | null => {
|
|
473
|
+
const stepIndex = stepIndexFromItemContext(context);
|
|
474
|
+
const tokenText = props?.tokenText;
|
|
475
|
+
if (stepIndex == null || !tokenText) return null;
|
|
476
|
+
const symbol = resolveSymbolRef.current?.({ stepIndex, tokenText });
|
|
477
|
+
return symbol == null ? null : { symbol, query: { stepIndex, tokenText } };
|
|
478
|
+
};
|
|
479
|
+
return {
|
|
480
|
+
onTokenClick: (props, _event, context) => {
|
|
481
|
+
const hit = resolveAt(props, context);
|
|
482
|
+
if (hit) onSymbolClickRef.current?.(hit.symbol, hit.query);
|
|
483
|
+
},
|
|
484
|
+
onTokenEnter: (props, _event, context) => {
|
|
485
|
+
if (resolveAt(props, context)) paintSymbolToken(props.tokenElement, true);
|
|
486
|
+
},
|
|
487
|
+
onTokenLeave: (props) => paintSymbolToken(props.tokenElement, false),
|
|
488
|
+
};
|
|
489
|
+
}, [resolveSymbol, onSymbolClick]);
|
|
490
|
+
|
|
418
491
|
const options = useMemo((): CodeViewReactOptions => {
|
|
419
492
|
return {
|
|
420
493
|
theme: {
|
|
@@ -426,10 +499,11 @@ export function PierreWalkthroughCodeView({
|
|
|
426
499
|
layout: { paddingTop: 0, paddingBottom: 0, gap: 4 },
|
|
427
500
|
onPostRender,
|
|
428
501
|
...(onOpenFile ? { onLineClick } : {}),
|
|
502
|
+
...symbolHandlers,
|
|
429
503
|
...(background ? buildPierreOptions(background) : {}),
|
|
430
504
|
...(mode === 'light' || mode === 'dark' ? { themeType: mode } : {}),
|
|
431
505
|
};
|
|
432
|
-
}, [background, mode, onPostRender, onOpenFile, onLineClick]);
|
|
506
|
+
}, [background, mode, onPostRender, onOpenFile, onLineClick, symbolHandlers]);
|
|
433
507
|
|
|
434
508
|
useEffect(() => {
|
|
435
509
|
if (load.status !== 'ready' || stepIndex == null) return;
|
|
@@ -171,6 +171,8 @@ function FlowsDemo() {
|
|
|
171
171
|
stepIndex,
|
|
172
172
|
onOpenFile,
|
|
173
173
|
proposedAliases,
|
|
174
|
+
resolveSymbol,
|
|
175
|
+
onSymbolClick,
|
|
174
176
|
}: WalkthroughViewerContext) => (
|
|
175
177
|
<PierreWalkthroughCodeView
|
|
176
178
|
walkthrough={walkthrough}
|
|
@@ -179,6 +181,8 @@ function FlowsDemo() {
|
|
|
179
181
|
contextLines={4}
|
|
180
182
|
onOpenFile={onOpenFile}
|
|
181
183
|
proposedAliases={proposedAliases}
|
|
184
|
+
resolveSymbol={resolveSymbol}
|
|
185
|
+
onSymbolClick={onSymbolClick}
|
|
182
186
|
/>
|
|
183
187
|
),
|
|
184
188
|
[],
|
|
@@ -276,6 +280,8 @@ function ProposedMissingStepDemo() {
|
|
|
276
280
|
stepIndex,
|
|
277
281
|
onOpenFile,
|
|
278
282
|
proposedAliases,
|
|
283
|
+
resolveSymbol,
|
|
284
|
+
onSymbolClick,
|
|
279
285
|
}: WalkthroughViewerContext) => (
|
|
280
286
|
<PierreWalkthroughCodeView
|
|
281
287
|
walkthrough={walkthrough}
|
|
@@ -284,6 +290,8 @@ function ProposedMissingStepDemo() {
|
|
|
284
290
|
contextLines={4}
|
|
285
291
|
onOpenFile={onOpenFile}
|
|
286
292
|
proposedAliases={proposedAliases}
|
|
293
|
+
resolveSymbol={resolveSymbol}
|
|
294
|
+
onSymbolClick={onSymbolClick}
|
|
287
295
|
/>
|
|
288
296
|
),
|
|
289
297
|
[],
|
|
@@ -319,3 +327,217 @@ function ProposedMissingStepDemo() {
|
|
|
319
327
|
export const ProposedMissingStep: Story = {
|
|
320
328
|
render: () => <ProposedMissingStepDemo />,
|
|
321
329
|
};
|
|
330
|
+
|
|
331
|
+
// --- Clickable constructs -------------------------------------------------
|
|
332
|
+
//
|
|
333
|
+
// A walkthrough step's line is an edge between two constructs. When the host
|
|
334
|
+
// supplies `resolveSymbol`/`onSymbolClick` (the graph derives them from the
|
|
335
|
+
// step's `from`/`to` components), a token in the snippet that names either
|
|
336
|
+
// endpoint becomes clickable — clicking it opens that construct's file at its
|
|
337
|
+
// declaration line in the bottom drawer. The second endpoint here is a
|
|
338
|
+
// `method` construct (`DrawingStore.list`), so the bare `list` token is
|
|
339
|
+
// clickable and jumps to the method's declaration.
|
|
340
|
+
|
|
341
|
+
const clickableComponents: SubsystemComponent[] = [
|
|
342
|
+
{
|
|
343
|
+
alias: 'load-panel',
|
|
344
|
+
name: 'LoadPanel',
|
|
345
|
+
construct: 'function',
|
|
346
|
+
file: 'src/load/LoadPanel.tsx',
|
|
347
|
+
purl: 'pkg:github/principal-ai/desktop-app',
|
|
348
|
+
symbol: 'LoadPanel',
|
|
349
|
+
purpose: 'loads the drawing list and hands rows to the render surface',
|
|
350
|
+
process: 'draw-list',
|
|
351
|
+
declarationRef: {
|
|
352
|
+
file: 'src/load/LoadPanel.tsx',
|
|
353
|
+
startLine: 3,
|
|
354
|
+
lineHash: 'story',
|
|
355
|
+
capturedAt: '2026-01-01T00:00:00.000Z',
|
|
356
|
+
},
|
|
357
|
+
declaration: {
|
|
358
|
+
kind: 'function',
|
|
359
|
+
parameters: [],
|
|
360
|
+
returnType: 'JSX.Element',
|
|
361
|
+
callers: [],
|
|
362
|
+
callees: [],
|
|
363
|
+
},
|
|
364
|
+
declarationProvenance: 'authored',
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
alias: 'drawing-store-list',
|
|
368
|
+
name: 'list',
|
|
369
|
+
construct: 'method',
|
|
370
|
+
file: 'src/store/DrawingStore.ts',
|
|
371
|
+
purl: 'pkg:github/principal-ai/desktop-app',
|
|
372
|
+
symbol: 'DrawingStore.list',
|
|
373
|
+
purpose: 'lists stored drawings for the panel',
|
|
374
|
+
process: 'draw-list',
|
|
375
|
+
declarationRef: {
|
|
376
|
+
file: 'src/store/DrawingStore.ts',
|
|
377
|
+
startLine: 4,
|
|
378
|
+
lineHash: 'story',
|
|
379
|
+
capturedAt: '2026-01-01T00:00:00.000Z',
|
|
380
|
+
},
|
|
381
|
+
declaration: {
|
|
382
|
+
kind: 'method',
|
|
383
|
+
hostClass: 'DrawingStore',
|
|
384
|
+
parameters: [],
|
|
385
|
+
returnType: 'Drawing[]',
|
|
386
|
+
},
|
|
387
|
+
declarationProvenance: 'authored',
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
alias: 'render-surface',
|
|
391
|
+
name: 'RenderSurface',
|
|
392
|
+
construct: 'class',
|
|
393
|
+
file: 'src/render/RenderSurface.ts',
|
|
394
|
+
purl: 'pkg:github/principal-ai/desktop-app',
|
|
395
|
+
symbol: 'RenderSurface',
|
|
396
|
+
purpose: 'paints a drawing into a surface',
|
|
397
|
+
process: 'draw-host',
|
|
398
|
+
declarationRef: {
|
|
399
|
+
file: 'src/render/RenderSurface.ts',
|
|
400
|
+
startLine: 3,
|
|
401
|
+
lineHash: 'story',
|
|
402
|
+
capturedAt: '2026-01-01T00:00:00.000Z',
|
|
403
|
+
},
|
|
404
|
+
declaration: {
|
|
405
|
+
kind: 'class',
|
|
406
|
+
methods: [{ nodeId: 'RenderSurface.render', name: 'render' }],
|
|
407
|
+
properties: [],
|
|
408
|
+
extends: [],
|
|
409
|
+
implements: [],
|
|
410
|
+
instantiations: [],
|
|
411
|
+
references: [],
|
|
412
|
+
},
|
|
413
|
+
declarationProvenance: 'authored',
|
|
414
|
+
},
|
|
415
|
+
];
|
|
416
|
+
|
|
417
|
+
const clickableFiles: Record<string, string> = {
|
|
418
|
+
'src/load/LoadPanel.tsx': [
|
|
419
|
+
'// src/load/LoadPanel.tsx',
|
|
420
|
+
'',
|
|
421
|
+
'export function LoadPanel() {',
|
|
422
|
+
' const rows = DrawingStore.list();',
|
|
423
|
+
' return rows.map(RenderSurface.render);',
|
|
424
|
+
'}',
|
|
425
|
+
].join('\n'),
|
|
426
|
+
'src/store/DrawingStore.ts': [
|
|
427
|
+
'// src/store/DrawingStore.ts',
|
|
428
|
+
'',
|
|
429
|
+
'export class DrawingStore {',
|
|
430
|
+
' static list() {',
|
|
431
|
+
' return RenderSurface.read();',
|
|
432
|
+
' }',
|
|
433
|
+
'}',
|
|
434
|
+
].join('\n'),
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
function readClickableFile(path: string): Promise<string> {
|
|
438
|
+
const content = clickableFiles[path];
|
|
439
|
+
if (content == null) {
|
|
440
|
+
return Promise.reject(new Error(`file not found in graph repos: ${path}`));
|
|
441
|
+
}
|
|
442
|
+
return Promise.resolve(content);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const clickableWalkthroughs: SubsystemWalkthrough[] = [
|
|
446
|
+
{
|
|
447
|
+
id: 'tl-load',
|
|
448
|
+
title: 'Load drawings',
|
|
449
|
+
steps: [
|
|
450
|
+
{
|
|
451
|
+
from: 'load-panel',
|
|
452
|
+
to: 'drawing-store-list',
|
|
453
|
+
mechanism: 'calls',
|
|
454
|
+
file: 'src/load/LoadPanel.tsx',
|
|
455
|
+
line: 4,
|
|
456
|
+
purl: stepPurl('src/load/LoadPanel.tsx'),
|
|
457
|
+
symbol: 'LoadPanel.load',
|
|
458
|
+
annotation:
|
|
459
|
+
'LoadPanel calls DrawingStore.list — click the `list` (or `DrawingStore`) token to jump to the method.',
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
from: 'drawing-store-list',
|
|
463
|
+
to: 'render-surface',
|
|
464
|
+
mechanism: 'calls',
|
|
465
|
+
file: 'src/store/DrawingStore.ts',
|
|
466
|
+
line: 5,
|
|
467
|
+
purl: stepPurl('src/store/DrawingStore.ts'),
|
|
468
|
+
symbol: 'DrawingStore.list',
|
|
469
|
+
annotation: 'list reads through RenderSurface — click RenderSurface.',
|
|
470
|
+
},
|
|
471
|
+
],
|
|
472
|
+
},
|
|
473
|
+
];
|
|
474
|
+
|
|
475
|
+
function ClickableConstructsDemo() {
|
|
476
|
+
const [opened, setOpened] = React.useState<string | null>(null);
|
|
477
|
+
const renderWalkthroughViewer = useCallback(
|
|
478
|
+
({
|
|
479
|
+
walkthrough,
|
|
480
|
+
stepIndex,
|
|
481
|
+
onOpenFile,
|
|
482
|
+
proposedAliases,
|
|
483
|
+
resolveSymbol,
|
|
484
|
+
onSymbolClick,
|
|
485
|
+
}: WalkthroughViewerContext) => (
|
|
486
|
+
<PierreWalkthroughCodeView
|
|
487
|
+
walkthrough={walkthrough}
|
|
488
|
+
stepIndex={stepIndex}
|
|
489
|
+
readFile={readClickableFile}
|
|
490
|
+
contextLines={4}
|
|
491
|
+
onOpenFile={onOpenFile}
|
|
492
|
+
proposedAliases={proposedAliases}
|
|
493
|
+
resolveSymbol={resolveSymbol}
|
|
494
|
+
onSymbolClick={onSymbolClick}
|
|
495
|
+
/>
|
|
496
|
+
),
|
|
497
|
+
[],
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
return (
|
|
501
|
+
<div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
|
502
|
+
<div
|
|
503
|
+
style={{
|
|
504
|
+
padding: '8px 14px',
|
|
505
|
+
fontFamily: 'monospace',
|
|
506
|
+
fontSize: 12,
|
|
507
|
+
borderBottom: '1px solid #333',
|
|
508
|
+
background: '#141414',
|
|
509
|
+
color: '#ddd',
|
|
510
|
+
}}
|
|
511
|
+
>
|
|
512
|
+
Click the <strong>Load drawings</strong> step, then click the dotted-underlined{' '}
|
|
513
|
+
<code>list</code> <em>(a method)</em> or <code>RenderSurface</code> token in the
|
|
514
|
+
snippet. It opens that construct's file at its declaration line in the bottom drawer
|
|
515
|
+
— <code>list</code> jumps to <code>DrawingStore.ts:4</code>.
|
|
516
|
+
<span style={{ marginLeft: 8, color: '#8fd' }}>opened: {opened ?? '—'}</span>
|
|
517
|
+
</div>
|
|
518
|
+
<div style={{ flex: 1, minHeight: 0 }}>
|
|
519
|
+
<SubsystemComponentGraph
|
|
520
|
+
components={clickableComponents}
|
|
521
|
+
relations={[]}
|
|
522
|
+
walkthroughs={clickableWalkthroughs}
|
|
523
|
+
initialWalkthroughId="tl-load"
|
|
524
|
+
title="clickable constructs"
|
|
525
|
+
description="A walkthrough step's line is an edge to a construct. Tokens that name the step's `from`/`to` components are clickable and open that construct's declaration line in the file drawer — including a **method** endpoint (click `list` to jump to `DrawingStore.list`)."
|
|
526
|
+
renderWalkthroughViewer={renderWalkthroughViewer}
|
|
527
|
+
onFileSelect={setOpened}
|
|
528
|
+
renderFileViewer={(file, opts) => (
|
|
529
|
+
<div style={{ padding: 12, fontFamily: 'monospace', fontSize: 12, color: '#bbb' }}>
|
|
530
|
+
{`// ${file}`}
|
|
531
|
+
{opts?.startLine != null ? `\n // → focus line ${opts.startLine}` : ''}
|
|
532
|
+
{'\n …'}
|
|
533
|
+
</div>
|
|
534
|
+
)}
|
|
535
|
+
/>
|
|
536
|
+
</div>
|
|
537
|
+
</div>
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
export const ClickableConstructs: Story = {
|
|
542
|
+
render: () => <ClickableConstructsDemo />,
|
|
543
|
+
};
|