@dxos/plugin-preview 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264 → 0.13.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.
Files changed (123) hide show
  1. package/dist/lib/PreviewCapabilities.mjs +36 -0
  2. package/dist/lib/PreviewCapabilities.mjs.map +1 -0
  3. package/dist/lib/capabilities.browser.mjs +27 -0
  4. package/dist/lib/capabilities.browser.mjs.map +1 -0
  5. package/dist/lib/capabilities.mjs +15 -5
  6. package/dist/lib/capabilities.mjs.map +1 -1
  7. package/dist/lib/capabilities.node.mjs +2 -1
  8. package/dist/lib/capabilities.node.mjs.map +1 -1
  9. package/dist/lib/capabilities.tauri.mjs +27 -0
  10. package/dist/lib/capabilities.tauri.mjs.map +1 -0
  11. package/dist/lib/capabilities.workerd.mjs +2 -1
  12. package/dist/lib/capabilities.workerd.mjs.map +1 -1
  13. package/dist/lib/chunk-link-resolver.mjs +27 -0
  14. package/dist/lib/chunk-link-resolver.mjs.map +1 -0
  15. package/dist/lib/chunk-preview-popover.mjs +70 -28
  16. package/dist/lib/chunk-preview-popover.mjs.map +1 -1
  17. package/dist/lib/chunk-react-surface.mjs +59 -49
  18. package/dist/lib/chunk-react-surface.mjs.map +1 -1
  19. package/dist/lib/index.mjs +3 -3
  20. package/dist/lib/meta.mjs.map +1 -1
  21. package/dist/lib/ns/PreviewEvents.mjs +15 -0
  22. package/dist/lib/ns/PreviewEvents.mjs.map +1 -0
  23. package/dist/lib/plugin.mjs +2 -2
  24. package/dist/lib/plugin.mjs.map +1 -1
  25. package/dist/lib/translations.mjs.map +1 -1
  26. package/dist/lib/types.mjs +2 -0
  27. package/dist/types/src/PreviewEvents.d.ts +8 -0
  28. package/dist/types/src/PreviewEvents.d.ts.map +1 -0
  29. package/dist/types/src/capabilities/RelatedCards.d.ts +1 -1
  30. package/dist/types/src/capabilities/RelatedCards.d.ts.map +1 -1
  31. package/dist/types/src/capabilities/gen/browser.d.ts +7 -0
  32. package/dist/types/src/capabilities/gen/browser.d.ts.map +1 -0
  33. package/dist/types/src/capabilities/gen/node.d.ts +1 -0
  34. package/dist/types/src/capabilities/gen/node.d.ts.map +1 -1
  35. package/dist/types/src/capabilities/gen/tauri.d.ts +7 -0
  36. package/dist/types/src/capabilities/gen/tauri.d.ts.map +1 -0
  37. package/dist/types/src/capabilities/gen/workerd.d.ts +1 -0
  38. package/dist/types/src/capabilities/gen/workerd.d.ts.map +1 -1
  39. package/dist/types/src/capabilities/index.d.ts +1 -0
  40. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  41. package/dist/types/src/capabilities/link-resolver.d.ts +9 -0
  42. package/dist/types/src/capabilities/link-resolver.d.ts.map +1 -0
  43. package/dist/types/src/cards/ExpandoCard.d.ts +1 -1
  44. package/dist/types/src/cards/ExpandoCard.d.ts.map +1 -1
  45. package/dist/types/src/cards/FormCard.d.ts +1 -1
  46. package/dist/types/src/cards/FormCard.d.ts.map +1 -1
  47. package/dist/types/src/cards/JsonCard.d.ts.map +1 -1
  48. package/dist/types/src/cards/JsonCard.stories.d.ts +40 -0
  49. package/dist/types/src/cards/JsonCard.stories.d.ts.map +1 -0
  50. package/dist/types/src/cards/OrganizationCard.d.ts +1 -1
  51. package/dist/types/src/cards/OrganizationCard.d.ts.map +1 -1
  52. package/dist/types/src/cards/PersonCard.d.ts +1 -1
  53. package/dist/types/src/cards/PersonCard.d.ts.map +1 -1
  54. package/dist/types/src/cards/PersonCardIcon.d.ts +1 -1
  55. package/dist/types/src/cards/PersonCardIcon.d.ts.map +1 -1
  56. package/dist/types/src/cards/ProjectCard.d.ts +1 -1
  57. package/dist/types/src/cards/ProjectCard.d.ts.map +1 -1
  58. package/dist/types/src/cards/TaskCard.d.ts +1 -1
  59. package/dist/types/src/cards/TaskCard.d.ts.map +1 -1
  60. package/dist/types/src/cards/index.d.ts +8 -8
  61. package/dist/types/src/cards/index.d.ts.map +1 -1
  62. package/dist/types/src/components/UnsupportedType/UnsupportedType.d.ts.map +1 -1
  63. package/dist/types/src/components/UnsupportedType/UnsupportedType.stories.d.ts +1 -1
  64. package/dist/types/src/components/UnsupportedType/UnsupportedType.stories.d.ts.map +1 -1
  65. package/dist/types/src/components/UnsupportedType/index.d.ts +1 -1
  66. package/dist/types/src/components/UnsupportedType/index.d.ts.map +1 -1
  67. package/dist/types/src/components/index.d.ts +1 -1
  68. package/dist/types/src/components/index.d.ts.map +1 -1
  69. package/dist/types/src/index.d.ts +3 -3
  70. package/dist/types/src/index.d.ts.map +1 -1
  71. package/dist/types/src/plugin.d.ts.map +1 -1
  72. package/dist/types/src/stories/Card.stories.d.ts +3 -2
  73. package/dist/types/src/stories/Card.stories.d.ts.map +1 -1
  74. package/dist/types/src/stories/testing.d.ts +1 -1
  75. package/dist/types/src/stories/testing.d.ts.map +1 -1
  76. package/dist/types/src/testing.d.ts +1 -1
  77. package/dist/types/src/testing.d.ts.map +1 -1
  78. package/dist/types/src/translations.d.ts.map +1 -1
  79. package/dist/types/src/types/PreviewCapabilities.d.ts +49 -0
  80. package/dist/types/src/types/PreviewCapabilities.d.ts.map +1 -0
  81. package/dist/types/src/types/index.d.ts +2 -0
  82. package/dist/types/src/types/index.d.ts.map +1 -0
  83. package/dist/types/tsconfig.tsbuildinfo +1 -1
  84. package/package.json +59 -36
  85. package/src/PreviewEvents.ts +16 -0
  86. package/src/capabilities/RelatedCards.tsx +3 -3
  87. package/src/capabilities/gen/browser.ts +39 -0
  88. package/src/capabilities/gen/node.ts +2 -1
  89. package/src/capabilities/gen/tauri.ts +39 -0
  90. package/src/capabilities/gen/workerd.ts +2 -1
  91. package/src/capabilities/index.ts +18 -6
  92. package/src/capabilities/link-resolver.ts +40 -0
  93. package/src/capabilities/preview-popover.ts +97 -35
  94. package/src/capabilities/react-surface.ts +6 -6
  95. package/src/cards/ExpandoCard.tsx +5 -5
  96. package/src/cards/FormCard.tsx +8 -7
  97. package/src/cards/JsonCard.stories.tsx +75 -0
  98. package/src/cards/JsonCard.tsx +11 -10
  99. package/src/cards/OrganizationCard.tsx +4 -4
  100. package/src/cards/PersonCard.tsx +19 -18
  101. package/src/cards/PersonCardIcon.tsx +1 -1
  102. package/src/cards/ProjectCard.tsx +4 -4
  103. package/src/cards/TaskCard.tsx +13 -17
  104. package/src/cards/index.ts +8 -8
  105. package/src/components/UnsupportedType/UnsupportedType.stories.tsx +1 -1
  106. package/src/components/UnsupportedType/UnsupportedType.tsx +10 -10
  107. package/src/components/UnsupportedType/index.ts +1 -1
  108. package/src/components/index.ts +1 -1
  109. package/src/index.ts +3 -3
  110. package/src/meta.ts +1 -1
  111. package/src/plugin.test.ts +2 -2
  112. package/src/plugin.tsx +2 -1
  113. package/src/stories/Card.stories.tsx +12 -6
  114. package/src/stories/testing.tsx +8 -8
  115. package/src/testing.ts +1 -1
  116. package/src/translations.ts +2 -2
  117. package/src/types/PreviewCapabilities.ts +83 -0
  118. package/src/types/index.ts +5 -0
  119. package/dist/lib/chunk-events.mjs +0 -11
  120. package/dist/lib/chunk-events.mjs.map +0 -1
  121. package/dist/types/src/events.d.ts +0 -10
  122. package/dist/types/src/events.d.ts.map +0 -1
  123. package/src/events.ts +0 -16
