@formicoidea/labre-core 0.50.0 → 0.52.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 (210) hide show
  1. package/dist/_pkgs/affine-block-attachment/asset-picker.d.ts +24 -0
  2. package/dist/_pkgs/affine-block-attachment/asset-picker.js +87 -0
  3. package/dist/_pkgs/affine-block-attachment/configs/slash-menu.js +3 -20
  4. package/dist/_pkgs/affine-block-attachment/index.d.ts +1 -0
  5. package/dist/_pkgs/affine-block-attachment/index.js +1 -0
  6. package/dist/_pkgs/affine-block-attachment/utils.d.ts +9 -1
  7. package/dist/_pkgs/affine-block-attachment/utils.js +9 -0
  8. package/dist/_pkgs/affine-block-bookmark/adapters/markdown/markdown.js +14 -1
  9. package/dist/_pkgs/affine-block-bookmark/adapters/notion-html.js +5 -1
  10. package/dist/_pkgs/affine-block-database/adapters/markdown.js +5 -3
  11. package/dist/_pkgs/affine-block-embed/common/adapters/html.js +5 -3
  12. package/dist/_pkgs/affine-block-embed/common/adapters/markdown.js +11 -0
  13. package/dist/_pkgs/affine-block-frame/frame-template.d.ts +58 -0
  14. package/dist/_pkgs/affine-block-frame/frame-template.js +207 -0
  15. package/dist/_pkgs/affine-block-frame/frame-toolbar.js +19 -3
  16. package/dist/_pkgs/affine-block-frame/index.d.ts +1 -0
  17. package/dist/_pkgs/affine-block-frame/index.js +1 -0
  18. package/dist/_pkgs/affine-block-frame/translations.d.ts +7 -0
  19. package/dist/_pkgs/affine-block-frame/translations.js +16 -1
  20. package/dist/_pkgs/affine-block-frame/view.js +6 -0
  21. package/dist/_pkgs/affine-block-image/asset-picker.d.ts +63 -0
  22. package/dist/_pkgs/affine-block-image/asset-picker.js +183 -0
  23. package/dist/_pkgs/affine-block-image/commands/insert-images.d.ts +5 -0
  24. package/dist/_pkgs/affine-block-image/commands/insert-images.js +8 -8
  25. package/dist/_pkgs/affine-block-image/components/page-image-block.d.ts +11 -0
  26. package/dist/_pkgs/affine-block-image/components/page-image-block.js +81 -12
  27. package/dist/_pkgs/affine-block-image/configs/replace-action.d.ts +9 -0
  28. package/dist/_pkgs/affine-block-image/configs/replace-action.js +27 -0
  29. package/dist/_pkgs/affine-block-image/configs/toolbar.js +6 -0
  30. package/dist/_pkgs/affine-block-image/crop/crop-controller.d.ts +46 -0
  31. package/dist/_pkgs/affine-block-image/crop/crop-controller.js +104 -0
  32. package/dist/_pkgs/affine-block-image/crop/crop-math.d.ts +70 -0
  33. package/dist/_pkgs/affine-block-image/crop/crop-math.js +141 -0
  34. package/dist/_pkgs/affine-block-image/crop/crop-overlay.d.ts +57 -0
  35. package/dist/_pkgs/affine-block-image/crop/crop-overlay.js +343 -0
  36. package/dist/_pkgs/affine-block-image/crop/crop-toolbar.d.ts +12 -0
  37. package/dist/_pkgs/affine-block-image/crop/crop-toolbar.js +78 -0
  38. package/dist/_pkgs/affine-block-image/crop/write-crop.d.ts +16 -0
  39. package/dist/_pkgs/affine-block-image/crop/write-crop.js +57 -0
  40. package/dist/_pkgs/affine-block-image/edgeless-clipboard-config.js +4 -1
  41. package/dist/_pkgs/affine-block-image/effects.js +2 -0
  42. package/dist/_pkgs/affine-block-image/image-block.d.ts +2 -0
  43. package/dist/_pkgs/affine-block-image/image-block.js +2 -0
  44. package/dist/_pkgs/affine-block-image/image-edgeless-block.d.ts +2 -0
  45. package/dist/_pkgs/affine-block-image/image-edgeless-block.js +36 -8
  46. package/dist/_pkgs/affine-block-image/image-resize-manager.js +15 -0
  47. package/dist/_pkgs/affine-block-image/index.d.ts +1 -0
  48. package/dist/_pkgs/affine-block-image/index.js +1 -0
  49. package/dist/_pkgs/affine-block-image/translations.d.ts +15 -0
  50. package/dist/_pkgs/affine-block-image/translations.js +43 -0
  51. package/dist/_pkgs/affine-block-image/utils.d.ts +13 -0
  52. package/dist/_pkgs/affine-block-image/utils.js +10 -1
  53. package/dist/_pkgs/affine-block-root/clipboard/page-clipboard.js +5 -0
  54. package/dist/_pkgs/affine-block-root/commands/page-export-commands.d.ts +2 -0
  55. package/dist/_pkgs/affine-block-root/commands/page-export-commands.js +71 -0
  56. package/dist/_pkgs/affine-block-root/index.d.ts +1 -0
  57. package/dist/_pkgs/affine-block-root/index.js +1 -0
  58. package/dist/_pkgs/affine-block-root/view.js +8 -0
  59. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/capability.d.ts +30 -0
  60. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/capability.js +44 -0
  61. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/colors.d.ts +26 -0
  62. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/colors.js +82 -0
  63. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/command.d.ts +18 -0
  64. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/command.js +91 -0
  65. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/index.d.ts +3 -0
  66. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/index.js +3 -0
  67. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/writer.d.ts +50 -0
  68. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/writer.js +732 -0
  69. package/dist/_pkgs/affine-block-surface/extensions/export-manager/draw-image-block.d.ts +12 -0
  70. package/dist/_pkgs/affine-block-surface/extensions/export-manager/draw-image-block.js +15 -0
  71. package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.js +7 -1
  72. package/dist/_pkgs/affine-block-surface/extensions/export-svg/command.d.ts +7 -1
  73. package/dist/_pkgs/affine-block-surface/extensions/export-svg/command.js +60 -1
  74. package/dist/_pkgs/affine-block-surface/extensions/export-svg/index.d.ts +2 -2
  75. package/dist/_pkgs/affine-block-surface/extensions/export-svg/index.js +2 -2
  76. package/dist/_pkgs/affine-block-surface/extensions/export-svg/marker-version.d.ts +6 -0
  77. package/dist/_pkgs/affine-block-surface/extensions/export-svg/marker-version.js +8 -0
  78. package/dist/_pkgs/affine-block-surface/extensions/export-svg/render.d.ts +28 -1
  79. package/dist/_pkgs/affine-block-surface/extensions/export-svg/render.js +66 -6
  80. package/dist/_pkgs/affine-block-surface/extensions/export-svg/svg-context.d.ts +2 -7
  81. package/dist/_pkgs/affine-block-surface/extensions/export-svg/svg-context.js +2 -6
  82. package/dist/_pkgs/affine-block-surface/extensions/index.d.ts +2 -0
  83. package/dist/_pkgs/affine-block-surface/extensions/index.js +2 -0
  84. package/dist/_pkgs/affine-block-surface/extensions/interchange-export.d.ts +84 -0
  85. package/dist/_pkgs/affine-block-surface/extensions/interchange-export.js +150 -0
  86. package/dist/_pkgs/affine-block-surface/extensions/interchange-import.js +24 -2
  87. package/dist/_pkgs/affine-block-surface/extensions/interchange.d.ts +23 -2
  88. package/dist/_pkgs/affine-block-surface/extensions/interchange.js +4 -1
  89. package/dist/_pkgs/affine-block-surface/extensions/legend-role.d.ts +18 -0
  90. package/dist/_pkgs/affine-block-surface/extensions/legend-role.js +20 -0
  91. package/dist/_pkgs/affine-block-surface/extensions/legend.d.ts +2 -19
  92. package/dist/_pkgs/affine-block-surface/extensions/legend.js +2 -18
  93. package/dist/_pkgs/affine-block-surface/extensions/svg-sanitize.d.ts +67 -0
  94. package/dist/_pkgs/affine-block-surface/extensions/svg-sanitize.js +235 -0
  95. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch-translations.d.ts +1 -0
  96. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch-translations.js +9 -0
  97. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.d.ts +17 -13
  98. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.js +52 -80
  99. package/dist/_pkgs/affine-block-surface/extensions/xml-element.d.ts +98 -0
  100. package/dist/_pkgs/affine-block-surface/extensions/xml-element.js +261 -0
  101. package/dist/_pkgs/affine-block-surface/extensions/xml-reader.d.ts +238 -0
  102. package/dist/_pkgs/affine-block-surface/extensions/xml-reader.js +570 -0
  103. package/dist/_pkgs/affine-block-surface/framework-background/def.js +1 -1
  104. package/dist/_pkgs/affine-block-surface/index.d.ts +1 -0
  105. package/dist/_pkgs/affine-block-surface/index.js +1 -0
  106. package/dist/_pkgs/affine-block-surface/interchange-headless.d.ts +29 -0
  107. package/dist/_pkgs/affine-block-surface/interchange-headless.js +32 -0
  108. package/dist/_pkgs/affine-block-surface/view.js +22 -2
  109. package/dist/_pkgs/affine-block-table/adapters/utils.js +24 -1
  110. package/dist/_pkgs/affine-gfx-note/toolbar/note-menu.js +5 -8
  111. package/dist/_pkgs/affine-gfx-template/contribute.d.ts +14 -4
  112. package/dist/_pkgs/affine-gfx-template/contribute.js +47 -6
  113. package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.d.ts +7 -2
  114. package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.js +14 -40
  115. package/dist/_pkgs/affine-gfx-template/toolbar/resolve-name.d.ts +1 -1
  116. package/dist/_pkgs/affine-gfx-template/toolbar/resolve-name.js +1 -1
  117. package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.d.ts +4 -4
  118. package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.js +9 -9
  119. package/dist/_pkgs/affine-gfx-template/toolbar/template-type.d.ts +2 -4
  120. package/dist/_pkgs/affine-gfx-template/translations.d.ts +5 -0
  121. package/dist/_pkgs/affine-gfx-template/translations.js +9 -0
  122. package/dist/_pkgs/affine-inline-footnote/adapters/markdown/inline-delta.js +6 -0
  123. package/dist/_pkgs/affine-inline-footnote/adapters/markdown/markdown-inline.js +7 -0
  124. package/dist/_pkgs/affine-inline-link/adapters/html/html-inline.js +4 -1
  125. package/dist/_pkgs/affine-inline-link/adapters/html/inline-delta.js +3 -1
  126. package/dist/_pkgs/affine-inline-link/adapters/markdown/inline-delta.js +4 -1
  127. package/dist/_pkgs/affine-inline-link/adapters/notion-html/html-inline.js +2 -1
  128. package/dist/_pkgs/affine-inline-link/link-node/affine-link.d.ts +7 -0
  129. package/dist/_pkgs/affine-inline-link/link-node/affine-link.js +37 -0
  130. package/dist/_pkgs/affine-inline-link/link-node/index.d.ts +1 -0
  131. package/dist/_pkgs/affine-inline-link/link-node/index.js +1 -0
  132. package/dist/_pkgs/affine-inline-link/link-node/propose-views.d.ts +22 -0
  133. package/dist/_pkgs/affine-inline-link/link-node/propose-views.js +59 -0
  134. package/dist/_pkgs/affine-model/blocks/image/image-crop.d.ts +48 -0
  135. package/dist/_pkgs/affine-model/blocks/image/image-crop.js +78 -0
  136. package/dist/_pkgs/affine-model/blocks/image/image-model.d.ts +6 -0
  137. package/dist/_pkgs/affine-model/blocks/image/image-model.js +2 -0
  138. package/dist/_pkgs/affine-model/blocks/image/index.d.ts +1 -0
  139. package/dist/_pkgs/affine-model/blocks/image/index.js +1 -0
  140. package/dist/_pkgs/affine-model/consts/shape.d.ts +2 -0
  141. package/dist/_pkgs/affine-model/consts/shape.js +5 -0
  142. package/dist/_pkgs/affine-model/elements/bpmn/consts.d.ts +31 -0
  143. package/dist/_pkgs/affine-model/elements/bpmn/consts.js +31 -0
  144. package/dist/_pkgs/affine-model/elements/bpmn/index.d.ts +1 -0
  145. package/dist/_pkgs/affine-model/elements/bpmn/index.js +1 -0
  146. package/dist/_pkgs/affine-model/elements/bpmn/pool.d.ts +0 -25
  147. package/dist/_pkgs/affine-model/elements/bpmn/pool.js +1 -25
  148. package/dist/_pkgs/affine-model/elements/shape/shape.d.ts +0 -2
  149. package/dist/_pkgs/affine-model/elements/shape/shape.js +1 -2
  150. package/dist/_pkgs/affine-model/elements/uml/consts.d.ts +133 -0
  151. package/dist/_pkgs/affine-model/elements/uml/consts.js +107 -0
  152. package/dist/_pkgs/affine-model/elements/uml/diagram.d.ts +1 -79
  153. package/dist/_pkgs/affine-model/elements/uml/diagram.js +1 -53
  154. package/dist/_pkgs/affine-model/elements/uml/fragment.d.ts +0 -15
  155. package/dist/_pkgs/affine-model/elements/uml/fragment.js +1 -15
  156. package/dist/_pkgs/affine-model/elements/uml/index.d.ts +1 -0
  157. package/dist/_pkgs/affine-model/elements/uml/index.js +1 -0
  158. package/dist/_pkgs/affine-model/elements/uml/partition.d.ts +0 -20
  159. package/dist/_pkgs/affine-model/elements/uml/partition.js +1 -20
  160. package/dist/_pkgs/affine-model/elements/uml/region.d.ts +0 -11
  161. package/dist/_pkgs/affine-model/elements/uml/region.js +1 -11
  162. package/dist/_pkgs/affine-model/headless.d.ts +19 -0
  163. package/dist/_pkgs/affine-model/headless.js +19 -0
  164. package/dist/_pkgs/affine-shared/headless.d.ts +10 -0
  165. package/dist/_pkgs/affine-shared/headless.js +10 -0
  166. package/dist/_pkgs/affine-shared/services/asset-picker-service.d.ts +63 -0
  167. package/dist/_pkgs/affine-shared/services/asset-picker-service.js +20 -0
  168. package/dist/_pkgs/affine-shared/services/frame-template-service.d.ts +83 -0
  169. package/dist/_pkgs/affine-shared/services/frame-template-service.js +17 -0
  170. package/dist/_pkgs/affine-shared/services/index.d.ts +3 -0
  171. package/dist/_pkgs/affine-shared/services/index.js +3 -0
  172. package/dist/_pkgs/affine-shared/services/thread-marks-service.d.ts +69 -0
  173. package/dist/_pkgs/affine-shared/services/thread-marks-service.js +20 -0
  174. package/dist/_pkgs/affine-shared/services/translation-service/chrome.d.ts +6 -0
  175. package/dist/_pkgs/affine-shared/services/translation-service/chrome.js +10 -0
  176. package/dist/_pkgs/affine-widget-drag-handle/watchers/drag-event-watcher.js +8 -3
  177. package/dist/_pkgs/affine-widget-keyboard-toolbar/config.js +4 -16
  178. package/dist/_pkgs/affine-widget-linked-doc/transformers/html.d.ts +7 -0
  179. package/dist/_pkgs/affine-widget-linked-doc/transformers/html.js +25 -24
  180. package/dist/_pkgs/affine-widget-linked-doc/transformers/index.d.ts +1 -1
  181. package/dist/_pkgs/affine-widget-linked-doc/transformers/markdown.d.ts +7 -0
  182. package/dist/_pkgs/affine-widget-linked-doc/transformers/markdown.js +26 -25
  183. package/dist/_pkgs/affine-widget-linked-doc/transformers/utils.d.ts +23 -0
  184. package/dist/_pkgs/affine-widget-linked-doc/transformers/utils.js +14 -0
  185. package/dist/_pkgs/affine-widget-thread-marks/effects.d.ts +1 -0
  186. package/dist/_pkgs/affine-widget-thread-marks/effects.js +4 -0
  187. package/dist/_pkgs/affine-widget-thread-marks/excerpt.d.ts +19 -0
  188. package/dist/_pkgs/affine-widget-thread-marks/excerpt.js +68 -0
  189. package/dist/_pkgs/affine-widget-thread-marks/index.d.ts +5 -0
  190. package/dist/_pkgs/affine-widget-thread-marks/index.js +8 -0
  191. package/dist/_pkgs/affine-widget-thread-marks/toolbar.d.ts +49 -0
  192. package/dist/_pkgs/affine-widget-thread-marks/toolbar.js +108 -0
  193. package/dist/_pkgs/affine-widget-thread-marks/translations.d.ts +9 -0
  194. package/dist/_pkgs/affine-widget-thread-marks/translations.js +11 -0
  195. package/dist/_pkgs/affine-widget-thread-marks/view.d.ts +12 -0
  196. package/dist/_pkgs/affine-widget-thread-marks/view.js +25 -0
  197. package/dist/_pkgs/affine-widget-thread-marks/widget.d.ts +51 -0
  198. package/dist/_pkgs/affine-widget-thread-marks/widget.js +214 -0
  199. package/dist/commands.js +5 -2
  200. package/dist/extensions/view.js +2 -0
  201. package/dist/host-panels.d.ts +24 -1
  202. package/dist/host-panels.js +61 -10
  203. package/dist/interchange.d.ts +1 -0
  204. package/dist/interchange.js +1 -0
  205. package/dist/model/headless.d.ts +1 -0
  206. package/dist/model/headless.js +1 -0
  207. package/dist/shared/headless.d.ts +1 -0
  208. package/dist/shared/headless.js +1 -0
  209. package/dist/translations.js +10 -1
  210. package/package.json +13 -1
