@dxos/plugin-preview 0.9.0 → 0.9.1-staging.ee54ba693a

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 (51) hide show
  1. package/dist/lib/neutral/capabilities/index.mjs +2 -2
  2. package/dist/lib/neutral/chunk-SXDHWUBO.mjs +32 -0
  3. package/dist/lib/neutral/chunk-SXDHWUBO.mjs.map +7 -0
  4. package/dist/lib/neutral/index.mjs +1 -1
  5. package/dist/lib/neutral/meta.json +1 -1
  6. package/dist/lib/neutral/meta.mjs +1 -1
  7. package/dist/lib/neutral/plugin.mjs +1 -1
  8. package/dist/lib/neutral/{preview-popover-FLMYMFCH.mjs → preview-popover-EEQQJAH5.mjs} +16 -9
  9. package/dist/lib/neutral/preview-popover-EEQQJAH5.mjs.map +7 -0
  10. package/dist/lib/neutral/{react-surface-FE7E2JP7.mjs → react-surface-2CN3DRU6.mjs} +79 -65
  11. package/dist/lib/neutral/react-surface-2CN3DRU6.mjs.map +7 -0
  12. package/dist/lib/neutral/translations.mjs +1 -1
  13. package/dist/lib/neutral/translations.mjs.map +3 -3
  14. package/dist/types/dx.config.d.ts +28 -0
  15. package/dist/types/dx.config.d.ts.map +1 -0
  16. package/dist/types/src/capabilities/index.d.ts +2 -2
  17. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  18. package/dist/types/src/capabilities/preview-popover.d.ts +1 -1
  19. package/dist/types/src/capabilities/react-surface.d.ts +2 -2
  20. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  21. package/dist/types/src/cards/FormCard.d.ts +2 -2
  22. package/dist/types/src/cards/FormCard.d.ts.map +1 -1
  23. package/dist/types/src/cards/JsonCard.d.ts.map +1 -1
  24. package/dist/types/src/cards/OrganizationCard.d.ts.map +1 -1
  25. package/dist/types/src/cards/PersonCard.d.ts.map +1 -1
  26. package/dist/types/src/cards/ProjectCard.d.ts.map +1 -1
  27. package/dist/types/src/meta.d.ts +32 -2
  28. package/dist/types/src/meta.d.ts.map +1 -1
  29. package/dist/types/src/stories/Card.stories.d.ts +4 -6
  30. package/dist/types/src/stories/Card.stories.d.ts.map +1 -1
  31. package/dist/types/src/stories/testing.d.ts +5 -5
  32. package/dist/types/src/stories/testing.d.ts.map +1 -1
  33. package/dist/types/tsconfig.tsbuildinfo +1 -1
  34. package/dx.config.ts +21 -0
  35. package/package.json +32 -30
  36. package/src/PreviewPlugin.test.ts +1 -1
  37. package/src/capabilities/preview-popover.ts +15 -13
  38. package/src/capabilities/react-surface.tsx +23 -25
  39. package/src/cards/FormCard.tsx +18 -15
  40. package/src/cards/JsonCard.tsx +23 -8
  41. package/src/cards/OrganizationCard.tsx +1 -5
  42. package/src/cards/PersonCard.tsx +26 -21
  43. package/src/cards/ProjectCard.tsx +5 -1
  44. package/src/meta.ts +3 -14
  45. package/src/stories/Card.stories.tsx +16 -35
  46. package/src/stories/testing.tsx +7 -2
  47. package/src/translations.ts +1 -1
  48. package/dist/lib/neutral/chunk-QOM5APM7.mjs +0 -23
  49. package/dist/lib/neutral/chunk-QOM5APM7.mjs.map +0 -7
  50. package/dist/lib/neutral/preview-popover-FLMYMFCH.mjs.map +0 -7
  51. package/dist/lib/neutral/react-surface-FE7E2JP7.mjs.map +0 -7