@@ -0,0 +1,36 @@
1
+ import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
+ import * as Capability from "@dxos/app-framework/Capability";
3
+ import { meta } from "#meta";
4
+ //#region src/types/PreviewCapabilities.ts
5
+ var PreviewCapabilities_exports = /* @__PURE__ */ __exportAll({
6
+ LinkResolver: () => LinkResolver,
7
+ isPreviewLink: () => isPreviewLink,
8
+ linkIcon: () => linkIcon,
9
+ linkLabel: () => linkLabel
10
+ });
11
+ /**
12
+ * Multi capability: each contributing plugin provides one batch of resolvers. The popover asks
13
+ * every resolver and shows the first answer, so a plugin that can turn a URL into an object needs
14
+ * only this and a `CardContent` surface for the object's type.
15
+ */
16
+ var LinkResolver = Capability.make()(`${meta.profile.key}.capability.linkResolver`);
17
+ /** Whether any contributed resolver answers for the URL: what a renderer asks before making a link an anchor. */
18
+ var isPreviewLink = (resolvers, url) => resolvers.some(({ match }) => match(url));
19
+ /** The first short name a resolver offers for the URL, or undefined when none does. */
20
+ var linkLabel = (resolvers, url) => {
21
+ for (const { match, label } of resolvers) {
22
+ const name = match(url) ? label?.(url) : void 0;
23
+ if (name) return name;
24
+ }
25
+ };
26
+ /** The first icon a resolver offers for the URL, or undefined when none does. */
27
+ var linkIcon = (resolvers, url) => {
28
+ for (const { match, icon } of resolvers) {
29
+ const found = match(url) ? icon?.(url) : void 0;
30
+ if (found) return found;
31
+ }
32
+ };
33
+ //#endregion
34
+ export { LinkResolver, isPreviewLink, linkIcon, linkLabel, PreviewCapabilities_exports as t };
35
+
36
+ //# sourceMappingURL=PreviewCapabilities.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PreviewCapabilities.mjs","names":[],"sources":["../../src/types/PreviewCapabilities.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport type * as Effect from 'effect/Effect';\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport { type Database } from '@dxos/echo';\nimport { type PreviewLinkRef, type PreviewLinkTarget } from '@dxos/ui-types';\n\nimport { meta } from '#meta';\n\nexport type PreviewLinkContext = {\n /** The database of the space the activating document belongs to, when one is known. */\n db?: Database.Database;\n};\n\n/** Decides, synchronously, whether a URL is one this resolver answers — so a renderer can make it an anchor before anyone hovers. */\nexport type PreviewLinkMatch = (url: string) => boolean;\n\n/** Answers an anchor activation with the object its link names, or undefined. */\nexport type PreviewLinkResolve = (\n ref: PreviewLinkRef,\n context: PreviewLinkContext,\n) => Effect.Effect<PreviewLinkTarget | undefined>;\n\n/**\n * One kind of link — an ECHO entity URI, a GitHub URL — with the test that recognises it and the\n * lookup that answers it. A resolver owns its kind and declines the rest, so the order resolvers\n * are asked in does not matter.\n */\nexport type PreviewLinkResolver = {\n match: PreviewLinkMatch;\n resolve: PreviewLinkResolve;\n /**\n * A short name for a URL written bare — `#123` for a pull request — so a renderer can show it\n * instead of the whole address without knowing the URL's shape.\n */\n label?: (url: string) => string | undefined;\n /**\n * A leading icon for the URL — a pull request's, say — so a renderer's chip reads the same as the\n * editor's without knowing the URL's shape.\n */\n icon?: (url: string) => PreviewLinkIcon | undefined;\n};\n\n/** An icon a chip leads with: the icon name and any classes that colour it. */\nexport type PreviewLinkIcon = { icon: string; classNames?: string };\n\n/**\n * Multi capability: each contributing plugin provides one batch of resolvers. The popover asks\n * every resolver and shows the first answer, so a plugin that can turn a URL into an object needs\n * only this and a `CardContent` surface for the object's type.\n */\nexport const LinkResolver = Capability.make<PreviewLinkResolver[]>()(`${meta.profile.key}.capability.linkResolver`);\n\n/** Whether any contributed resolver answers for the URL: what a renderer asks before making a link an anchor. */\nexport const isPreviewLink = (resolvers: readonly PreviewLinkResolver[], url: string): boolean =>\n resolvers.some(({ match }) => match(url));\n\n/** The first short name a resolver offers for the URL, or undefined when none does. */\nexport const linkLabel = (resolvers: readonly PreviewLinkResolver[], url: string): string | undefined => {\n for (const { match, label } of resolvers) {\n const name = match(url) ? label?.(url) : undefined;\n if (name) {\n return name;\n }\n }\n return undefined;\n};\n\n/** The first icon a resolver offers for the URL, or undefined when none does. */\nexport const linkIcon = (resolvers: readonly PreviewLinkResolver[], url: string): PreviewLinkIcon | undefined => {\n for (const { match, icon } of resolvers) {\n const found = match(url) ? icon?.(url) : undefined;\n if (found) {\n return found;\n }\n }\n return undefined;\n};\n"],"mappings":";;;;;;;;;;;;;;;AAwDA,IAAa,eAAe,WAAW,KAA4B,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,yBAAyB;;AAGlH,IAAa,iBAAiB,WAA2C,QACvE,UAAU,MAAM,EAAE,YAAY,MAAM,GAAG,CAAC;;AAG1C,IAAa,aAAa,WAA2C,QAAoC;CACvG,KAAK,MAAM,EAAE,OAAO,WAAW,WAAW;EACxC,MAAM,OAAO,MAAM,GAAG,IAAI,QAAQ,GAAG,IAAI,KAAA;EACzC,IAAI,MACF,OAAO;CAEX;AAEF;;AAGA,IAAa,YAAY,WAA2C,QAA6C;CAC/G,KAAK,MAAM,EAAE,OAAO,UAAU,WAAW;EACvC,MAAM,QAAQ,MAAM,GAAG,IAAI,OAAO,GAAG,IAAI,KAAA;EACzC,IAAI,OACF,OAAO;CAEX;AAEF"}
@@ -0,0 +1,27 @@
1
+ import { Start } from "./ns/PreviewEvents.mjs";
2
+ import * as AppCapability from "@dxos/app-toolkit/AppCapability";
3
+ import { translations } from "#translations";
4
+ import * as Capability from "@dxos/app-framework/Capability";
5
+ import { PreviewCapabilities } from "#types";
6
+ //#region src/capabilities/gen/browser.ts
7
+ var LinkResolver = Capability.lazyModule("LinkResolver", {
8
+ provides: [PreviewCapabilities.LinkResolver],
9
+ activatesOn: Start,
10
+ environments: ["browser", "tauri"]
11
+ }, () => import("./chunk-link-resolver.mjs"));
12
+ var PreviewPopover = Capability.lazyModule("PreviewPopover", {
13
+ provides: [],
14
+ activatesOn: Start,
15
+ environments: ["browser", "tauri"]
16
+ }, () => import("./chunk-preview-popover.mjs"));
17
+ var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: [
18
+ "org.dxos.role.cardContent",
19
+ "org.dxos.role.cardIcon",
20
+ "org.dxos.role.article"
21
+ ] });
22
+ var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
23
+ var Translations = AppCapability.translations(translations);
24
+ //#endregion
25
+ export { LinkResolver, PreviewPopover, ReactSurface, Schema, Translations };
26
+
27
+ //# sourceMappingURL=capabilities.browser.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.browser.mjs","names":[],"sources":["../../src/capabilities/gen/browser.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'browser' condition.\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\nimport { PreviewCapabilities } from '#types';\n\nimport * as PreviewEvents from '../../PreviewEvents.ts';\n\n// Browser-only with the popover it serves: the resolver loads objects for a card no headless host renders.\nexport const LinkResolver = Capability.lazyModule(\n 'LinkResolver',\n {\n provides: [PreviewCapabilities.LinkResolver],\n activatesOn: PreviewEvents.Start,\n environments: ['browser', 'tauri'],\n },\n () => import('../link-resolver.ts'),\n);\n\n// Browser-only: the module mounts the popover itself, so its body is React all the way down.\nexport const PreviewPopover = Capability.lazyModule(\n 'PreviewPopover',\n { provides: [], activatesOn: PreviewEvents.Start, environments: ['browser', 'tauri'] },\n () => import('../preview-popover.ts'),\n);\n\nexport const ReactSurface = AppCapability.surface(() => import('../react-surface.ts'), {\n roles: ['org.dxos.role.cardContent', 'org.dxos.role.cardIcon', 'org.dxos.role.article'],\n});\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations(translations);\n"],"mappings":";;;;;;AAeA,IAAa,eAAe,WAAW,WACrC,gBACA;CACE,UAAU,CAAC,oBAAoB,YAAY;CAC3C,aAAa;CACb,cAAc,CAAC,WAAW,OAAO;AACnC,SACM,OAAO,4BACf;AAGA,IAAa,iBAAiB,WAAW,WACvC,kBACA;CAAE,UAAU,CAAC;CAAG,aAAa;CAAqB,cAAc,CAAC,WAAW,OAAO;AAAE,SAC/E,OAAO,8BACf;AAEA,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAwB,EACrF,OAAO;CAAC;CAA6B;CAA0B;AAAuB,EACxF,CAAC;AAED,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,YAAY"}
@@ -1,17 +1,27 @@
1
- import { t as PreviewEvents } from "./chunk-events.mjs";
1
+ import { Start } from "./ns/PreviewEvents.mjs";
2
2
  import * as AppCapability from "@dxos/app-toolkit/AppCapability";