@@ -1,10 +1,10 @@
1
- import { ExportToPdfIcon, ExportToPngIcon, GridIcon, } from '@blocksuite/icons/lit';
1
+ import { ExportIcon, ExportToPdfIcon, ExportToPngIcon, ExportToSvgIcon, GridIcon, } from '@blocksuite/icons/lit';
2
2
  import { ViewExtensionProvider, } from '../affine-ext-loader/index.js';
3
3
  import { CanvasGrid, CanvasLocalVisibility, } from '../affine-shared/services/index.js';
4
4
  import { BlockViewExtension, CommandExtension, FlavourExtension, } from '../std/index.js';
5
5
  import { literal } from 'lit/static-html.js';
6
6
  import { effects } from './effects.js';
7
- import { auditCommands, CanvasActiveLayer, canvasExportCommands, canvasGridCommands, EdgelessCRUDExtension, EdgelessLegacySlotExtension, EditPropsMiddlewareBuilder, exportSvgCommands, exportSvgToolbarExtension, mapQualityCommands, mapQualityWidget, ReadingManager, readingProposalWidget, readingToolbarExtension, selectionPaneCommands, SelectionPaneModel, SpotlightManager, UserLayerMiddlewareBuilder, userLayerCommands, ValidationManager, validationExceptionToolbarExtension, ValidationOverlay, violationDetailWidget, } from './extensions/index.js';
7
+ import { auditCommands, CanvasActiveLayer, canvasDrawioExportCommands, canvasExportCommands, CANVAS_INTERCHANGE, canvasGridCommands, canvasSvgExportCommands, EdgelessCRUDExtension, EdgelessLegacySlotExtension, EditPropsMiddlewareBuilder, exportSvgCommands, INTERCHANGE_EXPORT_ICON_KEY, InterchangeExtension, exportSvgToolbarExtension, mapQualityCommands, mapQualityWidget, ReadingManager, readingProposalWidget, readingToolbarExtension, selectionPaneCommands, SelectionPaneModel, SpotlightManager, UserLayerMiddlewareBuilder, userLayerCommands, ValidationManager, validationExceptionToolbarExtension, ValidationOverlay, violationDetailWidget, } from './extensions/index.js';
8
8
  import { ExportManagerExtension } from './extensions/export-manager/export-manager.js';
