@dxos/react-ui-rdf 0.0.0 → 0.11.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.
Files changed (31) hide show
  1. package/dist/lib/index.mjs +338 -0
  2. package/dist/lib/index.mjs.map +1 -0
  3. package/dist/types/src/components/FactViewer/FactViewer.d.ts +73 -0
  4. package/dist/types/src/components/FactViewer/FactViewer.d.ts.map +1 -0
  5. package/dist/types/src/{FactViewer.stories.d.ts → components/FactViewer/FactViewer.stories.d.ts} +4 -2
  6. package/dist/types/src/components/FactViewer/FactViewer.stories.d.ts.map +1 -0
  7. package/dist/types/src/components/FactViewer/FactViewer.theme.d.ts +74 -0
  8. package/dist/types/src/components/FactViewer/FactViewer.theme.d.ts.map +1 -0
  9. package/dist/types/src/components/FactViewer/index.d.ts +2 -0
  10. package/dist/types/src/components/FactViewer/index.d.ts.map +1 -0
  11. package/dist/types/src/components/index.d.ts +2 -0
  12. package/dist/types/src/components/index.d.ts.map +1 -0
  13. package/dist/types/src/index.d.ts +1 -1
  14. package/dist/types/tsconfig.tsbuildinfo +1 -1
  15. package/package.json +12 -13
  16. package/src/{FactViewer.stories.tsx → components/FactViewer/FactViewer.stories.tsx} +15 -2
  17. package/src/components/FactViewer/FactViewer.theme.ts +51 -0
  18. package/src/components/FactViewer/FactViewer.tsx +289 -0
  19. package/src/components/FactViewer/index.ts +5 -0
  20. package/src/components/index.ts +5 -0
  21. package/src/index.ts +1 -1
  22. package/dist/lib/browser/index.mjs +0 -249
  23. package/dist/lib/browser/index.mjs.map +0 -7
  24. package/dist/lib/browser/meta.json +0 -1
  25. package/dist/lib/node-esm/index.mjs +0 -251
  26. package/dist/lib/node-esm/index.mjs.map +0 -7
  27. package/dist/lib/node-esm/meta.json +0 -1
  28. package/dist/types/src/FactViewer.d.ts +0 -18
  29. package/dist/types/src/FactViewer.d.ts.map +0 -1
  30. package/dist/types/src/FactViewer.stories.d.ts.map +0 -1
  31. package/src/FactViewer.tsx +0 -161