3
3
  import { translations } from "#translations";
4
4
  import * as Capability from "@dxos/app-framework/Capability";
5
+ import { PreviewCapabilities } from "#types";
5
6
  //#region src/capabilities/index.ts
7
+ var LinkResolver = Capability.lazyModule("LinkResolver", {
8
+ provides: [PreviewCapabilities.LinkResolver],
9
+ activatesOn: Start,
10
+ environments: ["browser", "tauri"]
11
+ }, () => import("./chunk-link-resolver.mjs"));
6
12
  var PreviewPopover = Capability.lazyModule("PreviewPopover", {
7
13
  provides: [],
8
- activatesOn: PreviewEvents.Start,
9
- environments: []
14
+ activatesOn: Start,
15
+ environments: ["browser", "tauri"]
10
16
  }, () => import("./chunk-preview-popover.mjs"));
11
- var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: ["org.dxos.role.cardContent", "org.dxos.role.article"] });
17
+ var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: [
18
+ "org.dxos.role.cardContent",
19
+ "org.dxos.role.cardIcon",
20
+ "org.dxos.role.article"
21
+ ] });
12
22
  var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
13
23
  var Translations = AppCapability.translations(translations);
14
24
  //#endregion
15
- export { PreviewPopover, ReactSurface, Schema, Translations };
25
+ export { LinkResolver, PreviewPopover, ReactSurface, Schema, Translations };
16
26
 
17
27
  //# sourceMappingURL=capabilities.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.mjs","names":[],"sources":["../../src/capabilities/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nimport { PreviewEvents } from '../events';\n\n// Browser-only: the module mounts the popover itself, so its body is React all the way down.\nexport const PreviewPopover = Capability.lazyModule(\n 'PreviewPopover',\n { provides: [], activatesOn: PreviewEvents.Start, environments: [] },\n () => import('./preview-popover'),\n);\nexport const ReactSurface = AppCapability.surface(() => import('./react-surface'), {\n roles: ['org.dxos.role.cardContent', 'org.dxos.role.article'],\n});\nexport const Schema = AppCapability.schema(() => import('./schema'));\nexport const Translations = AppCapability.translations(translations);\n"],"mappings":";;;;;AAYA,IAAa,iBAAiB,WAAW,WACvC,kBACA;CAAE,UAAU,CAAC;CAAG,aAAa,cAAc;CAAO,cAAc,CAAC;AAAE,SAC7D,OAAO,8BACf;AACA,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAoB,EACjF,OAAO,CAAC,6BAA6B,uBAAuB,EAC9D,CAAC;AACD,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAW;AACnE,IAAa,eAAe,cAAc,aAAa,YAAY"}