9
9
  import { BackgroundStackingExtension } from './framework-background/stacking.js';
10
10
  import { ElementHighlightOverlay } from './renderer/element-highlight-overlay.js';
@@ -97,6 +97,26 @@ export class SurfaceViewExtension extends ViewExtensionProvider {
97
97
  'canvas.export.png': ExportToPngIcon(),
98
98
  'canvas.export.pdf': ExportToPdfIcon(),
99
99
  }));
100
+ // The canvas as a draw.io drawing (ADR 0019, amendment of 2026-10-14):
101
+ // the third "This view" export, and the registry's one capability no
102
+ // framework owns. Never gated, for the PNG's reason — it writes what the
103
+ // canvas paints, whatever drew it — so with every framework off the
104
+ // registry still answers `canvas:drawio:export`.
105
+ context.register(InterchangeExtension(CANVAS_INTERCHANGE));
106
+ context.register(CommandExtension(canvasDrawioExportCommands, {
107
+ 'canvas.export.drawio': ExportIcon(),
108
+ // The icon of the view actions derived from the frameworks'
109
+ // `*:export` capabilities (`interchange-export.ts`): they are
110
+ // listed by the host façade, never registered as commands, so their
111
+ // one icon rides on this table.
112
+ [INTERCHANGE_EXPORT_ICON_KEY]: ExportIcon(),
113
+ }));
114
+ // The whole canvas as SVG (ADR 0034, amendment of 2026-10-14): the
115
+ // renderer replay of `export.svg`, over everything this viewer sees.
116
+ // Never gated, for the PNG's reason.
117
+ context.register(CommandExtension(canvasSvgExportCommands, {
118
+ 'canvas.export.svg': ExportToSvgIcon(),
119
+ }));
100
120
  // User layers (ADR 0031 §2-§6): the viewer's active layer (session