package/dx.config.ts ADDED
@@ -0,0 +1,21 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Config2 } from '@dxos/app-framework/config';
6
+ import { trim } from '@dxos/util';
7
+
8
+ export default Config2.make({
9
+ plugin: {
10
+ key: 'org.dxos.plugin.preview',
11
+ name: 'Preview',
12
+ author: 'DXOS',
13
+ description: trim`
14
+ Rich preview panel for viewing object content without opening full editors.
15
+ Quick peek at documents, images, and data with inline rendering.
16
+ `,
17
+ icon: { key: 'ph--eye--regular' },
18
+ source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-preview',
19
+ tags: ['system'],
20
+ },
21
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-preview",
3
- "version": "0.9.0",
3
+ "version": "0.9.1-staging.ee54ba693a",
4
4
  "description": "Preview plugin",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -65,43 +65,45 @@
65
65
  "types": "dist/types/src/index.d.ts",
66
66
  "files": [
67
67
  "dist",
68
+ "dx.config.ts",
68
69
  "src"
69
70
  ],
70
71
  "dependencies": {
71
- "@dxos/app-framework": "0.9.0",
72
- "@dxos/app-toolkit": "0.9.0",
73
- "@dxos/client": "0.9.0",
74
- "@dxos/echo": "0.9.0",
75
- "@dxos/echo-react": "0.9.0",
76
- "@dxos/keys": "0.9.0",
77
- "@dxos/log": "0.9.0",
78
- "@dxos/plugin-client": "0.9.0",
79
- "@dxos/react-ui": "0.9.0",
80
- "@dxos/react-ui-form": "0.9.0",
81
- "@dxos/effect": "0.9.0",
82
- "@dxos/react-ui-mosaic": "0.9.0",
83
- "@dxos/react-ui-syntax-highlighter": "0.9.0",
84
- "@dxos/schema": "0.9.0",
85
- "@dxos/types": "0.9.0",
86
- "@dxos/ui-editor": "0.9.0",
87
- "@dxos/util": "0.9.0",
88
- "@dxos/async": "0.9.0"
72
+ "@dxos/app-toolkit": "0.9.1-staging.ee54ba693a",
73
+ "@dxos/app-framework": "0.9.1-staging.ee54ba693a",
74
+ "@dxos/async": "0.9.1-staging.ee54ba693a",
75
+ "@dxos/echo": "0.9.1-staging.ee54ba693a",
76
+ "@dxos/echo-react": "0.9.1-staging.ee54ba693a",
77
+ "@dxos/effect": "0.9.1-staging.ee54ba693a",
78
+ "@dxos/keys": "0.9.1-staging.ee54ba693a",
79
+ "@dxos/plugin-client": "0.9.1-staging.ee54ba693a",
80
+ "@dxos/log": "0.9.1-staging.ee54ba693a",
81
+ "@dxos/react-ui": "0.9.1-staging.ee54ba693a",
82
+ "@dxos/react-ui-form": "0.9.1-staging.ee54ba693a",
83
+ "@dxos/client": "0.9.1-staging.ee54ba693a",
84
+ "@dxos/react-ui-mosaic": "0.9.1-staging.ee54ba693a",
85
+ "@dxos/react-ui-syntax-highlighter": "0.9.1-staging.ee54ba693a",
86
+ "@dxos/schema": "0.9.1-staging.ee54ba693a",
87
+ "@dxos/types": "0.9.1-staging.ee54ba693a",
88
+ "@dxos/ui-types": "0.9.1-staging.ee54ba693a",
89
+ "@dxos/ui-editor": "0.9.1-staging.ee54ba693a",
90
+ "@dxos/util": "0.9.1-staging.ee54ba693a"
89
91
  },
90
92
  "devDependencies": {
91
- "effect": "3.21.3",
92
- "react": "~19.2.3",
93
- "react-dom": "~19.2.3",
93
+ "effect": "3.21.4",
94
+ "react": "~19.2.7",
95
+ "react-dom": "~19.2.7",
94
96
  "vite": "^8.0.16",
95
- "@dxos/echo-react": "0.9.0",
96
- "@dxos/plugin-testing": "0.9.0",
97
- "@dxos/random": "0.9.0",
98
- "@dxos/storybook-utils": "0.9.0",
99
- "@dxos/react-ui-components": "0.9.0"
97
+ "@dxos/plugin-testing": "0.9.1-staging.ee54ba693a",
98
+ "@dxos/echo-react": "0.9.1-staging.ee54ba693a",
99
+ "@dxos/random": "0.9.1-staging.ee54ba693a",
100
+ "@dxos/react-ui-components": "0.9.1-staging.ee54ba693a",
101
+ "@dxos/storybook-utils": "0.9.1-staging.ee54ba693a"
100
102
  },
101
103
  "peerDependencies": {
102
- "effect": "3.21.3",
103
- "react": "~19.2.3",
104
- "react-dom": "~19.2.3"
104
+ "effect": "3.21.4",
105
+ "react": "~19.2.7",
106
+ "react-dom": "~19.2.7"
105
107
  },
106
108
  "publishConfig": {
107
109
  "access": "public"
@@ -11,7 +11,7 @@ import { PreviewPlugin } from '#plugin';
11
11
 
12
12
  import { meta } from './meta';
13
13
 
14
- const moduleId = (name: string) => `${meta.id}.module.${name}`;
14
+ const moduleId = (name: string) => `${meta.profile.key}.module.${name}`;
15
15
 
16
16
  describe('PreviewPlugin', () => {
17
17
  test('modules activate on the expected events', { timeout: 10_000 }, async ({ expect }) => {
@@ -5,27 +5,29 @@
5
5
  import * as Effect from 'effect/Effect';
6
6
 
7
7
  import { Capabilities, Capability } from '@dxos/app-framework';
8
- import { AppCapabilities, LayoutOperation, getPersonalSpace, getSpaceIdFromPath } from '@dxos/app-toolkit';
8
+ import { AppCapabilities, AppSpace, LayoutOperation, Paths } from '@dxos/app-toolkit';
9
9
  import { addEventListener } from '@dxos/async';
10
- import { type Client } from '@dxos/client';
11
10
  import { type Space } from '@dxos/client/echo';
12
11
  import { Obj } from '@dxos/echo';
13
12
  import { EID } from '@dxos/keys';
14
13
  import { log } from '@dxos/log';
15
14
  import { ClientCapabilities } from '@dxos/plugin-client';
16
15
  import { DX_ANCHOR_ACTIVATE, type DxAnchorActivate } from '@dxos/react-ui';
17
- import { type PreviewLinkRef, type PreviewLinkTarget } from '@dxos/ui-editor';
16
+ import { type PreviewLinkRef, type PreviewLinkTarget } from '@dxos/ui-types';
18
17
 
19
18
  const customEventOptions = { capture: true, passive: false };
20
19
 
21
- const handlePreviewLookup = async (
22
- client: Client,
23
- defaultSpace: Space,
24
- { dxn, label }: PreviewLinkRef,
25
- ): Promise<PreviewLinkTarget | null> => {
20
+ // TODO(burdon): Factor out?
21
+ const handlePreviewLookup = async (space: Space, { dxn, label }: PreviewLinkRef): Promise<PreviewLinkTarget | null> => {
22
+ const eid = EID.tryParse(dxn);
23
+ if (!eid) {
24
+ // dxn: type URIs and other non-EID refs cannot be resolved to an object.
25
+ return null;
26
+ }
26
27
  try {
27
- const object = await defaultSpace.db.makeRef(EID.parse(dxn)).load();
28
- return { label, object };
28
+ const object = await space.db.makeRef(eid).load();
29
+ const resolvedLabel = Obj.getLabel(object as any, { fallback: 'typename' });
30
+ return { label: resolvedLabel ?? label, object };
29
31
  } catch {
30
32
  return null;
31
33
  }
@@ -74,12 +76,12 @@ export default Capability.makeModule(
74
76
  // would crash inside Atom's identity check (`'~atom/Serializable' in undefined`). When layout
75
77
  // isn't available, fall through to the personal-space default.
76
78
  const [layoutAtom] = capabilities.getAll(AppCapabilities.Layout);
77
- const spaceId = layoutAtom && getSpaceIdFromPath(registry.get(layoutAtom).workspace);
78
- const space = (spaceId && client.spaces.get(spaceId)) ?? getPersonalSpace(client);
79
+ const spaceId = layoutAtom && Paths.getSpaceIdFromPath(registry.get(layoutAtom).workspace);
80
+ const space = (spaceId && client.spaces.get(spaceId)) ?? AppSpace.getPersonalSpace(client);
79
81
  if (!space) {
80
82
  return;
81
83
  }
82
- const result = await handlePreviewLookup(client, space, { dxn, label });
84
+ const result = await handlePreviewLookup(space, { dxn, label });
83
85
  if (!result) {
84
86
  return;
85
87
  }
@@ -10,8 +10,9 @@ import { Surface } from '@dxos/app-framework/ui';
10
10
  import { AppSurface } from '@dxos/app-toolkit/ui';
11
11
  import { Obj, Type } from '@dxos/echo';
12
12
  import { Card } from '@dxos/react-ui';
13
- import { Expando, type ProjectionModel } from '@dxos/schema';
13
+ import { Expando } from '@dxos/schema';
14
14
  import { Organization, Person, Pipeline, Task } from '@dxos/types';
15
+ import { Position } from '@dxos/util';
15
16
 
16
17
  import { ExpandoCard, FormCard, JsonCard, OrganizationCard, PersonCard, ProjectCard, TaskCard } from '../cards';
17
18
 
@@ -25,8 +26,8 @@ export default Capability.makeModule(() =>
25
26
 
26
27
  Surface.create<{ subject: Person.Person }>({
27
28
  id: 'schemaPopoverContact',
28
- position: 'first',
29
- filter: AppSurface.object(AppSurface.Card, Person.Person),
29
+ position: Position.first,
30
+ filter: AppSurface.object(AppSurface.CardContent, Person.Person),
30
31
  component: ({ data, role }) => {
31
32
  return (
32
33
  <>
@@ -38,8 +39,8 @@ export default Capability.makeModule(() =>
38
39
  }),
39
40
  Surface.create({
40
41
  id: 'schemaPopoverOrganization',
41
- position: 'first',
42
- filter: AppSurface.object(AppSurface.Card, Organization.Organization),
42
+ position: Position.first,
43
+ filter: AppSurface.object(AppSurface.CardContent, Organization.Organization),
43
44
  component: ({ data, role }) => {
44
45
  return (
45
46
  <>
@@ -51,23 +52,23 @@ export default Capability.makeModule(() =>
51
52
  }),
52
53
  Surface.create({
53
54
  id: 'schemaPopoverProject',
54
- position: 'first',
55
- filter: AppSurface.object(AppSurface.Card, Pipeline.Pipeline),
55
+ position: Position.first,
56
+ filter: AppSurface.object(AppSurface.CardContent, Pipeline.Pipeline),
56
57
  component: ({ data, role }) => {
57
58
  return <ProjectCard role={role} subject={data.subject} />;
58
59
  },
59
60
  }),
60
61
  Surface.create({
61
62
  id: 'schemaPopoverTask',
62
- position: 'first',
63
- filter: AppSurface.object(AppSurface.Card, Task.Task),
63
+ position: Position.first,
64
+ filter: AppSurface.object(AppSurface.CardContent, Task.Task),
64
65
  component: ({ data, role }) => {
65
66
  return <TaskCard role={role} subject={data.subject} />;
66
67
  },
67
68
  }),
68
69
  Surface.create<AppSurface.ObjectCardData<Expando.Expando>>({
69
70
  id: 'schemaPopoverExpando',
70
- filter: AppSurface.object(AppSurface.Card, Expando.Expando),
71
+ filter: AppSurface.object(AppSurface.CardContent, Expando.Expando),
71
72
  component: ({ data, role }) => {
72
73
  return <ExpandoCard role={role} subject={data.subject} ignorePaths={data.ignorePaths} />;
73
74
  },
@@ -75,19 +76,18 @@ export default Capability.makeModule(() =>
75
76
 
76
77
  Surface.create({
77
78
  id: 'schemaPopoverDynamicType',
78
- role: 'card--content',
79
- filter: (data): data is { subject: Obj.Unknown } => {
80
- if (!Obj.isObject(data.subject)) {
79
+ filter: AppSurface.subject(AppSurface.CardContent, (subject): subject is Obj.Unknown => {
80
+ if (!Obj.isObject(subject)) {
81
81
  return false;
82
82
  }
83
- const type = Obj.getType(data.subject);
83
+ const type = Obj.getType(subject);
84
84
  if (type) {
85
85
  return Type.getDatabase(type) != null;
86
86
  }
87
87
  // Obj.getType fails for database-registered schemas (DXN mismatch); fall back to typename query.
88
88
  try {
89
- const db = Obj.getDatabase(data.subject);
90
- const typename = Obj.getTypename(data.subject);
89
+ const db = Obj.getDatabase(subject);
90
+ const typename = Obj.getTypename(subject);
91
91
  return (
92
92
  !!db &&
93
93
  !!typename &&
@@ -99,7 +99,7 @@ export default Capability.makeModule(() =>
99
99
  } catch {
100
100
  return false;
101
101
  }
102
- },
102
+ }),
103
103
  component: ({ data, role }) => {
104
104
  // Dynamic/mutable schemas render an editable, full-layout form;
105
105
  // FormCard handles both static and runtime schema resolution internally.
@@ -113,9 +113,8 @@ export default Capability.makeModule(() =>
113
113
 
114
114
  Surface.create({
115
115
  id: 'fallbackPopover',
116
- role: 'card--content',
117
- position: 'last',
118
- filter: (data): data is { subject: Obj.Unknown; projection?: ProjectionModel } => Obj.isObject(data.subject),
116
+ position: Position.last,
117
+ filter: AppSurface.subject(AppSurface.CardContent, Obj.isObject),
119
118
  component: ({ data, role }) => {
120
119
  return <FormCard role={role} subject={data.subject} projection={data.projection} />;
121
120
  },
@@ -123,9 +122,8 @@ export default Capability.makeModule(() =>
123
122
 
124
123
  Surface.create({
125
124
  id: 'fallbackJson',
126
- role: 'card--content',
127
- position: 'last',
128
- filter: (data): data is Record<string, unknown> => true,
125
+ filter: Surface.makeFilter(AppSurface.CardContent),
126
+ position: Position.last,
129
127
  component: ({ data }) => {
130
128
  return <JsonCard data={data} />;
131
129
  },
@@ -133,14 +131,14 @@ export default Capability.makeModule(() =>
133
131
 
134
132
  Surface.create({
135
133
  id: 'section',
136
- position: 'last',
134
+ position: Position.last,
137
135
  filter: AppSurface.subject(AppSurface.Section, Obj.isObject),
138
136
  component: ({ data }) => {
139
137
  return (
140
138
  <div className='flex w-full justify-center'>
141
139
  <div className='py-2 dx-card-min-width dx-card-max-width'>
142
140
  <Card.Root>
143
- <Surface.Surface type={AppSurface.Card} data={data} limit={1} />
141
+ <Surface.Surface type={AppSurface.CardContent} data={data} limit={1} />
144
142
  </Card.Root>
145
143
  </div>
146
144
  </div>
@@ -10,8 +10,8 @@ import { Obj, Type } from '@dxos/echo';
10
10
  import { useType } from '@dxos/echo-react';
11
11
  import { type AnyProperties } from '@dxos/echo/internal';
12
12
  import { SchemaEx } from '@dxos/effect';
13
- import { Card, Icon, useTranslation } from '@dxos/react-ui';
14
- import { Form, type FormUpdateMeta, type Presentation, getFormProperties, omitId } from '@dxos/react-ui-form';
13
+ import { Card, useTranslation } from '@dxos/react-ui';
14
+ import { Form, type FormPresentation, type FormUpdateMeta, getFormProperties, omitId } from '@dxos/react-ui-form';
15
15
  import { type ProjectionModel } from '@dxos/schema';
16
16
 
17
17
  import { meta } from '#meta';
@@ -19,7 +19,7 @@ import { meta } from '#meta';
19
19
  export type FormCardProps = AppSurface.ObjectCardProps & {
20
20
  projection?: ProjectionModel;
21
21
  readonly?: boolean;
22
- layout?: Presentation;
22
+ layout?: FormPresentation;
23
23
  };
24
24
 
25
25
  /**
@@ -29,18 +29,29 @@ export type FormCardProps = AppSurface.ObjectCardProps & {
29
29
  * schema looked up via `useType`.
30
30
  */
31
31
  export const FormCard = ({ subject, projection, readonly = true, layout }: FormCardProps) => {
32
- const { t } = useTranslation(meta.id);
32
+ const { t } = useTranslation(meta.profile.key);
33
33
  // Readonly cards default to the `static` presentation — plain DOM, undefined values
34
34
  // omitted — which reads as a preview rather than a form. Editable cards keep the
35
35
  // `compact` form layout. Callers can override either via the explicit `layout` prop.
36
- const resolvedLayout: Presentation = layout ?? (readonly ? 'static' : 'compact');
36
+ const resolvedLayout: FormPresentation = layout ?? (readonly ? 'static' : 'compact');
37
37
 
38
38
  // Try the static schema first; fall back to the runtime/database schema for
39
39
  // dynamic types whose schema isn't reachable via `Obj.getSchema` (DXN mismatch).
40
40
  const staticType = Obj.getType(subject);
41
41
  const db = Obj.getDatabase(subject);
42
- // Obj.getTypeURI throws for corrupted objects; only evaluate it on the fallback path.
43
- const runtimeType = useType(db, staticType ? undefined : Obj.getTypeURI(subject));
42
+ // `Obj.getTypeURI` throws on corrupted objects that are missing a type; swallow that
43
+ // and fall through to the "unable to create preview" path rather than crashing the card.
44
+ const fallbackTypeUri = useMemo(() => {
45
+ if (staticType) {
46
+ return undefined;
47
+ }
48
+ try {
49
+ return Obj.getTypeURI(subject);
50
+ } catch {
51
+ return undefined;
52
+ }
53
+ }, [staticType, subject]);
54
+ const runtimeType = useType(db, fallbackTypeUri);
44
55
  const schema = useMemo((): Schema.Schema.AnyNoContext | undefined => {
45
56
  const resolvedType = runtimeType ?? staticType;
46
57
  return resolvedType ? omitId(Type.getSchema(resolvedType)) : undefined;
@@ -112,14 +123,6 @@ export const FormCard = ({ subject, projection, readonly = true, layout }: FormC
112
123
  </Form.Content>
113
124
  </Form.Viewport>
114
125
  </Form.Root>
115
- <Card.Row fullWidth>
116
- <div className='pt-1'>
117
- <div {...{ 'data-hue': hue }} className='inline-flex items-center gap-1 dx-tag'>
118
- <Icon icon={icon} />
119
- <span>{Obj.getTypename(subject)}</span>
120
- </div>
121
- </div>
122
- </Card.Row>
123
126
  </Card.Body>
124
127
  );
125
128
  };
@@ -2,18 +2,33 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React from 'react';
5
+ import React, { useState } from 'react';
6
6
 
7
- import { Card } from '@dxos/react-ui';
7
+ import { Card, ToggleIconButton } from '@dxos/react-ui';
8
8
  import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
9
9
 
10
10
  export const JsonCard = ({ data }: { data: unknown }) => {
11
+ const [open, setOpen] = useState(false);
12
+ // JSON.stringify throws on circular references / unsupported values (e.g. BigInt); keep the card stable.
13
+ let collapsedLength = 0;
14
+ try {
15
+ collapsedLength = JSON.stringify(data)?.length ?? 0;
16
+ } catch {}
11
17
  return (
12
- <Card.Body>
13
- <span />
14
- {/* TODO(wittjosiah): Span whole row. */}
15
- <JsonHighlighter data={data} />
16
- <span />
17
- </Card.Body>
18
+ <Card.Row>
19
+ <Card.Block>
20
+ <ToggleIconButton
21
+ variant='ghost'
22
+ icon='ph--caret-right--regular'
23
+ iconOnly
24
+ active={open}
25
+ onClick={() => setOpen(!open)}
26
+ label='Toggle JSON'
27
+ />
28
+ </Card.Block>
29
+ {(open && <JsonHighlighter data={data} classNames='py-1.5 col-span-full text-xs overflow-auto' />) || (
30
+ <Card.Text variant='description'>{collapsedLength}</Card.Text>
31
+ )}
32
+ </Card.Row>
18
33
  );
19
34
  };
@@ -13,11 +13,7 @@ export const OrganizationCard = ({ subject }: AppSurface.ObjectCardProps<Organiz
13
13
 
14
14
  return (
15
15
  <Card.Body>
16
- <Card.Poster
17
- alt={name ?? ''}
18
- {...(image ? { image } : { icon: 'ph--building-office--regular' })}
19
- classNames={!image && 'opacity-50'}
20
- />
16
+ {image && <Card.Poster alt={name ?? ''} image={image} />}
21
17
  {description && (
22
18
  <Card.Row>
23
19
  <Card.Text variant='description'>{description}</Card.Text>
@@ -6,12 +6,12 @@ import * as Effect from 'effect/Effect';
6
6
  import React, { useCallback } from 'react';
7
7
 
8
8
  import { useOperationInvoker } from '@dxos/app-framework/ui';
9
- import { LayoutOperation, getObjectPathFromObject, getSpacePath } from '@dxos/app-toolkit';
9
+ import { LayoutOperation, Paths } from '@dxos/app-toolkit';
10
10
  import { type AppSurface } from '@dxos/app-toolkit/ui';
11
11
  import { Obj } from '@dxos/echo';
12
12
  import { EffectEx } from '@dxos/effect';
13
13
  import { Avatar } from '@dxos/react-ui';
14
- import { Card } from '@dxos/react-ui';
14
+ import { Card, Icon } from '@dxos/react-ui';
15
15
  import { type Person } from '@dxos/types';
16
16
 
17
17
  export const PersonCard = ({ subject }: AppSurface.ObjectCardProps<Person.Person>) => {
@@ -24,42 +24,47 @@ export const PersonCard = ({ subject }: AppSurface.ObjectCardProps<Person.Person
24
24
  }
25
25
 
26
26
  return Effect.gen(function* () {
27
- const organizationPath = getObjectPathFromObject(organization);
27
+ const organizationPath = Paths.getObjectPathFromObject(organization);
28
28
  const db = Obj.getDatabase(organization);
29
29
  yield* invoke(LayoutOperation.UpdatePopover, { state: false, anchorId: '' });
30
30
  yield* invoke(LayoutOperation.Open, {
31
31
  subject: [organizationPath],
32
- workspace: db ? getSpacePath(db.spaceId) : undefined,
32
+ workspace: db ? Paths.getSpacePath(db.spaceId) : undefined,
33
33
  });
34
34
  }).pipe(EffectEx.runAndForwardErrors);
35
35
  }, [invoke, organization]);
36
36
 
37
37
  return (
38
38
  <Card.Body>
39
- <Avatar.Root>
40
- {image && (
41
- <Card.Row className='py-1'>
39
+ {image && (
40
+ <Card.Row>
41
+ <Avatar.Root>
42
42
  <Avatar.Content
43
43
  imgSrc={image}
44
44
  icon='ph--user--regular'
45
45
  size={20}
46
- classNames={!image && 'opacity-50'}
46
+ classNames={[!image && 'opacity-50']}
47
47
  hue='neutral'
48
48
  variant='square'
49
49
  />
50
- </Card.Row>
51
- )}
52
- {organization?.name && (
53
- <Card.Action icon='ph--buildings--regular' label={organization.name} onClick={handleOrganizationClick} />
54
- )}
55
- {emails.map(({ value }) => (
56
- <Card.Row key={value} icon='ph--at--regular'>
57
- <Card.Text truncate className='text-primary-text'>
58
- {value}
59
- </Card.Text>
60
- </Card.Row>
61
- ))}
62
- </Avatar.Root>
50
+ </Avatar.Root>
51
+ </Card.Row>
52
+ )}
53
+ {organization?.name && (
54
+ <Card.Action icon='ph--buildings--regular' label={organization.name} onClick={handleOrganizationClick} />
55
+ )}
56
+ {emails.length > 0 && (
57
+ <Card.Row>
58
+ <Card.Block>
59
+ <Icon icon='ph--at--regular' />
60
+ </Card.Block>
61
+ <Card.Text truncate className='text-sky-text text-sm'>
62
+ {emails.map(({ value }) => (
63
+ <div key={value}>{value}</div>
64
+ ))}
65
+ </Card.Text>
66
+ </Card.Row>
67
+ )}
63
68
  </Card.Body>
64
69
  );
65
70
  };
@@ -16,7 +16,11 @@ export const ProjectCard = ({ subject }: AppSurface.ObjectCardProps<Pipeline.Pip
16
16
  <Card.Body>
17
17
  {image && <Card.Poster image={image} alt={Obj.getLabel(subject) ?? ''} aspect='auto' />}
18
18
  {/* <CardHeader label={name} subject={subject} db={db} /> */}
19
- {description && <Card.Text variant='description'>{description}</Card.Text>}
19
+ {description && (
20
+ <Card.Row>
21
+ <Card.Text variant='description'>{description}</Card.Text>
22
+ </Card.Row>
23
+ )}
20
24
  </Card.Body>
21
25
  );
22
26
  };
package/src/meta.ts CHANGED
@@ -3,18 +3,7 @@
3
3
  //
4
4
 
5
5
  import { Plugin } from '@dxos/app-framework';
6
- import { DXN } from '@dxos/keys';
7
- import { trim } from '@dxos/util';
8
6
 
9
- export const meta = Plugin.makeMeta({
10
- key: DXN.make('org.dxos.plugin.preview'),
11
- name: 'Preview',
12
- author: 'DXOS',
13
- description: trim`
14
- Rich preview panel for viewing object content without opening full editors.
15
- Quick peek at documents, images, and data with inline rendering.
16
- `,
17
- icon: 'ph--eye--regular',
18
- source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-preview',
19
- tags: ['system'],
20
- });
7
+ import config from '../dx.config';
8
+
9
+ export const meta = Plugin.getMetaFromConfig(config);
@@ -3,19 +3,17 @@
3
3
  //
4
4
 
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
- import React from 'react';
7
6
 
8
7
  import { withPluginManager } from '@dxos/app-framework/testing';
9
8
  import { corePlugins } from '@dxos/plugin-testing';
10
9
  import { random } from '@dxos/random';
11
- import { Card } from '@dxos/react-ui';
12
10
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
13
11
  import { type Expando } from '@dxos/schema';
14
12
  import { type Organization, type Person, type Pipeline, type Task } from '@dxos/types';
15
13
 
16
14
  import { translations } from '#translations';
17
15
 
18
- import { ExpandoCard, FormCard, JsonCard, OrganizationCard, PersonCard, ProjectCard, TaskCard } from '../cards';
16
+ import { ExpandoCard, FormCard, OrganizationCard, PersonCard, ProjectCard, TaskCard } from '../cards';
19
17
  import {
20
18
  DefaultStory,
21
19
  createExpando,
@@ -114,6 +112,13 @@ export const _Person: StoryObj<typeof DefaultStory<Person.Person>> = {
114
112
  },
115
113
  };
116
114
 
115
+ export const _PersonNoImage: StoryObj<typeof DefaultStory<Person.Person>> = {
116
+ args: {
117
+ Component: PersonCard,
118
+ createObject: createPerson,
119
+ },
120
+ };
121
+
117
122
  export const _Organization: StoryObj<typeof DefaultStory<Organization.Organization>> = {
118
123
  args: {
119
124
  Component: OrganizationCard,
@@ -122,7 +127,14 @@ export const _Organization: StoryObj<typeof DefaultStory<Organization.Organizati
122
127
  },
123
128
  };
124
129
 
125
- export const _Project: StoryObj<typeof DefaultStory<Pipeline.Pipeline>> = {
130
+ export const _OrganizationNoImage: StoryObj<typeof DefaultStory<Organization.Organization>> = {
131
+ args: {
132
+ Component: OrganizationCard,
133
+ createObject: createOrganization,
134
+ },
135
+ };
136
+
137
+ export const _Pipeline: StoryObj<typeof DefaultStory<Pipeline.Pipeline>> = {
126
138
  args: {
127
139
  Component: ProjectCard,
128
140
  createObject: createProject,
@@ -144,34 +156,3 @@ export const _Expando: StoryObj<typeof DefaultStory<Expando.Expando>> = {
144
156
  createObject: createExpando,
145
157
  },
146
158
  };
147
-
148
- export const _Json = {
149
- render: () => {
150
- const data = {
151
- subject: {
152
- id: random.string.uuid(),
153
- name: random.person.fullName(),
154
- email: random.internet.email(),
155
- tags: [random.lorem.word(), random.lorem.word(), random.lorem.word()],
156
- nested: {
157
- count: random.number.int({ max: 100 }),
158
- active: random.datatype.boolean(),
159
- },
160
- },
161
- };
162
-
163
- return (
164
- <div className='flex justify-center p-16'>
165
- <div className='dx-card-min-width dx-card-max-width'>
166
- <Card.Root>
167
- <Card.Header>
168
- <Card.IconBlock />
169
- <Card.Title>JSON</Card.Title>
170
- </Card.Header>
171
- <JsonCard data={data} />
172
- </Card.Root>
173
- </div>
174
- </div>
175
- );
176
- },
177
- };