1
+ {"version":3,"file":"capabilities.mjs","names":[],"sources":["../../src/capabilities/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\nimport { PreviewCapabilities } from '#types';\n\nimport * as PreviewEvents from '../PreviewEvents.ts';\n\n// Browser-only with the popover it serves: the resolver loads objects for a card no headless host renders.\nexport const LinkResolver = Capability.lazyModule(\n 'LinkResolver',\n {\n provides: [PreviewCapabilities.LinkResolver],\n activatesOn: PreviewEvents.Start,\n environments: ['browser', 'tauri'],\n },\n () => import('./link-resolver.ts'),\n);\n\n// Browser-only: the module mounts the popover itself, so its body is React all the way down.\nexport const PreviewPopover = Capability.lazyModule(\n 'PreviewPopover',\n { provides: [], activatesOn: PreviewEvents.Start, environments: ['browser', 'tauri'] },\n () => import('./preview-popover.ts'),\n);\nexport const ReactSurface = AppCapability.surface(() => import('./react-surface.ts'), {\n roles: ['org.dxos.role.cardContent', 'org.dxos.role.cardIcon', 'org.dxos.role.article'],\n});\nexport const Schema = AppCapability.schema(() => import('./schema.ts'));\nexport const Translations = AppCapability.translations(translations);\n"],"mappings":";;;;;;AAaA,IAAa,eAAe,WAAW,WACrC,gBACA;CACE,UAAU,CAAC,oBAAoB,YAAY;CAC3C,aAAa;CACb,cAAc,CAAC,WAAW,OAAO;AACnC,SACM,OAAO,4BACf;AAGA,IAAa,iBAAiB,WAAW,WACvC,kBACA;CAAE,UAAU,CAAC;CAAG,aAAa;CAAqB,cAAc,CAAC,WAAW,OAAO;AAAE,SAC/E,OAAO,8BACf;AACA,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAuB,EACpF,OAAO;CAAC;CAA6B;CAA0B;AAAuB,EACxF,CAAC;AACD,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAc;AACtE,IAAa,eAAe,cAAc,aAAa,YAAY"}
@@ -3,9 +3,10 @@ import { translations } from "#translations";
3
3
  //#region src/capabilities/gen/node.ts
4
4
  var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
5
5
  var Translations = AppCapability.translations(translations);
6
+ var LinkResolver = void 0;
6
7
  var PreviewPopover = void 0;
7
8
  var ReactSurface = void 0;
8
9
  //#endregion
9
- export { PreviewPopover, ReactSurface, Schema, Translations };
10
+ export { LinkResolver, PreviewPopover, ReactSurface, Schema, Translations };
10
11
 
11
12
  //# sourceMappingURL=capabilities.node.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.node.mjs","names":[],"sources":["../../src/capabilities/gen/node.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'node' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nexport const Schema = AppCapability.schema(() => import('../schema'));\n\nexport const Translations = AppCapability.translations(translations);\n\nexport const PreviewPopover = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;AAUA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAY;AAEpE,IAAa,eAAe,cAAc,aAAa,YAAY;AAEnE,IAAa,iBAAiB,KAAA;AAC9B,IAAa,eAAe,KAAA"}
1
+ {"version":3,"file":"capabilities.node.mjs","names":[],"sources":["../../src/capabilities/gen/node.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'node' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations(translations);\n\nexport const LinkResolver = undefined;\nexport const PreviewPopover = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;AAUA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,YAAY;AAEnE,IAAa,eAAe,KAAA;AAC5B,IAAa,iBAAiB,KAAA;AAC9B,IAAa,eAAe,KAAA"}
@@ -0,0 +1,27 @@
1
+ import { Start } from "./ns/PreviewEvents.mjs";
2
+ import * as AppCapability from "@dxos/app-toolkit/AppCapability";
3
+ import { translations } from "#translations";
4
+ import * as Capability from "@dxos/app-framework/Capability";
5
+ import { PreviewCapabilities } from "#types";
6
+ //#region src/capabilities/gen/tauri.ts
7
+ var LinkResolver = Capability.lazyModule("LinkResolver", {
8
+ provides: [PreviewCapabilities.LinkResolver],
9
+ activatesOn: Start,
10
+ environments: ["browser", "tauri"]
11
+ }, () => import("./chunk-link-resolver.mjs"));
12
+ var PreviewPopover = Capability.lazyModule("PreviewPopover", {
13
+ provides: [],
14
+ activatesOn: Start,
15
+ environments: ["browser", "tauri"]
16
+ }, () => import("./chunk-preview-popover.mjs"));
17
+ var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: [
18
+ "org.dxos.role.cardContent",
19
+ "org.dxos.role.cardIcon",
20
+ "org.dxos.role.article"
21
+ ] });
22
+ var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
23
+ var Translations = AppCapability.translations(translations);
24
+ //#endregion
25
+ export { LinkResolver, PreviewPopover, ReactSurface, Schema, Translations };
26
+
27
+ //# sourceMappingURL=capabilities.tauri.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.tauri.mjs","names":[],"sources":["../../src/capabilities/gen/tauri.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'tauri' condition.\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\nimport { PreviewCapabilities } from '#types';\n\nimport * as PreviewEvents from '../../PreviewEvents.ts';\n\n// Browser-only with the popover it serves: the resolver loads objects for a card no headless host renders.\nexport const LinkResolver = Capability.lazyModule(\n 'LinkResolver',\n {\n provides: [PreviewCapabilities.LinkResolver],\n activatesOn: PreviewEvents.Start,\n environments: ['browser', 'tauri'],\n },\n () => import('../link-resolver.ts'),\n);\n\n// Browser-only: the module mounts the popover itself, so its body is React all the way down.\nexport const PreviewPopover = Capability.lazyModule(\n 'PreviewPopover',\n { provides: [], activatesOn: PreviewEvents.Start, environments: ['browser', 'tauri'] },\n () => import('../preview-popover.ts'),\n);\n\nexport const ReactSurface = AppCapability.surface(() => import('../react-surface.ts'), {\n roles: ['org.dxos.role.cardContent', 'org.dxos.role.cardIcon', 'org.dxos.role.article'],\n});\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations(translations);\n"],"mappings":";;;;;;AAeA,IAAa,eAAe,WAAW,WACrC,gBACA;CACE,UAAU,CAAC,oBAAoB,YAAY;CAC3C,aAAa;CACb,cAAc,CAAC,WAAW,OAAO;AACnC,SACM,OAAO,4BACf;AAGA,IAAa,iBAAiB,WAAW,WACvC,kBACA;CAAE,UAAU,CAAC;CAAG,aAAa;CAAqB,cAAc,CAAC,WAAW,OAAO;AAAE,SAC/E,OAAO,8BACf;AAEA,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAwB,EACrF,OAAO;CAAC;CAA6B;CAA0B;AAAuB,EACxF,CAAC;AAED,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,YAAY"}
@@ -3,9 +3,10 @@ import { translations } from "#translations";
3
3
  //#region src/capabilities/gen/workerd.ts
4
4
  var Schema = AppCapability.schema(() => import("./chunk-schema.mjs"));
5
5
  var Translations = AppCapability.translations(translations);