101
121
  // only), the `beforeAdd` middleware that stamps it on what is created,
102
122
  // and the commands the pane's layer rows run. Content, never gated.
@@ -130,12 +130,35 @@ export const parseTableFromHtml = (element, astToDelta) => {
130
130
  });
131
131
  return createTableProps(rowTextLists);
132
132
  };
133
+ /**
134
+ * A cell holds rich text, and no inline matcher turns an `image` into a delta:
135
+ * `![alt](url)` in a pasted Markdown table used to vanish from its cell. It is
136
+ * kept as a link named by its alt text (its URL when it has none), so nothing
137
+ * of the source is lost; the link matcher still refuses an unsafe scheme and
138
+ * keeps the words (labre REQ-IM-2, "no data loss").
139
+ */
140
+ function imagesAsLinks(node) {
141
+ if (node.type === 'image') {
142
+ return {
143
+ type: 'link',
144
+ url: node.url,
145
+ title: node.title,
146
+ children: [{ type: 'text', value: node.alt || node.url }],
147
+ };
148
+ }
149
+ if (!('children' in node))
150
+ return node;
151
+ return {
152
+ ...node,
153
+ children: node.children.map(child => imagesAsLinks(child)),
154
+ };
155
+ }
133
156
  export const parseTableFromMarkdown = (node, astToDelta) => {
134
157
  const rowTextLists = [];
135
158
  node.children.forEach(row => {
136
159
  const rowText = [];
137
160
  row.children.forEach(cell => {
138
- rowText.push(astToDelta(cell));
161
+ rowText.push(astToDelta(imagesAsLinks(cell)));
139
162
  });
140
163
  rowTextLists.push(rowText);
141
164
  });
@@ -32,13 +32,12 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { addAttachments } from '../../affine-block-attachment/index.js';
35
+ import { pickAndAddAttachments } from '../../affine-block-attachment/index.js';
36
36
  import { insertLinkByQuickSearchCommand } from '../../affine-block-bookmark/index.js';
37
- import { addImages } from '../../affine-block-image/index.js';
37
+ import { pickAndAddImages } from '../../affine-block-image/index.js';
38
38
  import { DefaultTool } from '../../affine-block-surface/index.js';
39
39
  import { MAX_IMAGE_WIDTH } from '../../affine-model/index.js';
40
40
  import { TelemetryProvider, translateKey } from '../../affine-shared/services/index.js';
41
- import { getImageFilesFromLocal, openSingleFileWith, } from '../../affine-shared/utils/index.js';
42
41
  import { EdgelessToolbarToolMixin } from '../../affine-widget-edgeless-toolbar/index.js';
43
42
  import { AttachmentIcon, ImageIcon, LinkIcon } from '@blocksuite/icons/lit';
44
43
  import { effect } from '@preact/signals-core';
@@ -112,8 +111,7 @@ let EdgelessNoteMenu = (() => {
112
111
  `; }
113
112
  async _addImages() {
114
113
  this._imageLoading = true;
115
- const imageFiles = await getImageFilesFromLocal();
116
- const ids = await addImages(this.edgeless.std, imageFiles, {
114
+ const ids = await pickAndAddImages(this.edgeless.std, {
117
115
  maxWidth: MAX_IMAGE_WIDTH,
118
116
  });
119
117
  this._imageLoading = false;
@@ -185,10 +183,9 @@ let EdgelessNoteMenu = (() => {
185
183
  .activeMode=${'background'}
186
184
  .tooltip=${translateKey(this.edgeless.std, ...GFX_NOTE_MENU_FILE)}
187
185
  @click=${async () => {
188
- const file = await openSingleFileWith();
189
- if (!file)
186
+ const ids = await pickAndAddAttachments(this.edgeless.std);
187
+ if (!ids.length)
190
188
  return;
191
- await addAttachments(this.edgeless.std, [file]);
192
189
  this.gfx.tool.setTool(DefaultTool);
193
190
  this.edgeless.std
194
191
  .getOptional(TelemetryProvider)
@@ -26,9 +26,19 @@ export declare const TemplateCategoryIdentifier: import("../global/di/types.js")
26
26
  */
27
27
  export declare function TemplateCategoryExtension(...categories: TemplateCategory[]): ExtensionType;
28
28
  /**
29
- * The catalogue ONE editor sees: the built-in categories (`Other`, plus
30
- * whatever a host appended through `EdgelessTemplatePanel.templates.extend`)
31
- * followed by the categories the view extensions mounted on this `std`
32
- * registered — i.e. exactly the frameworks whose tooling is on.
29
+ * The identity of the host's category of saved frames (the
30
+ * `FrameTemplateProvider` seam): the stable name the panel remembers and
31
+ * lists by. Its tab reads {@link TEMPLATE_PANEL_CATEGORY_SAVED_FRAMES}.
32
+ */
33
+ export declare const SAVED_FRAMES_CATEGORY = "Saved frames";
34
+ /**
35
+ * The catalogue ONE editor sees: the built-in `Other`, then the categories
36
+ * the view extensions mounted on this `std` registered — i.e. exactly the
37
+ * frameworks whose tooling is on — then the host's saved frames, when a
38
+ * host keeps them (`FrameTemplateProvider`).
39
+ *
40
+ * Per editor, from its DI container: the module-level `extend(...)` of
41
+ * 0.49 and earlier, which appended for every editor and was never emptied,
42
+ * is gone (a host category is a `TemplateCategoryExtension`).
33
43
  */
34
44
  export declare function templateManagerFor(std: BlockStdScope): TemplateManager;
@@ -1,6 +1,8 @@
1
+ import { FrameTemplateProvider, } from '../affine-shared/services/index.js';
1
2
  import { createIdentifier } from '../global/di/index.js';
2
3
  import { builtInTemplates } from './toolbar/builtin-templates.js';
3
4
  import { resolveTemplateName } from './toolbar/resolve-name.js';
5
+ import { TEMPLATE_PANEL_CATEGORY_SAVED_FRAMES } from './translations.js';
4
6
  /**
5
7
  * A framework registers its Templates-panel categories here; the panel reads
6
8
  * them back from the std of the edgeless it opens on. One identifier per
@@ -33,20 +35,61 @@ export function TemplateCategoryExtension(...categories) {
33
35
  },
34
36
  };
35
37
  }
38
+ /**
39
+ * The identity of the host's category of saved frames (the
40
+ * `FrameTemplateProvider` seam): the stable name the panel remembers and
41
+ * lists by. Its tab reads {@link TEMPLATE_PANEL_CATEGORY_SAVED_FRAMES}.
42
+ */
43
+ export const SAVED_FRAMES_CATEGORY = 'Saved frames';
44
+ function savedFrameTemplate(service, saved) {
45
+ return {
46
+ name: saved.title,
47
+ type: 'template',
48
+ content: saved.snapshot,
49
+ preview: saved.preview,
50
+ // The panel runs `afterInsert` only once the template job has resolved.
51
+ afterInsert: service.frameInserted
52
+ ? std => service.frameInserted?.({
53
+ docId: std.store.id,
54
+ templateId: saved.id,
55
+ })
56
+ : undefined,
57
+ };
58
+ }
59
+ /**
60
+ * The host's saved frames as a category, or none without a host. Lazy: the
61
+ * host is asked only when the panel shows the tab or searches across tabs.
62
+ */
63
+ function savedFramesCategory(std) {
64
+ const service = std.provider.getOptional(FrameTemplateProvider);
65
+ if (!service)
66
+ return null;
67
+ return {
68
+ name: SAVED_FRAMES_CATEGORY,
69
+ nameKey: TEMPLATE_PANEL_CATEGORY_SAVED_FRAMES[0],
70
+ templates: async () => (await service.list()).map(saved => savedFrameTemplate(service, saved)),
71
+ };
72
+ }
36
73
  function loadCategory(category) {
37
74
  return Array.isArray(category.templates)
38
75
  ? Promise.resolve(category.templates)
39
76
  : category.templates();
40
77
  }
41
78
  /**
42
- * The catalogue ONE editor sees: the built-in categories (`Other`, plus
43
- * whatever a host appended through `EdgelessTemplatePanel.templates.extend`)
44
- * followed by the categories the view extensions mounted on this `std`
45
- * registered — i.e. exactly the frameworks whose tooling is on.
79
+ * The catalogue ONE editor sees: the built-in `Other`, then the categories
80
+ * the view extensions mounted on this `std` registered — i.e. exactly the
81
+ * frameworks whose tooling is on — then the host's saved frames, when a
82
+ * host keeps them (`FrameTemplateProvider`).
83
+ *
84
+ * Per editor, from its DI container: the module-level `extend(...)` of
85
+ * 0.49 and earlier, which appended for every editor and was never emptied,
86
+ * is gone (a host category is a `TemplateCategoryExtension`).
46
87
  */
47
88
  export function templateManagerFor(std) {
89
+ const saved = savedFramesCategory(std);
48
90
  const contributed = [
49
91
  ...std.provider.getAll(TemplateCategoryIdentifier).values(),
92
+ ...(saved ? [saved] : []),
50
93
  ];
51
94
  const byName = (name) => contributed.find(c => c.name === name);
52
95
  return {
@@ -57,8 +100,6 @@ export function templateManagerFor(std) {
57
100
  ]),
58
101
  ],
59
102
  categoryLabel: name => byName(name)?.nameKey ?? builtInTemplates.categoryLabel(name),
60
- // Both halves, so a host's `extend(...)` can still append to a framework's
61
- // own category (the global half answers `[]` for a name it does not know).
62
103
  list: async (name) => {
63
104
  const category = byName(name);
64
105
  return [
@@ -1,9 +1,14 @@
1
- import type { Template, TemplateCategory, TemplateManager } from './template-type.js';
1
+ import type { Template, TemplateCategory } from './template-type.js';
2
2
  export declare const templates: TemplateCategory[];
3
+ /**
4
+ * The catalogue every editor has, whatever is mounted: `Other`. A host or a
5
+ * framework adds a category per editor with `TemplateCategoryExtension`; the
6
+ * module-level `extend(...)` this object carried until 0.49 appended for every
7
+ * editor of the page and was never emptied.
8
+ */
3
9
  export declare const builtInTemplates: {
4
10
  list: (category: string) => Promise<Template[]>;
5
11
  categories: () => Promise<string[]>;
6
12
  categoryLabel: (name: string) => string | undefined;
7
13
  search: (keyword: string, cateName?: string) => Promise<Template[]>;
8
- extend(manager: TemplateManager): void;
9
14
  };
@@ -16,48 +16,27 @@ function lcs(text1, text2) {
16
16
  }
17
17
  return dp[text1.length][text2.length];
18
18
  }
19
- const extendTemplate = [];
20
- const flat = (arr) => arr.reduce((pre, current) => {
21
- if (current) {
22
- return pre.concat(current);
23
- }
24
- return pre;
25
- }, []);
19
+ /**
20
+ * The catalogue every editor has, whatever is mounted: `Other`. A host or a
21
+ * framework adds a category per editor with `TemplateCategoryExtension`; the
22
+ * module-level `extend(...)` this object carried until 0.49 appended for every
23
+ * editor of the page and was never emptied.
24
+ */
26
25
  export const builtInTemplates = {
27
26
  list: async (category) => {
28
- const extendTemplates = flat(await Promise.all(extendTemplate.map(manager => manager.list(category))));
29
- // eslint-disable-next-line sonarjs/no-empty-collection
30
27
  const cate = templates.find(cate => cate.name === category);
31
28
  if (!cate)
32
- return extendTemplates;
33
- const result = cate.templates instanceof Function
34
- ? await cate.templates()
35
- : await Promise.all(Object.values(cate.templates));
36
- return result.concat(extendTemplates);
37
- },
38
- categories: async () => {
39
- const extendCates = flat(await Promise.all(extendTemplate.map(manager => manager.categories())));
40
- // eslint-disable-next-line sonarjs/no-empty-collection
41
- return templates.map(cate => cate.name).concat(extendCates);
42
- },
43
- categoryLabel: (name) => {
44
- // eslint-disable-next-line sonarjs/no-empty-collection
45
- const own = templates.find(cate => cate.name === name)?.nameKey;
46
- if (own)
47
- return own;
48
- for (const manager of extendTemplate) {
49
- const key = manager.categoryLabel?.(name);
50
- if (key)
51
- return key;
52
- }
53
- return undefined;
29
+ return [];
30
+ return cate.templates instanceof Function
31
+ ? cate.templates()
32
+ : cate.templates;
54
33
  },
34
+ categories: async () => templates.map(cate => cate.name),
35
+ categoryLabel: (name) => templates.find(cate => cate.name === name)?.nameKey,
55
36
  search: async (keyword, cateName) => {
56
- const candidates = flat(await Promise.all(extendTemplate.map(manager => manager.search(keyword, cateName))));
37
+ const candidates = [];
57
38
  keyword = keyword.trim().toLocaleLowerCase();
58
- await Promise.all(
59
- // eslint-disable-next-line sonarjs/no-empty-collection
60
- templates.map(async (categroy) => {
39
+ await Promise.all(templates.map(async (categroy) => {
61
40
  if (cateName && cateName !== categroy.name) {
62
41
  return;
63
42
  }
@@ -73,9 +52,4 @@ export const builtInTemplates = {
73
52
  }));
74
53
  return candidates;
75
54
  },
76
- extend(manager) {
77
- if (extendTemplate.includes(manager))
78
- return;
79
- extendTemplate.push(manager);
80
- },
81
55
  };
@@ -10,7 +10,7 @@ import type { Template } from './template-type.js';
10
10
  * through this generic seam.
11
11
  *
12
12
  * `Template.name` itself stays the STABLE identity `templateFromCommand`
13
- * bakes in (the drag payload, the `repeat` key, the `templateCache` storage
13
+ * bakes in (the drag payload, the search, the `templateCache` storage
14
14
  * key) — and it is ALSO what gets passed as `translateKey`'s fallback, never
15
15
  * `command.labelFallback`: most derived templates leave `name` at its default
16
16
  * (the command's own fallback, so the two agree anyway), but a few tiles
@@ -10,7 +10,7 @@ import { getRegisteredCommands } from '../../std/index.js';
10
10
  * through this generic seam.
11
11
  *
12
12
  * `Template.name` itself stays the STABLE identity `templateFromCommand`
13
- * bakes in (the drag payload, the `repeat` key, the `templateCache` storage
13
+ * bakes in (the drag payload, the search, the `templateCache` storage
14
14
  * key) — and it is ALSO what gets passed as `translateKey`'s fallback, never
15
15
  * `command.labelFallback`: most derived templates leave `name` at its default
16
16
  * (the command's own fallback, so the two agree anyway), but a few tiles
@@ -6,16 +6,16 @@ declare const EdgelessTemplatePanel_base: typeof LitElement & import("../../glob
6
6
  export declare class EdgelessTemplatePanel extends EdgelessTemplatePanel_base {
7
7
  static styles: import("lit").CSSResult;
8
8
  /**
9
- * The GLOBAL half of the catalogue — `Other` and a host's `extend(...)`.
10
- * Framework categories are not here: they live in the editor's DI container
11
- * (`TemplateCategoryExtension`) and are read through {@link catalogue}.
9
+ * The GLOBAL half of the catalogue — `Other`. Framework and host
10
+ * categories are not here: they live in the editor's DI container
11
+ * (`TemplateCategoryExtension`, `FrameTemplateProvider`) and are read
12
+ * through {@link catalogue}.
12
13
  */
13
14
  static templates: {
14
15
  list: (category: string) => Promise<Template[]>;
15
16
  categories: () => Promise<string[]>;
16
17
  categoryLabel: (name: string) => string | undefined;
17
18
  search: (keyword: string, cateName?: string) => Promise<Template[]>;
18
- extend(manager: import("./template-type.js").TemplateManager): void;
19
19
  };
20
20
  /** What THIS edgeless offers: the global half plus its std's categories. */
21
21
  get catalogue(): import("./template-type.js").TemplateManager;
@@ -265,9 +265,10 @@ let EdgelessTemplatePanel = (() => {
265
265
  }
266
266
  `; }
267
267
  /**
268
- * The GLOBAL half of the catalogue — `Other` and a host's `extend(...)`.
269
- * Framework categories are not here: they live in the editor's DI container
270
- * (`TemplateCategoryExtension`) and are read through {@link catalogue}.
268
+ * The GLOBAL half of the catalogue — `Other`. Framework and host
269
+ * categories are not here: they live in the editor's DI container
270
+ * (`TemplateCategoryExtension`, `FrameTemplateProvider`) and are read
271
+ * through {@link catalogue}.
271
272
  */
272
273
  static { this.templates = builtInTemplates; }
273
274
  /** What THIS edgeless offers: the global half plus its std's categories. */
@@ -485,10 +486,10 @@ let EdgelessTemplatePanel = (() => {
485
486
  })}
486
487
  ></affine-template-loading>`
487
488
  : repeat(_templates,
488
- // The drag payload, `templateCache` and `isBeingDragged`
489
- // below all key off this SAME identity — never the
490
- // resolved (possibly translated) display name.
491
- template => template.name, template => {
489
+ // The template object itself: two saved frames may share
490
+ // a title (`FrameTemplateProvider`), and a name key
491
+ // would draw one tile for both.
492
+ template => template, template => {
492
493
  const preview = template.preview
493
494
  ? template.preview.startsWith('<svg')
494
495
  ? html `${unsafeSVG(template.preview)}`
@@ -498,8 +499,7 @@ let EdgelessTemplatePanel = (() => {
498
499
  loading="lazy"
499
500
  />`
500
501
  : defaultPreview;
501
- const isBeingDragged = draggingElement &&
502
- draggingElement.data.name === template.name;
502
+ const isBeingDragged = draggingElement && draggingElement.data === template;
503
503
  // The one place that asks the host's catalogue for the
504
504
  // WORDING above the identity — see `resolveTemplateName`.
505
505
  const displayName = resolveTemplateName(this.edgeless.std, template);
@@ -70,9 +70,8 @@ export type TemplateCategory = {
70
70
  * key (`com.labre.framework.<id>`, the senior button's); a generic one
71
71
  * (`Other`) gets a key of its own. Absent for a category whose display name
72
72
  * cannot be attributed to one owner (Cynefin and Estuarine share a single
73
- * `cynefin-estuarine` framework key that names neither individually) or that
74
- * a host contributed through {@link TemplateManager.extend} — either way
75
- * `name` is shown as is.
73
+ * `cynefin-estuarine` framework key that names neither individually), in
74
+ * which case `name` is shown as is.
76
75
  */
77
76
  nameKey?: string;
78
77
  templates: Template[] | (() => Promise<Template[]>);
@@ -83,5 +82,4 @@ export interface TemplateManager {
83
82
  search(keyword: string, category?: string): Promise<Template[]> | Template[];
84
83
  /** {@link TemplateCategory.nameKey} of the category named `name`, if any. */
85
84
  categoryLabel?(name: string): string | undefined;
86
- extend?(manager: TemplateManager): void;
87
85
  }
@@ -18,6 +18,11 @@ export declare const TEMPLATE_PANEL_SEARCH_PLACEHOLDER: ChromeWording;
18
18
  * same word as {@link CATALOGUE_OTHER}, one key.
19
19
  */
20
20
  export declare const TEMPLATE_PANEL_CATEGORY_OTHER: ChromeWording;
21
+ /**
22
+ * The tab of the frames a host keeps as templates (`FrameTemplateProvider`,
23
+ * `SAVED_FRAMES_CATEGORY`); there is no such tab without that host.
24
+ */
25
+ export declare const TEMPLATE_PANEL_CATEGORY_SAVED_FRAMES: ChromeWording;
21
26
  /** The "Template" senior tool's own button name (`SeniorTool.labelKey`). */
22
27
  export declare const TEMPLATE_SENIOR_TOOL_NAME: ChromeWording;
23
28
  export declare const TEMPLATE_NAME_SWOT: ChromeWording;
@@ -24,6 +24,14 @@ export const TEMPLATE_PANEL_SEARCH_PLACEHOLDER = [
24
24
  * same word as {@link CATALOGUE_OTHER}, one key.
25
25
  */
26
26
  export const TEMPLATE_PANEL_CATEGORY_OTHER = CATALOGUE_OTHER;
27
+ /**
28
+ * The tab of the frames a host keeps as templates (`FrameTemplateProvider`,
29
+ * `SAVED_FRAMES_CATEGORY`); there is no such tab without that host.
30
+ */
31
+ export const TEMPLATE_PANEL_CATEGORY_SAVED_FRAMES = [
32
+ 'com.labre.template.panel.category.saved-frames',
33
+ 'Saved frames',
34
+ ];
27
35
  /** The "Template" senior tool's own button name (`SeniorTool.labelKey`). */
28
36
  export const TEMPLATE_SENIOR_TOOL_NAME = [
29
37
  'com.labre.template.senior-tool.name',
@@ -65,6 +73,7 @@ export const TEMPLATE_NAME_GANTT_CHART = [
65
73
  export const TEMPLATE_PACKAGE_WORDINGS = [
66
74
  TEMPLATE_PANEL_ADD,
67
75
  TEMPLATE_PANEL_SEARCH_PLACEHOLDER,
76
+ TEMPLATE_PANEL_CATEGORY_SAVED_FRAMES,
68
77
  TEMPLATE_SENIOR_TOOL_NAME,
69
78
  TEMPLATE_NAME_SWOT,
70
79
  TEMPLATE_NAME_KANBAN_BOARD,
@@ -1,4 +1,5 @@
1
1
  import { FOOTNOTE_DEFINITION_PREFIX, InlineDeltaToMarkdownAdapterExtension, } from '../../../affine-shared/adapters/index.js';
2
+ import { isSafeLinkUrl } from '../../../affine-shared/utils/index.js';
2
3
  export const footnoteReferenceDeltaToMarkdownAdapterMatcher = InlineDeltaToMarkdownAdapterExtension({
3
4
  name: 'footnote-reference',
4
5
  match: delta => !!delta.attributes?.footnote,
@@ -19,6 +20,11 @@ export const footnoteReferenceDeltaToMarkdownAdapterMatcher = InlineDeltaToMarkd
19
20
  if (!configs.has(footnoteDefinitionKey)) {
20
21
  // clone the footnote reference
21
22
  const clonedFootnoteReference = { ...footnote.reference };
23
+ // A stored url with a refused scheme is not written back out.
24
+ if (clonedFootnoteReference.url &&
25
+ !isSafeLinkUrl(clonedFootnoteReference.url)) {
26
+ delete clonedFootnoteReference.url;
27
+ }
22
28
  // If the footnote reference contains url, encode it
23
29
  if (clonedFootnoteReference.url) {
24
30
  clonedFootnoteReference.url = encodeURIComponent(clonedFootnoteReference.url);
@@ -1,5 +1,6 @@
1
1
  import { FootNoteReferenceParamsSchema } from '../../../affine-model/index.js';
2
2
  import { FOOTNOTE_DEFINITION_PREFIX, MarkdownASTToDeltaExtension, } from '../../../affine-shared/adapters/index.js';
3
+ import { isSafeLinkUrl } from '../../../affine-shared/utils/index.js';
3
4
  export const markdownFootnoteReferenceToDeltaMatcher = MarkdownASTToDeltaExtension({
4
5
  name: 'footnote-reference',
5
6
  match: ast => ast.type === 'footnoteReference',
@@ -23,6 +24,12 @@ export const markdownFootnoteReferenceToDeltaMatcher = MarkdownASTToDeltaExtensi
23
24
  footnoteDefinitionJson.favicon = decodeURIComponent(footnoteDefinitionJson.favicon);
24
25
  }
25
26
  const footnoteReference = FootNoteReferenceParamsSchema.parse(footnoteDefinitionJson);
27
+ // A refused scheme is not stored: the footnote keeps its label and
28
+ // loses the url, which the render side would refuse anyway.
29
+ if (footnoteReference.url !== undefined &&
30
+ !isSafeLinkUrl(footnoteReference.url)) {
31
+ delete footnoteReference.url;
32
+ }
26
33
  // If the footnote reference is an attachment, and the file type is not set,
27
34
  // Try to infer the file type from the file name.
28
35
  const { type: referenceType, fileName, fileType } = footnoteReference;
@@ -1,4 +1,5 @@
1
1
  import { HtmlASTToDeltaExtension, } from '../../../affine-shared/adapters/index.js';
2
+ import { isSafeLinkUrl } from '../../../affine-shared/utils/index.js';
2
3
  const isElement = (ast) => {
3
4
  return ast.type === 'element';
4
5
  };
@@ -47,7 +48,9 @@ export const htmlLinkElementToDeltaMatcher = HtmlASTToDeltaExtension({
47
48
  }
48
49
  }
49
50
  return ast.children.flatMap(child => context.toDelta(child, { trim: false }).map(delta => {
50
- if (href.startsWith('http')) {
51
+ // http(s) only, and by the parsed scheme: a prefix test let
52
+ // `httpx:` or `http-javascript:` through.
53
+ if (/^https?:/i.test(href.trim()) && isSafeLinkUrl(href)) {
51
54
  delta.attributes = {
52
55
  ...delta.attributes,
53
56
  link: href,
@@ -1,4 +1,5 @@
1
1
  import { InlineDeltaToHtmlAdapterExtension } from '../../../affine-shared/adapters/index.js';
2
+ import { isSafeLinkUrl } from '../../../affine-shared/utils/index.js';
2
3
  export const linkDeltaToHtmlAdapterMatcher = InlineDeltaToHtmlAdapterExtension({
3
4
  name: 'link',
4
5
  match: delta => !!delta.attributes?.link,
@@ -8,7 +9,8 @@ export const linkDeltaToHtmlAdapterMatcher = InlineDeltaToHtmlAdapterExtension({
8
9
  value: delta.insert,
9
10
  };
10
11
  const link = delta.attributes?.link;
11
- if (!link) {
12
+ // A stored link with a refused scheme is written as its text alone.
13
+ if (!link || !isSafeLinkUrl(link)) {
12
14
  return hast;
13
15
  }
14
16
  return {
@@ -1,4 +1,5 @@
1
1
  import { InlineDeltaToMarkdownAdapterExtension } from '../../../affine-shared/adapters/index.js';
2
+ import { isSafeLinkUrl } from '../../../affine-shared/utils/index.js';
2
3
  export const linkDeltaToMarkdownAdapterMatcher = InlineDeltaToMarkdownAdapterExtension({
3
4
  name: 'link',
4
5
  match: delta => !!delta.attributes?.link,
@@ -8,7 +9,9 @@ export const linkDeltaToMarkdownAdapterMatcher = InlineDeltaToMarkdownAdapterExt
8
9
  value: delta.insert,
9
10
  };
10
11
  const link = delta.attributes?.link;
11
- if (!link) {
12
+ // A stored link with a refused scheme (an older document, a paste) is
13
+ // written as its text alone, never re-emitted as a link.
14
+ if (!link || !isSafeLinkUrl(link)) {
12
15
  return mdast;
13
16
  }
14
17
  const { current: currentMdast } = context;
@@ -1,4 +1,5 @@
1
1
  import { NotionHtmlASTToDeltaExtension, } from '../../../affine-shared/adapters/index.js';
2
+ import { isSafeLinkUrl } from '../../../affine-shared/utils/index.js';
2
3
  const isElement = (ast) => {
3
4
  return ast.type === 'element';
4
5
  };
@@ -29,7 +30,7 @@ export const notionHtmlLinkElementToDeltaMatcher = NotionHtmlASTToDeltaExtension
29
30
  return delta;
30
31
  }
31
32
  }
32
- if (href.startsWith('http')) {
33
+ if (/^https?:/i.test(href.trim()) && isSafeLinkUrl(href)) {
33
34
  delta.attributes = {
34
35
  ...delta.attributes,
35
36
  link: href,
@@ -14,6 +14,13 @@ export declare class AffineLink extends AffineLink_base {
14
14
  setFloating: (element?: Element) => void;
15
15
  dispose: () => void;
16
16
  };
17
+ /**
18
+ * Opens this link's toolbar without a hover, as a proposal (see
19
+ * `proposeLinkViews`). The toolbar keeps the hover contract once the pointer
20
+ * enters it; until then any key or press outside it closes it, so the
21
+ * proposal never outlives the next gesture.
22
+ */
23
+ proposeViews(): void;
17
24
  connectedCallback(): void;
18
25
  get block(): BlockComponent<import("../../store/index.js").BlockModel<object>, import("../../std/index.js").BlockService, string> | null;
19
26
  get inlineEditor(): import("../../std/inline/inline-editor.js").InlineEditor<AffineTextAttributes> | undefined;
@@ -65,6 +65,43 @@ let AffineLink = (() => {
65
65
  text-decoration: underline;
66
66
  }
67
67
  `; }
68
+ /**
69
+ * Opens this link's toolbar without a hover, as a proposal (see
70
+ * `proposeLinkViews`). The toolbar keeps the hover contract once the pointer
71
+ * enters it; until then any key or press outside it closes it, so the
72
+ * proposal never outlives the next gesture.
73
+ */
74
+ proposeViews() {
75
+ const message$ = this.std.get(ToolbarRegistryIdentifier).message$;
76
+ let floating;
77
+ const controller = new AbortController();
78
+ const close = (event) => {
79
+ if (floating && event.composedPath().includes(floating))
80
+ return;
81
+ controller.abort();
82
+ if (message$.peek()?.element === this)
83
+ message$.value = null;
84
+ };
85
+ const options = { capture: true, signal: controller.signal };
86
+ document.addEventListener('keydown', close, options);
87
+ document.addEventListener('pointerdown', close, options);
88
+ this._disposables.add(() => controller.abort());
89
+ message$.value = {
90
+ flavour: 'affine:link',
91
+ element: this,
92
+ setFloating: element => {
93
+ floating = element;
94
+ this._whenHover.setFloating(element);
95
+ },
96
+ };
97
+ // Subscribed after the write: `subscribe` calls back at once with the
98
+ // current value, which must already be this proposal.
99
+ const unsubscribe = message$.subscribe(message => {
100
+ if (message?.element !== this)
101
+ controller.abort();
102
+ });
103
+ controller.signal.addEventListener('abort', unsubscribe);
104
+ }
68
105
  connectedCallback() {
69
106
  super.connectedCallback();
70
107
  this._whenHover.setReference(this);
@@ -1,2 +1,3 @@
1
1
  export { AffineLink } from './affine-link.js';
2
2
  export { toggleLinkPopup } from './link-popup/toggle-link-popup.js';
3
+ export { lonePastedUrl, proposeLinkViews } from './propose-views.js';
@@ -1,2 +1,3 @@
1
1
  export { AffineLink } from './affine-link.js';
2
2
  export { toggleLinkPopup } from './link-popup/toggle-link-popup.js';
3
+ export { lonePastedUrl, proposeLinkViews } from './propose-views.js';