@@ -1,161 +0,0 @@
1
- //
2
- // Copyright 2026 DXOS.org
3
- //
4
-
5
- import React, { useMemo, useState } from 'react';
6
-
7
- import { type RDF, buildFactGraph, factSourceFromFacts } from '@dxos/pipeline-rdf';
8
- import { Icon, IconButton, Input, Panel, ScrollArea, Tag, type ThemedClassName, Toolbar } from '@dxos/react-ui';
9
- import { Tree } from '@dxos/react-ui-graph';
10
- import { Empty, Listbox } from '@dxos/react-ui-list';
11
- import { mx } from '@dxos/ui-theme';
12
-
13
- import { Group, factualityColor, formatDate, formatTerm, graphToTreeNode, groupFacts, termKey } from './types';
14
-
15
- type View = 'list' | 'graph';
16
-
17
- export type FactViewerProps = ThemedClassName<{
18
- facts: RDF.Fact[];
19
- /** Context entity id; scopes the list and roots the graph. */
20
- context?: string;
21
- /** Predicate filter; when set, only facts with this predicate are shown. */
22
- predicate?: string;
23
- }>;
24
-
25
- /**
26
- * Viewer for extracted semantic facts with two views: a grouped **list** (by subject entity, with
27
- * conflicts highlighted) and a **graph** (tidy tree rooted at the context entity, exploring the fact
28
- * graph). A `context` entity scopes the list and roots the graph; a `predicate` further filters the
29
- * list. Pure/presentational.
30
- */
31
- export const FactViewer = ({ classNames, facts, context, predicate }: FactViewerProps) => {
32
- const [filter, setFilter] = useState('');
33
- const [view, setView] = useState<View>('list');
34
- const scoped = useMemo(
35
- () =>
36
- facts.filter(
37
- (fact) =>
38
- (context == null ||
39
- termKey(fact.assertion.subject) === context ||
40
- termKey(fact.assertion.object) === context) &&
41
- (predicate == null || fact.assertion.predicate === predicate),
42
- ),
43
- [facts, context, predicate],
44
- );
45
- const groups = useMemo(() => groupFacts(scoped, filter), [scoped, filter]);
46
- const graph = useMemo(
47
- () => (context == null ? undefined : graphToTreeNode(buildFactGraph(context, factSourceFromFacts(facts)), context)),
48
- [facts, context],
49
- );
50
-
51
- return (
52
- <Panel.Root classNames={classNames}>
53
- <Panel.Toolbar asChild>
54
- <Toolbar.Root>
55
- <Input.Root>
56
- <Input.Label srOnly>Filter facts</Input.Label>
57
- <Input.TextInput
58
- placeholder='Filter by entity or predicate…'
59
- value={filter}
60
- onChange={(event) => setFilter(event.target.value)}
61
- />
62
- </Input.Root>
63
- <div role='none' className='grow' />
64
- <IconButton
65
- icon='ph--list--regular'
66
- iconOnly
67
- label='List view'
68
- variant={view === 'list' ? 'primary' : 'default'}
69
- onClick={() => setView('list')}
70
- />
71
- <IconButton
72
- icon='ph--graph--regular'
73
- iconOnly
74
- label='Graph view'
75
- variant={view === 'graph' ? 'primary' : 'default'}
76
- onClick={() => setView('graph')}
77
- />
78
- </Toolbar.Root>
79
- </Panel.Toolbar>
80
- {view === 'list' ? (
81
- <Panel.Content asChild>
82
- <ScrollArea.Root padding>
83
- <ScrollArea.Viewport classNames='flex flex-col gap-2 py-1'>
84
- {groups.length === 0 && <Empty label='No facts.' />}
85
- {groups.map((group) => (
86
- <SubjectGroup key={group.subject} group={group} />
87
- ))}
88
- </ScrollArea.Viewport>
89
- </ScrollArea.Root>
90
- </Panel.Content>
91
- ) : (
92
- <Panel.Content classNames='overflow-hidden'>
93
- {graph ? (
94
- <Tree data={graph} variant='tidy' margin={80} classNames='w-full h-full' />
95
- ) : (
96
- <Empty icon='ph--graph--regular' label='Select an entity to root the graph.' />
97
- )}
98
- </Panel.Content>
99
- )}
100
- </Panel.Root>
101
- );
102
- };
103
-
104
- const SubjectGroup = ({ group }: { group: Group }) => (
105
- <div className='shrink-0 flex flex-col bg-card-surface border border-subdued-separator rounded-sm overflow-hidden'>
106
- <div className='flex px-3 py-1 items-center justify-between'>
107
- <h3>{group.subject}</h3>
108
- {group.conflicted && (
109
- <Tag hue='warning'>
110
- <span className='flex items-center gap-1'>
111
- <Icon icon='ph--warning--regular' size={3} />
112
- conflict
113
- </span>
114
- </Tag>
115
- )}
116
- </div>
117
- <Listbox.Root>
118
- <Listbox.Content aria-label={group.subject}>
119
- {group.facts.map((fact) => (
120
- <FactRow key={fact.id} fact={fact} conflicting={group.conflictedIds.has(fact.id)} />
121
- ))}
122
- </Listbox.Content>
123
- </Listbox.Root>
124
- </div>
125
- );
126
-
127
- const cellClassNames =
128
- 'bg-input-surface border border-subdued-separator rounded-sm px-2 py-0.5 font-medium whitespace-nowrap truncate';
129
-
130
- const FactRow = ({ fact, conflicting }: { fact: RDF.Fact; conflicting: boolean }) => {
131
- const { assertion, factuality, attribution } = fact;
132
- return (
133
- <Listbox.Item
134
- id={fact.id}
135
- classNames={mx('flex flex-col items-stretch gap-2 px-3 py-1', conflicting && 'border-is-2 border-warning-border')}
136
- >
137
- {assertion.quote && <div className='text-sm text-description italic'>"{assertion.quote}"</div>}
138
-
139
- <div className='grid grid-cols-[1fr_5rem] items-center justify-between gap-2'>
140
- <div className='w-full grid grid-cols-[1fr_1rem_1fr_1rem_1fr] items-center flex-wrap'>
141
- <span className={mx(cellClassNames, 'text-right')}>{formatTerm(assertion.subject)}</span>
142
- <span className='border border-subdued-separator'></span>
143
- <span className={mx(cellClassNames, 'text-center text-description')}>{assertion.predicate}</span>
144
- <span className='border border-subdued-separator'></span>
145
- <span className={mx(cellClassNames)}>{formatTerm(assertion.object)}</span>
146
- </div>
147
-
148
- <div className='flex items-center justify-end gap-2 shrink-0'>
149
- {factuality.confidence != null && (
150
- <span className='text-xs text-subdued'>{Math.round(factuality.confidence * 100)}%</span>
151
- )}
152
- <Tag hue={factualityColor(factuality.value)}>{factuality.value}</Tag>
153
- </div>
154
- </div>
155
-
156
- <div className='text-xs text-subdued text-right'>
157
- {[attribution.agent, attribution.source, formatDate(attribution.generatedAtTime)].filter(Boolean).join(' · ')}
158
- </div>
159
- </Listbox.Item>
160
- );
161
- };