6
+ var LinkResolver = void 0;
6
7
  var PreviewPopover = void 0;
7
8
  var ReactSurface = void 0;
8
9
  //#endregion
9
- export { PreviewPopover, ReactSurface, Schema, Translations };
10
+ export { LinkResolver, PreviewPopover, ReactSurface, Schema, Translations };
10
11
 
11
12
  //# sourceMappingURL=capabilities.workerd.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.workerd.mjs","names":[],"sources":["../../src/capabilities/gen/workerd.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'workerd' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nexport const Schema = AppCapability.schema(() => import('../schema'));\n\nexport const Translations = AppCapability.translations(translations);\n\nexport const PreviewPopover = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;AAUA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAY;AAEpE,IAAa,eAAe,cAAc,aAAa,YAAY;AAEnE,IAAa,iBAAiB,KAAA;AAC9B,IAAa,eAAe,KAAA"}
1
+ {"version":3,"file":"capabilities.workerd.mjs","names":[],"sources":["../../src/capabilities/gen/workerd.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'workerd' condition.\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { translations } from '#translations';\n\nexport const Schema = AppCapability.schema(() => import('../schema.ts'));\n\nexport const Translations = AppCapability.translations(translations);\n\nexport const LinkResolver = undefined;\nexport const PreviewPopover = undefined;\nexport const ReactSurface = undefined;\n"],"mappings":";;;AAUA,IAAa,SAAS,cAAc,aAAa,OAAO,qBAAe;AAEvE,IAAa,eAAe,cAAc,aAAa,YAAY;AAEnE,IAAa,eAAe,KAAA;AAC5B,IAAa,iBAAiB,KAAA;AAC9B,IAAa,eAAe,KAAA"}
@@ -0,0 +1,27 @@
1
+ import * as Capability from "@dxos/app-framework/Capability";
2
+ import { PreviewCapabilities } from "#types";
3
+ import * as Effect from "effect/Effect";
4
+ import { Obj } from "@dxos/echo";
5
+ import { EID } from "@dxos/keys";
6
+ //#region src/capabilities/link-resolver.ts
7
+ /**
8
+ * The ECHO resolver: an anchor whose eid parses as an entity URI is loaded from the space. Other
9
+ * refs — `dxn:` type URIs, web URLs — are another resolver's.
10
+ */
11
+ var link_resolver_default = Capability.makeModule(() => Effect.succeed(Capability.contribute(PreviewCapabilities.LinkResolver, [{
12
+ match: (url) => EID.tryParse(url) !== void 0,
13
+ resolve: ({ eid, label }, { db }) => Effect.gen(function* () {
14
+ const parsed = EID.tryParse(eid);
15
+ if (!parsed || !db) return;
16
+ const entity = yield* Effect.tryPromise(() => db.makeRef(parsed).load()).pipe(Effect.catch(() => Effect.succeed(void 0)));
17
+ if (!Obj.isObject(entity)) return;
18
+ return {
19
+ label: Obj.getLabel(entity, { fallback: "typename" }) ?? label,
20
+ object: entity
21
+ };
22
+ })
23
+ }])));
24
+ //#endregion
25
+ export { link_resolver_default as default };
26
+
27
+ //# sourceMappingURL=chunk-link-resolver.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-link-resolver.mjs","names":[],"sources":["../../src/capabilities/link-resolver.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport { Obj } from '@dxos/echo';\nimport { EID } from '@dxos/keys';\n\nimport { PreviewCapabilities } from '#types';\n\n/**\n * The ECHO resolver: an anchor whose eid parses as an entity URI is loaded from the space. Other\n * refs — `dxn:` type URIs, web URLs — are another resolver's.\n */\nexport default Capability.makeModule(() =>\n Effect.succeed(\n Capability.contribute(PreviewCapabilities.LinkResolver, [\n {\n match: (url) => EID.tryParse(url) !== undefined,\n resolve: ({ eid, label }, { db }) =>\n Effect.gen(function* () {\n const parsed = EID.tryParse(eid);\n if (!parsed || !db) {\n return undefined;\n }\n const entity = yield* Effect.tryPromise(() => db.makeRef(parsed).load()).pipe(\n Effect.catch(() => Effect.succeed(undefined)),\n );\n // A relation has no card; only an object is previewed.\n if (!Obj.isObject(entity)) {\n return undefined;\n }\n return { label: Obj.getLabel(entity, { fallback: 'typename' }) ?? label, object: entity };\n }),\n },\n ]),\n ),\n);\n"],"mappings":";;;;;;;;;;AAgBA,IAAA,wBAAe,WAAW,iBACxB,OAAO,QACL,WAAW,WAAW,oBAAoB,cAAc,CACtD;CACE,QAAQ,QAAQ,IAAI,SAAS,GAAG,MAAM,KAAA;CACtC,UAAU,EAAE,KAAK,SAAS,EAAE,SAC1B,OAAO,IAAI,aAAa;EACtB,MAAM,SAAS,IAAI,SAAS,GAAG;EAC/B,IAAI,CAAC,UAAU,CAAC,IACd;EAEF,MAAM,SAAS,OAAO,OAAO,iBAAiB,GAAG,QAAQ,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KACvE,OAAO,YAAY,OAAO,QAAQ,KAAA,CAAS,CAAC,CAC9C;EAEA,IAAI,CAAC,IAAI,SAAS,MAAM,GACtB;EAEF,OAAO;GAAE,OAAO,IAAI,SAAS,QAAQ,EAAE,UAAU,WAAW,CAAC,KAAK;GAAO,QAAQ;EAAO;CAC1F,CAAC;AACL,CACF,CAAC,CACH,CACF"}
@@ -1,42 +1,43 @@
1
1
  import * as Capability from "@dxos/app-framework/Capability";
2
+ import { PreviewCapabilities } from "#types";
2
3
  import * as Effect from "effect/Effect";
4
+ import { Obj } from "@dxos/echo";
3
5
  import * as Capabilities from "@dxos/app-framework/Capabilities";
4
6
  import * as AppCapabilities from "@dxos/app-toolkit/AppCapabilities";
5
7
  import * as AppSpace from "@dxos/app-toolkit/AppSpace";
6
8
  import * as GraphPath from "@dxos/app-toolkit/GraphPath";
9
+ import * as Hooks from "@dxos/app-toolkit/Hooks";
7
10
  import * as LayoutOperation from "@dxos/app-toolkit/LayoutOperation";
8
11
  import { addEventListener } from "@dxos/async";
9
- import { Obj } from "@dxos/echo";
10
- import { EID } from "@dxos/keys";
12
+ import * as EffectEx from "@dxos/effect/EffectEx";
11
13
  import { log } from "@dxos/log";
12
14
  import * as ClientCapabilities from "@dxos/plugin-client/ClientCapabilities";
13
- import { DX_ANCHOR_ACTIVATE } from "@dxos/react-ui";
15
+ import { Attention } from "@dxos/react-ui-attention/types";
16
+ import { DX_ANCHOR_ACTIVATE } from "@dxos/ui-types";
14
17
  //#region src/capabilities/preview-popover.ts
15
18
  var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-preview/src/capabilities/preview-popover.ts";
16
19
  var customEventOptions = {
17
20
  capture: true,
18
21
  passive: false
19
22
  };
20
- var handlePreviewLookup = async (space, { dxn, label }) => {
21
- const eid = EID.tryParse(dxn);
22
- if (!eid) return null;
23
- try {
24
- const object = await space.db.makeRef(eid).load();
25
- return {
26
- label: Obj.getLabel(object, { fallback: "typename" }) ?? label,
27
- object
28
- };
29
- } catch {
30
- return null;
23
+ /** The first resolver's answer, asked in contribution order; a resolver declines by answering undefined. */
24
+ var resolveLink = (resolvers, ref, context) => Effect.gen(function* () {
25
+ for (const { match, resolve } of resolvers) {
26
+ if (!match(ref.eid)) continue;
27
+ const target = yield* resolve(ref, context);
28
+ if (target) return target;
31
29
  }
32
- };
30
+ });
33
31
  var preview_popover_default = Capability.makeModule(Effect.fnUntraced(function* () {
34
32
  const capabilities = yield* Capability.Service;
35
33
  let activationSequence = 0;
36
- const handleAnchorActivate = async ({ dxn, label, trigger, kind = "card", title: titleProp, side, props, state }) => {
37
- const sequence = ++activationSequence;
34
+ let activeTrigger;
35
+ const handleAnchorActivate = async ({ eid, label, trigger, kind = "card", title: titleProp, side, props, state, navigate }) => {
38
36
  const { invokePromise } = capabilities.get(Capabilities.OperationInvoker);
39
37
  if (state === false) {
38
+ if (trigger !== activeTrigger) return;
39
+ activeTrigger = void 0;
40
+ activationSequence++;
40
41
  await invokePromise(LayoutOperation.UpdatePopover, {
41
42
  variant: "virtual",
42
43
  anchor: trigger,
@@ -45,29 +46,70 @@ var preview_popover_default = Capability.makeModule(Effect.fnUntraced(function*
45
46
  });
46
47
  return;
47
48
  }
49
+ const sequence = ++activationSequence;
50
+ const pivotId = navigate ? Attention.getRootAttendableId(trigger) : void 0;
51
+ if (navigate && activeTrigger) {
52
+ activeTrigger = void 0;
53
+ await invokePromise(LayoutOperation.UpdatePopover, {
54
+ variant: "virtual",
55
+ anchor: trigger,
56
+ kind: "base",
57
+ state: false
58
+ });
59
+ }
60
+ if (!navigate) activeTrigger = trigger;
48
61
  const client = capabilities.get(ClientCapabilities.Client);
49
62
  const registry = capabilities.get(Capabilities.AtomRegistry);
50
63
  const [layoutAtom] = capabilities.getAll(AppCapabilities.Layout);
51
64
  const spaceId = layoutAtom && GraphPath.getSpaceIdFromPath(registry.get(layoutAtom).workspace);
52
65
  const space = (spaceId && client.spaces.get(spaceId)) ?? AppSpace.getDefaultSpace(client);
53
- if (!space) return;
54
- const result = await handlePreviewLookup(space, {
55
- dxn,
66
+ const resolvers = capabilities.getAll(PreviewCapabilities.LinkResolver).flat();
67
+ const result = await EffectEx.runPromise(resolveLink(resolvers, {
68
+ eid,
56
69
  label
57
- });
58
- if (!result) return;
70
+ }, { db: space?.db }));
59
71
  if (sequence !== activationSequence) return;
60
- const title = titleProp ?? Obj.getLabel(result.object, { fallback: "typename" });
61
- await invokePromise(LayoutOperation.UpdatePopover, {
62
- subjectRef: dxn,
72
+ if (navigate) {
73
+ if (Obj.isObject(result?.object) && Obj.getDatabase(result.object)) {
74
+ const { invoke } = capabilities.get(Capabilities.OperationInvoker);
75
+ await EffectEx.runPromise(Hooks.openObject(result.object, invoke, { pivotId }));
76
+ return;
77
+ }
78
+ activeTrigger = trigger;
79
+ }
80
+ if (!result) {
81
+ if (kind === "card" && PreviewCapabilities.isPreviewLink(resolvers, eid)) await invokePromise(LayoutOperation.UpdatePopover, {
82
+ subjectRef: eid,
83
+ state: true,
84
+ variant: "virtual",
85
+ anchor: trigger,
86
+ kind,
87
+ title: titleProp ?? PreviewCapabilities.linkLabel(resolvers, eid) ?? (label || eid),
88
+ ...side && { side }
89
+ });
90
+ return;
91
+ }
92
+ const fallbackTitle = ["object-name.placeholder", {
93
+ ns: Obj.getTypename(result.object) ?? "",
94
+ defaultValue: "New item"
95
+ }];
96
+ const title = titleProp ?? Obj.getLabel(result.object) ?? fallbackTitle;
97
+ const input = {
98
+ subjectRef: eid,
63
99
  subject: result.object,
64
100
  state: true,
65
101
  variant: "virtual",
66
102
  anchor: trigger,
67
- ...kind && { kind },
68
- ...title && { title },
69
103
  ...side && { side },
70
104
  ...props && { props }
105
+ };
106
+ await invokePromise(LayoutOperation.UpdatePopover, kind === "card" ? {
107
+ ...input,
108
+ kind,
109
+ title
110
+ } : {
111
+ ...input,
112
+ kind
71
113
  });
72
114
  };
73
115
  let cleanup;
@@ -75,7 +117,7 @@ var preview_popover_default = Capability.makeModule(Effect.fnUntraced(function*
75
117
  else log.warn("no default view found", void 0, {
76
118
  "~LogMeta": "~LogMeta",
77
119
  F: __dxlog_file,
78
- L: 122,
120
+ L: 184,
79
121
  S: this
80
122
  });
81
123
  yield* Effect.addFinalizer(() => Effect.sync(() => cleanup?.()));
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-preview-popover.mjs","names":[],"sources":["../../src/capabilities/preview-popover.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport * as AppSpace from '@dxos/app-toolkit/AppSpace';\nimport * as GraphPath from '@dxos/app-toolkit/GraphPath';\nimport * as LayoutOperation from '@dxos/app-toolkit/LayoutOperation';\nimport { addEventListener } from '@dxos/async';\nimport { type Space } from '@dxos/client/echo';\nimport { Obj } from '@dxos/echo';\nimport { EID } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport * as ClientCapabilities from '@dxos/plugin-client/ClientCapabilities';\nimport { DX_ANCHOR_ACTIVATE, type DxAnchorActivate } from '@dxos/react-ui';\nimport { type PreviewLinkRef, type PreviewLinkTarget } from '@dxos/ui-types';\n\nconst customEventOptions = { capture: true, passive: false };\n\n// TODO(burdon): Factor out?\nconst handlePreviewLookup = async (space: Space, { dxn, label }: PreviewLinkRef): Promise<PreviewLinkTarget | null> => {\n const eid = EID.tryParse(dxn);\n if (!eid) {\n // dxn: type URIs and other non-EID refs cannot be resolved to an object.\n return null;\n }\n try {\n const object = await space.db.makeRef(eid).load();\n const resolvedLabel = Obj.getLabel(object as any, { fallback: 'typename' });\n return { label: resolvedLabel ?? label, object };\n } catch {\n return null;\n }\n};\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n // Get context for lazy capability access in callbacks.\n const capabilities = yield* Capability.Service;\n\n // TODO(wittjosiah): Factor out lookup handlers to other plugins to make not ECHO-specific.\n // Monotonic activation token: each invocation captures its own sequence; only the\n // most recent activation is allowed to commit popover state. Prevents a slow\n // open (async lookup) from clobbering a later close that fires while it's in flight.\n let activationSequence = 0;\n const handleAnchorActivate = async ({\n dxn,\n label,\n trigger,\n kind = 'card',\n title: titleProp,\n side,\n props,\n state,\n }: DxAnchorActivate) => {\n const sequence = ++activationSequence;\n const { invokePromise } = capabilities.get(Capabilities.OperationInvoker);\n\n // Explicit close: callers pass `state: false` on pointer-leave to dismiss\n // the popover. Operation schema requires anchor + kind, so use placeholders;\n // they're overwritten in ephemeral state but only `state` is read by the UI.\n if (state === false) {\n await invokePromise(LayoutOperation.UpdatePopover, {\n variant: 'virtual',\n anchor: trigger,\n kind: 'base',\n state: false,\n });\n return;\n }\n\n const client = capabilities.get(ClientCapabilities.Client);\n const registry = capabilities.get(Capabilities.AtomRegistry);\n // Layout is optional: in standalone harnesses (Storybook, tests) no plugin contributes\n // `AppCapabilities.Layout`, and `getAll` returns an empty array. Reading `registry.get(undefined)`\n // would crash inside Atom's identity check (`'~atom/Serializable' in undefined`). When layout\n // isn't available, fall through to the default space.\n const [layoutAtom] = capabilities.getAll(AppCapabilities.Layout);\n const spaceId = layoutAtom && GraphPath.getSpaceIdFromPath(registry.get(layoutAtom).workspace);\n const space = (spaceId && client.spaces.get(spaceId)) ?? AppSpace.getDefaultSpace(client);\n if (!space) {\n return;\n }\n const result = await handlePreviewLookup(space, { dxn, label });\n if (!result) {\n return;\n }\n // A newer activation (open or close) arrived while the lookup was in flight; bail\n // out so we don't clobber the latest state.\n if (sequence !== activationSequence) {\n return;\n }\n\n const title = titleProp ?? Obj.getLabel(result.object, { fallback: 'typename' });\n\n await invokePromise(LayoutOperation.UpdatePopover, {\n subjectRef: dxn,\n subject: result.object,\n state: true,\n variant: 'virtual',\n anchor: trigger,\n ...(kind && { kind }),\n ...(title && { title }),\n ...(side && { side }),\n ...(props && { props }),\n });\n };\n\n let cleanup: () => void;\n if (document.defaultView) {\n cleanup = addEventListener(\n document.defaultView,\n DX_ANCHOR_ACTIVATE as any,\n handleAnchorActivate,\n customEventOptions,\n );\n } else {\n log.warn('no default view found');\n }\n\n yield* Effect.addFinalizer(() => Effect.sync(() => cleanup?.()));\n return [];\n }),\n);\n"],"mappings":";;;;;;;;;;;;;;;AAqBA,IAAM,qBAAqB;CAAE,SAAS;CAAM,SAAS;AAAM;AAG3D,IAAM,sBAAsB,OAAO,OAAc,EAAE,KAAK,YAA+D;CACrH,MAAM,MAAM,IAAI,SAAS,GAAG;CAC5B,IAAI,CAAC,KAEH,OAAO;CAET,IAAI;EACF,MAAM,SAAS,MAAM,MAAM,GAAG,QAAQ,GAAG,CAAC,CAAC,KAAK;EAEhD,OAAO;GAAE,OADa,IAAI,SAAS,QAAe,EAAE,UAAU,WAAW,CACzD,KAAiB;GAAO;EAAO;CACjD,QAAQ;EACN,OAAO;CACT;AACF;AAEA,IAAA,0BAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAE7B,MAAM,eAAe,OAAO,WAAW;CAMvC,IAAI,qBAAqB;CACzB,MAAM,uBAAuB,OAAO,EAClC,KACA,OACA,SACA,OAAO,QACP,OAAO,WACP,MACA,OACA,YACsB;EACtB,MAAM,WAAW,EAAE;EACnB,MAAM,EAAE,kBAAkB,aAAa,IAAI,aAAa,gBAAgB;EAKxE,IAAI,UAAU,OAAO;GACnB,MAAM,cAAc,gBAAgB,eAAe;IACjD,SAAS;IACT,QAAQ;IACR,MAAM;IACN,OAAO;GACT,CAAC;GACD;EACF;EAEA,MAAM,SAAS,aAAa,IAAI,mBAAmB,MAAM;EACzD,MAAM,WAAW,aAAa,IAAI,aAAa,YAAY;EAK3D,MAAM,CAAC,cAAc,aAAa,OAAO,gBAAgB,MAAM;EAC/D,MAAM,UAAU,cAAc,UAAU,mBAAmB,SAAS,IAAI,UAAU,CAAC,CAAC,SAAS;EAC7F,MAAM,SAAS,WAAW,OAAO,OAAO,IAAI,OAAO,MAAM,SAAS,gBAAgB,MAAM;EACxF,IAAI,CAAC,OACH;EAEF,MAAM,SAAS,MAAM,oBAAoB,OAAO;GAAE;GAAK;EAAM,CAAC;EAC9D,IAAI,CAAC,QACH;EAIF,IAAI,aAAa,oBACf;EAGF,MAAM,QAAQ,aAAa,IAAI,SAAS,OAAO,QAAQ,EAAE,UAAU,WAAW,CAAC;EAE/E,MAAM,cAAc,gBAAgB,eAAe;GACjD,YAAY;GACZ,SAAS,OAAO;GAChB,OAAO;GACP,SAAS;GACT,QAAQ;GACR,GAAI,QAAQ,EAAE,KAAK;GACnB,GAAI,SAAS,EAAE,MAAM;GACrB,GAAI,QAAQ,EAAE,KAAK;GACnB,GAAI,SAAS,EAAE,MAAM;EACvB,CAAC;CACH;CAEA,IAAI;CACJ,IAAI,SAAS,aACX,UAAU,iBACR,SAAS,aACT,oBACA,sBACA,kBACF;MAEA,IAAI,KAAK,yBAAsB,KAAA,GAAA;EAAA,YAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA;CAAA,CAAC;CAGlC,OAAO,OAAO,mBAAmB,OAAO,WAAW,UAAU,CAAC,CAAC;CAC/D,OAAO,CAAC;AACV,CAAC,CACH"}
1
+ {"version":3,"file":"chunk-preview-popover.mjs","names":[],"sources":["../../src/capabilities/preview-popover.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport * as AppSpace from '@dxos/app-toolkit/AppSpace';\nimport * as GraphPath from '@dxos/app-toolkit/GraphPath';\nimport * as Hooks from '@dxos/app-toolkit/Hooks';\nimport * as LayoutOperation from '@dxos/app-toolkit/LayoutOperation';\nimport { addEventListener } from '@dxos/async';\nimport { Obj } from '@dxos/echo';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { log } from '@dxos/log';\nimport * as ClientCapabilities from '@dxos/plugin-client/ClientCapabilities';\nimport { Attention } from '@dxos/react-ui-attention/types';\nimport { DX_ANCHOR_ACTIVATE, type DxAnchorActivate, type PreviewLinkRef, type PreviewLinkTarget } from '@dxos/ui-types';\n\nimport { PreviewCapabilities } from '#types';\n\nconst customEventOptions = { capture: true, passive: false };\n\n/** The first resolver's answer, asked in contribution order; a resolver declines by answering undefined. */\nconst resolveLink = (\n resolvers: PreviewCapabilities.PreviewLinkResolver[],\n ref: PreviewLinkRef,\n context: PreviewCapabilities.PreviewLinkContext,\n): Effect.Effect<PreviewLinkTarget | undefined> =>\n Effect.gen(function* () {\n for (const { match, resolve } of resolvers) {\n if (!match(ref.eid)) {\n continue;\n }\n const target = yield* resolve(ref, context);\n if (target) {\n return target;\n }\n }\n return undefined;\n });\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n // Get context for lazy capability access in callbacks.\n const capabilities = yield* Capability.Service;\n\n // Monotonic activation token: each invocation captures its own sequence; only the\n // most recent activation is allowed to commit popover state. Prevents a slow\n // open (async lookup) from clobbering a later close that fires while it's in flight.\n let activationSequence = 0;\n // The anchor whose card is showing: a close arrives after the anchor's grace period, by which\n // time the pointer may have opened another anchor, and only the shown anchor may close it.\n let activeTrigger: HTMLElement | undefined;\n const handleAnchorActivate = async ({\n eid,\n label,\n trigger,\n kind = 'card',\n title: titleProp,\n side,\n props,\n state,\n navigate,\n }: DxAnchorActivate) => {\n const { invokePromise } = capabilities.get(Capabilities.OperationInvoker);\n\n // Explicit close: callers pass `state: false` on pointer-leave to dismiss\n // the popover. Operation schema requires anchor + kind, so use placeholders;\n // they're overwritten in ephemeral state but only `state` is read by the UI.\n // A close from another anchor is dropped before it can invalidate this one's lookup.\n if (state === false) {\n if (trigger !== activeTrigger) {\n return;\n }\n activeTrigger = undefined;\n activationSequence++;\n await invokePromise(LayoutOperation.UpdatePopover, {\n variant: 'virtual',\n anchor: trigger,\n kind: 'base',\n state: false,\n });\n return;\n }\n\n // Tracked before the lookup, so leaving the anchor while it is in flight is an accepted close\n // that invalidates the pending result rather than a stranger's close that is dropped.\n const sequence = ++activationSequence;\n // Read before the lookup: the anchor sits inside a plank the target should open beside, and may unmount meanwhile.\n const pivotId = navigate ? Attention.getRootAttendableId(trigger) : undefined;\n // Navigation replaces any card showing, so it must not be left open behind the opened object.\n if (navigate && activeTrigger) {\n activeTrigger = undefined;\n await invokePromise(LayoutOperation.UpdatePopover, {\n variant: 'virtual',\n anchor: trigger,\n kind: 'base',\n state: false,\n });\n }\n if (!navigate) {\n activeTrigger = trigger;\n }\n const client = capabilities.get(ClientCapabilities.Client);\n const registry = capabilities.get(Capabilities.AtomRegistry);\n // Layout is optional: in standalone harnesses (Storybook, tests) no plugin contributes\n // `AppCapabilities.Layout`, and `getAll` returns an empty array. Reading `registry.get(undefined)`\n // would crash inside Atom's identity check (`'~atom/Serializable' in undefined`). When layout\n // isn't available, fall through to the default space.\n const [layoutAtom] = capabilities.getAll(AppCapabilities.Layout);\n const spaceId = layoutAtom && GraphPath.getSpaceIdFromPath(registry.get(layoutAtom).workspace);\n const space = (spaceId && client.spaces.get(spaceId)) ?? AppSpace.getDefaultSpace(client);\n const resolvers = capabilities.getAll(PreviewCapabilities.LinkResolver).flat();\n const result = await EffectEx.runPromise(resolveLink(resolvers, { eid, label }, { db: space?.db }));\n // A newer activation (open or close) arrived while the lookup was in flight; bail\n // out so we don't clobber the latest state.\n if (sequence !== activationSequence) {\n return;\n }\n\n if (navigate) {\n if (Obj.isObject(result?.object) && Obj.getDatabase(result.object)) {\n const { invoke } = capabilities.get(Capabilities.OperationInvoker);\n await EffectEx.runPromise(Hooks.openObject(result.object, invoke, { pivotId }));\n return;\n }\n // A target with no stored object to open (e.g. an in-memory GitHub object) still gets its preview.\n activeTrigger = trigger;\n }\n\n if (!result) {\n // A link a resolver claims but cannot answer (a private repository, an object that is gone) is\n // still a chip the reader hovered, so it opens a card that says so rather than nothing at all.\n if (kind === 'card' && PreviewCapabilities.isPreviewLink(resolvers, eid)) {\n await invokePromise(LayoutOperation.UpdatePopover, {\n subjectRef: eid,\n state: true,\n variant: 'virtual',\n anchor: trigger,\n kind,\n title: titleProp ?? PreviewCapabilities.linkLabel(resolvers, eid) ?? (label || eid),\n ...(side && { side }),\n });\n }\n return;\n }\n\n // Same fallback chain as the graph node label (AppNode.getObjectGraphNodePartials), so an\n // unnamed object shows its per-type placeholder (e.g. \"New game\") rather than the raw typename.\n const fallbackTitle: [string, { ns: string; defaultValue?: string }] = [\n 'object-name.placeholder',\n { ns: Obj.getTypename(result.object) ?? '', defaultValue: 'New item' },\n ];\n const title = titleProp ?? Obj.getLabel(result.object) ?? fallbackTitle;\n\n const input = {\n subjectRef: eid,\n subject: result.object,\n state: true,\n variant: 'virtual',\n anchor: trigger,\n ...(side && { side }),\n ...(props && { props }),\n } as const;\n // The union discriminates on `kind`: only the card member carries a title.\n await invokePromise(\n LayoutOperation.UpdatePopover,\n kind === 'card' ? { ...input, kind, title } : { ...input, kind },\n );\n };\n\n let cleanup: () => void;\n if (document.defaultView) {\n cleanup = addEventListener(\n document.defaultView,\n DX_ANCHOR_ACTIVATE as any,\n handleAnchorActivate,\n customEventOptions,\n );\n } else {\n log.warn('no default view found');\n }\n\n yield* Effect.addFinalizer(() => Effect.sync(() => cleanup?.()));\n return [];\n }),\n);\n"],"mappings":"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"}