@clepit/core 0.5.0 → 0.6.0-beta.397

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 (35) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/core/allowed.d.ts +26 -0
  3. package/dist/core/editor/index.d.ts +11 -1
  4. package/dist/core/editor/issues.d.ts +19 -0
  5. package/dist/core/element/index.d.ts +13 -1
  6. package/dist/core/labels.d.ts +3 -0
  7. package/dist/core/managers/block/css.d.ts +1 -1
  8. package/dist/core/managers/block/index.d.ts +4 -0
  9. package/dist/core/managers/event.d.ts +5 -0
  10. package/dist/core/store.d.ts +1 -0
  11. package/dist/core/toolbars/block/index.d.ts +1 -0
  12. package/dist/index.d.ts +7 -2
  13. package/dist/index.js +1 -1
  14. package/dist/index.mjs +1 -1
  15. package/dist/tools/block/embed/css.d.ts +3 -3
  16. package/dist/tools/block/embed/input.d.ts +10 -1
  17. package/dist/tools/block/embed/issue-card.d.ts +17 -0
  18. package/dist/tools/block/embed/offer.d.ts +13 -0
  19. package/dist/tools/block/embed/output.d.ts +16 -2
  20. package/dist/tools/block/embed/paste.d.ts +16 -0
  21. package/dist/tools/block/embed/providers.d.ts +7 -2
  22. package/dist/tools/block/issues/css.d.ts +7 -0
  23. package/dist/tools/block/issues/index.d.ts +13 -0
  24. package/dist/tools/block/issues/input.d.ts +14 -0
  25. package/dist/tools/block/issues/output.d.ts +24 -0
  26. package/dist/tools/block/openapi/input.d.ts +1 -1
  27. package/dist/tools/block/typeset/input.d.ts +2 -2
  28. package/dist/tools/inline/issue/css.d.ts +10 -0
  29. package/dist/tools/inline/issue/index.d.ts +58 -0
  30. package/dist/tools/inline/issue/labels.d.ts +11 -0
  31. package/dist/tools/inline/issue/menu.d.ts +14 -0
  32. package/dist/tools/inline/issue/paste.d.ts +22 -0
  33. package/dist/tools/inline/issue/providers.d.ts +9 -0
  34. package/dist/types.d.ts +134 -5
  35. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0] - 2026-09-25
11
+
12
+ Notes derived from the commits that touched shipped `src/` since 0.5.0. `@clepit/react` 0.3.0 rides this release: `ClepitEditor` forwards `resolveLink`, `resolveIssues`, `onEmbedAction` and `onIssueAction` through the same latest-ref contract as the uploaders, and requires `@clepit/core` ^0.6.0.
13
+
14
+ ### Added
15
+ - `EditorConfig.allowedBlockTools` and `allowedInlineTools`: an embedder names the block types the block menu, its search, the convert submenu and the URL paste path offer, and the marks the inline toolbar serves. Absent, everything registered is offered, and a stored block outside the list still renders.
16
+ - `EditorConfig.inlineRewriteTooltip`: the localized tooltip for the inline toolbar's rewrite wand. Without it the wand keeps saying `rewrite`, which was the only string the toolbar rendered untranslated.
17
+ - `INLINE_REWRITE_ACTIONS` and `InlineRewriteAction` are exported from the package root, so a host can label every rewrite action without reaching into `types`.
18
+ - Embeds: a Figma, Miro, Loom, Google Drive or Docs link, or a GitHub gist, pasted on an empty line becomes an embed block at once and goes on the next line when the line has text, honouring the member's preference for a live frame, a card or an offer chip under the caret; Enter on the offer converts it and any other key dismisses it. The block's own menu offers Show live, Show as card, Show as link and Copy link. `EditorConfig` carries the provider policy, the translated words, the host's link lookup (`resolveLink`) and the action seam (`onEmbedAction`). `resolveEmbed`, `isEmbedProviderDisabled`, `embedLabel`, `EMBED_LABELS`, `EMBED_TOOL_NAMES`, `EMBED_WIRE_KEYS`, `EMBED_ENGLISH` and the `Embed*` types are exported from the package root.
19
+ - Issues: an issue link pasted into a sentence is a chip carrying the issue's key, title, status and holder, or a card on an empty line for a member who asked; a Linked issues block is derived from the document like the table of contents; chips, cards and tables are refreshed on mount, on focus and every five minutes without marking the document changed. `EditorConfig.resolveIssues` and `onIssueAction` are the host's seams. `classifyIssueUrl`, `IssueChip`, `chipsInHtml`, `readChipMarkup`, `refreshChipsInHtml`, `issueLabel`, `issueToolName`, `ISSUE_LABELS`, `ISSUE_TOOLS`, `ISSUE_TOOL_NAMES`, `ISSUE_ENGLISH` and the `Issue*` types are exported from the package root.
20
+
21
+ ### Fixed
22
+ - `Editor.destroy` removes the document from the store after the DOM and the listeners are gone, so a destroyed editor no longer keeps its document under its container id, and `Editor.create` releases a previous runtime in the same container without deleting the document the store rehydrated before the page loaded.
23
+ - Issue cards on a public page are no longer redrawn as empty chips by the finished-markup chip pass.
24
+ - The table block's CSV download prefixes an apostrophe to a cell or header that opens with `=`, `+`, `-`, `@`, a tab or a carriage return, so a spreadsheet reads it as text, and a carriage return inside a cell is quoted.
25
+
10
26
  ## [0.5.0] - 2026-08-31
11
27
 
12
28
  Note: 0.4.0 was released without a changelog entry, so the notes below cover
@@ -0,0 +1,26 @@
1
+ import { pasteEmbedTarget } from '../tools/block/embed/paste';
2
+ import { pasteIssueTarget } from '../tools/inline/issue/paste';
3
+ import { EditorConfig, Tool } from '../types';
4
+ type BlockAllowance = Pick<EditorConfig, 'allowedBlockTools'> | undefined;
5
+ type InlineAllowance = Pick<EditorConfig, 'allowedInlineTools'> | undefined;
6
+ export declare const isBlockToolAllowed: (config: BlockAllowance, type: string) => boolean;
7
+ export declare const allowedBlockTools: (config: BlockAllowance, tools: Tool[]) => Tool[];
8
+ export declare const isInlineToolAllowed: (config: InlineAllowance, label: string) => boolean;
9
+ export type PasteTarget = {
10
+ kind: 'embed';
11
+ embed: NonNullable<ReturnType<typeof pasteEmbedTarget>>;
12
+ } | {
13
+ kind: 'image';
14
+ url: string;
15
+ } | {
16
+ kind: 'issue';
17
+ issue: NonNullable<ReturnType<typeof pasteIssueTarget>>;
18
+ } | {
19
+ kind: 'video';
20
+ url: string;
21
+ isVimeo: boolean;
22
+ isYouTube: boolean;
23
+ };
24
+ export declare const pasteTarget: (config: Pick<EditorConfig, "allowedBlockTools" | "disabledEmbedProviders" | "jiraSites"> | undefined, pastedText: string) => PasteTarget | null;
25
+ export {};
26
+ //# sourceMappingURL=allowed.d.ts.map
@@ -13,10 +13,20 @@ export declare class Editor {
13
13
  */
14
14
  private static localizeData;
15
15
  private static render;
16
- /** Runs even after the container has left the DOM (a React host removes the
16
+ /** Releases the runtime a container holds and leaves its document in the
17
+ * store. `create` runs this first so a second create in the same container
18
+ * starts clean, and the document it must NOT touch is the one the store
19
+ * rehydrated before this page load: `addInstance` keeps a stored document
20
+ * in preference to `initialData`, which is what lets a reader's content
21
+ * survive a reload. Removing the store entry here deleted that document a
22
+ * moment before `addInstance` would have kept it, and only the landing's
23
+ * persistence spec caught it.
24
+ *
25
+ * Runs even after the container has left the DOM (a React host removes the
17
26
  * element before effect cleanup fires): every helper guards its own DOM
18
27
  * access, and the id-keyed state — theme registry, body theme attribute,
19
28
  * pending toolbar timers — must be released either way. */
29
+ private static teardown;
20
30
  static destroy(containerId: string): void;
21
31
  }
22
32
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,19 @@
1
+ /** Five minutes: an issue's status is a slow thing, and a page left open all
2
+ * day should not cost a tracker a read a minute. */
3
+ export declare const REFRESH_EVERY_MS: number;
4
+ /**
5
+ * Keeps a page's issues current while it is open: one lookup on mount, again
6
+ * when the window regains focus (the reader came back from the tracker), and
7
+ * every five minutes. Chips, cards and tables are redrawn in place; the
8
+ * document is not marked changed, because nothing the author did changed.
9
+ */
10
+ export declare class IssueRefresh {
11
+ private static timers;
12
+ private static focusHandlers;
13
+ static start(containerId: string): void;
14
+ static stop(containerId: string): void;
15
+ /** The URLs a refresh asks about: every chip and every issue card in the editor. */
16
+ static urls(containerId: string): string[];
17
+ static run(containerId: string): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=issues.d.ts.map
@@ -1,4 +1,4 @@
1
- import { Block } from '../../types';
1
+ import { Block, EmbedLabel, IssueChipParts, IssueLabel, IssueSnapshot } from '../../types';
2
2
  /**
3
3
  * The construction surface a block's render path is allowed to use.
4
4
  *
@@ -54,6 +54,18 @@ export type DrawContext = {
54
54
  mapsEmbedKey?: string;
55
55
  /** How a doc card turns a page id into a link, which only the host knows. */
56
56
  docHref?: (pageId: string) => string;
57
+ /** The workspace's switched-off embed tools, by wire key: a card where the
58
+ * workspace said so, on a public page as much as in the editor. */
59
+ disabledEmbedProviders?: readonly string[];
60
+ /** Translated embed words; English fallbacks otherwise. */
61
+ embedLabels?: Partial<Record<EmbedLabel, string>>;
62
+ /** The freshest issue snapshots the host resolved, by URL: a chip, a card or
63
+ * a Linked issues row draws these over what the document stored. */
64
+ issues?: Record<string, IssueSnapshot>;
65
+ /** The reader's two chip switches; both on when absent. */
66
+ issueChip?: IssueChipParts;
67
+ /** Translated issue words; English fallbacks otherwise. */
68
+ issueLabels?: Partial<Record<IssueLabel, string>>;
57
69
  };
58
70
  export type Backing = {
59
71
  create(tag: string): ClepitElement;
@@ -0,0 +1,3 @@
1
+ import { BlockToolbarLabel } from '../types';
2
+ export declare const resolveHint: (containerId: string, key: BlockToolbarLabel, fallback: string) => string;
3
+ //# sourceMappingURL=labels.d.ts.map
@@ -1,3 +1,3 @@
1
1
  export declare const stylesKey = "clepit-block-manager-styles";
2
- export declare const style = "\n/* Blocks */\n.clepit-block {\n position: relative;\n margin: var(--block-margin-top, 8px) 0 var(--block-margin-bottom, 16px) 0;\n padding: 8px 0;\n padding-left: 54px;\n border-radius: 4px;\n transition: background-color 0.2s ease;\n display: flex;\n align-items: flex-start;\n gap: 6px;\n}\n\n.clepit-block:hover {\n background-color: var(--clepit-block-highlight-bg);\n}\n\n.clepit-block--active {\n background-color: var(--clepit-block-highlight-bg);\n}\n\n.clepit-block-content {\n outline: none;\n min-height: 1.2em;\n flex: 1;\n min-width: 0; /* Allow content to shrink */\n}\n\n/* Remove focus outline from editable elements */\n.clepit-block-content [contenteditable=\"true\"] {\n outline: none;\n}\n\n/* Placeholder styles for all text elements */\n.clepit-block-content:empty::before {\n content: attr(data-placeholder);\n color: var(--clepit-text-subtle);\n pointer-events: none;\n font-style: italic;\n}\n\n/* Menu animation classes */\n.clepit-block-type-selector--visible {\n display: block;\n transform: translateY(0);\n}\n\n.clepit-block-type-selector--hidden {\n display: none;\n}\n\n/* Block Operations Menu */\n.clepit-block-operations-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1001;\n background: var(--clepit-bg);\n border: 1px solid var(--clepit-border);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n padding: 8px;\n min-width: 200px;\n margin-top: 4px;\n display: none;\n transition: transform 0.2s ease;\n}\n\n.clepit-block-operations-menu--visible {\n display: block;\n transform: translateY(0);\n}\n\n.clepit-block-operations-menu--hidden {\n display: none;\n}\n\n/* Block Type Selector */\n.clepit-block-type-selector {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1001;\n background: var(--clepit-bg);\n border: 1px solid var(--clepit-border);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n padding: 8px;\n min-width: 150px;\n margin-top: 4px;\n display: none;\n transition: transform 0.2s ease;\n}\n\n.clepit-block-type-selector--visible {\n display: block;\n transform: translateY(0);\n}\n\n.clepit-block-type-selector--hidden {\n display: none;\n}\n\n/* Default menu item styles - package doesn't care about theme */\n.clepit-menu-item {\n padding: 8px 12px;\n cursor: pointer;\n border-radius: 4px;\n transition: background-color 0.2s;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.clepit-menu-item .clepit-menu-icon {\n width: 16px;\n height: 16px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n}\n\n.clepit-menu-item:hover {\n background-color: var(--clepit-selection-bg);\n}\n\n.clepit-menu-item.active {\n background-color: var(--clepit-primary-bg);\n color: var(--clepit-primary);\n font-weight: bold;\n}\n\n.clepit-menu-item--danger {\n color: var(--clepit-error);\n}\n\n/* Block type menu items - match font size with operations menu */\n.clepit-block-type-item {\n font-size: 14px;\n}\n\n/* Default menu divider styles */\n.clepit-menu-divider {\n height: 1px;\n background: linear-gradient(to right, transparent, var(--clepit-border), transparent);\n margin: 8px 0;\n}\n\n/* Default menu arrow styles */\n.clepit-menu-arrow {\n margin-left: auto;\n font-size: 12px;\n color: var(--clepit-text-muted);\n}\n\n/* Input styles for block type selector */\n.clepit-block-type-selector input {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid var(--clepit-input-border, var(--clepit-border));\n border-radius: 4px;\n font-size: 14px;\n margin-bottom: 8px;\n outline: none;\n transition: border-color 0.2s;\n background-color: var(--input-bg, var(--clepit-bg));\n color: var(--input-color, var(--clepit-text));\n}\n\n.clepit-block-type-selector input:focus {\n border-color: var(--clepit-primary);\n box-shadow: 0 0 0 2px var(--clepit-primary-bg);\n}\n\n.clepit-block-type-selector input::placeholder {\n color: var(--placeholder-color, var(--clepit-text-subtle));\n}\n\n.clepit-block-type-selector-items {\n max-height: 200px;\n overflow-y: auto;\n}\n\n.clepit-menu-icon {\n font-size: 14px;\n min-width: 20px;\n text-align: center;\n font-weight: bold;\n}\n\n.clepit-menu-shortcut {\n margin-left: auto;\n font-size: 9px;\n color: var(--clepit-text-muted);\n opacity: 0.7;\n}\n\n.clepit-menu-positioned {\n position: absolute;\n z-index: 1001;\n top: var(--menu-top, 0);\n left: var(--menu-left, 0);\n}\n\n/* The block menus are popovers in the top layer. Anchor positioning puts them\n * under the block toolbar without JavaScript measuring anything.\n *\n * Guarded: anchor positioning is Baseline 2026 (Chrome 125+, Firefox 132+,\n * Safari 18.2+), roughly 91% of traffic. Browsers without it get the popover's\n * default centred placement rather than a second JS positioning path \u2014 the menu\n * stays usable, just not adjacent to its button. */\n.clepit-block-type-selector:popover-open,\n.clepit-block-operations-menu:popover-open {\n /* The base menu rules above still say display:none from the pre-popover\n * mechanism, and an author rule outranks the UA's popover reveal. Without\n * an explicit display here every menu opens into the top layer invisible. */\n display: block;\n margin: 0;\n border: 1px solid var(--clepit-border);\n border-radius: 6px;\n background: var(--clepit-bg);\n padding: 8px;\n overflow: visible;\n}\n\n.clepit-convert-submenu:popover-open {\n display: block;\n}\n\n@supports (anchor-name: --a) {\n .clepit-block-type-selector:popover-open,\n .clepit-block-operations-menu:popover-open {\n position-anchor: --clepit-block-toolbar;\n position-area: bottom span-inline-end;\n margin-block-start: 5px;\n }\n}\n\n@supports (position-try-fallbacks: flip-block) {\n .clepit-block-type-selector:popover-open,\n .clepit-block-operations-menu:popover-open {\n position-try-fallbacks: flip-block;\n }\n}\n\n/* Convert Submenu */\n@supports (anchor-name: --a) {\n .clepit-convert-submenu:popover-open {\n position: fixed;\n position-area: inline-end span-block-end;\n margin: 0 0 0 4px;\n inset: auto;\n }\n\n .clepit-convert-submenu:popover-open {\n position-try-fallbacks: flip-inline;\n }\n}\n\n.clepit-convert-submenu {\n position: absolute;\n top: var(--submenu-top, 0);\n left: var(--submenu-left, 100%);\n z-index: var(--submenu-z-index, 1002);\n background: var(--clepit-bg);\n border: 1px solid var(--clepit-border);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n padding: 8px;\n min-width: 140px;\n margin-left: 4px;\n display: none;\n transition: transform 0.2s ease;\n}\n\n.clepit-convert-submenu--visible {\n display: block;\n opacity: 1;\n transform: translateX(0);\n}\n\n.clepit-convert-submenu--hidden {\n display: none;\n opacity: 0;\n transform: translateX(-10px);\n}\n\n/* Input styles for convert submenu */\n.clepit-convert-submenu input {\n width: 100%;\n padding: 6px 8px;\n border: 1px solid var(--clepit-input-border, var(--clepit-border));\n border-radius: 4px;\n font-size: 12px;\n outline: none;\n background-color: var(--input-bg, var(--clepit-bg));\n color: var(--input-color, var(--clepit-text));\n}\n\n.clepit-convert-submenu input:focus {\n border-color: var(--clepit-input-focus-border, var(--clepit-primary));\n box-shadow: 0 0 0 2px var(--clepit-input-focus-shadow, var(--clepit-primary-bg));\n}\n\n.clepit-convert-submenu input::placeholder {\n color: var(--clepit-input-placeholder, var(--clepit-text-subtle));\n}\n\n.clepit-convert-submenu-items {\n max-height: 200px;\n overflow-y: auto;\n}\n\n.clepit-block-operation-item {\n padding: 8px 12px;\n cursor: pointer;\n border-radius: 4px;\n transition: background-color 0.2s;\n font-size: 14px;\n}\n\n.clepit-block-operation-item:hover {\n background-color: var(--clepit-hover-bg, var(--clepit-selection-bg));\n}\n\n/* Input styles for operations menu */\n.clepit-block-operations-menu input {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid var(--clepit-input-border, var(--clepit-border));\n border-radius: 4px;\n font-size: 14px;\n margin-bottom: 8px;\n outline: none;\n transition: border-color 0.2s;\n background-color: var(--input-bg, var(--clepit-bg));\n color: var(--input-color, var(--clepit-text));\n}\n\n.clepit-block-operations-menu input:focus {\n border-color: var(--clepit-primary);\n box-shadow: 0 0 0 2px var(--clepit-primary-bg);\n}\n\n.clepit-block-operations-menu input::placeholder {\n color: var(--placeholder-color, var(--clepit-text-subtle));\n}\n\n.clepit-block-operations-menu-items {\n max-height: 200px;\n overflow-y: auto;\n}\n\n.clepit-block-operation-item[data-operation=\"delete\"] {\n color: var(--clepit-error);\n}\n\n/* Convert submenu items */\n.clepit-convert-option-item {\n padding: 8px 12px;\n cursor: pointer;\n border-radius: 4px;\n transition: background-color 0.2s;\n font-size: 14px;\n}\n\n.clepit-convert-option-item:hover {\n background-color: var(--clepit-hover-bg, var(--clepit-selection-bg));\n}\n\n/* Indentation */\n.clepit-block[data-indent=\"1\"] .clepit-block-content {\n margin-left: 20px;\n}\n\n.clepit-block[data-indent=\"2\"] .clepit-block-content {\n margin-left: 40px;\n}\n\n.clepit-block[data-indent=\"3\"] .clepit-block-content {\n margin-left: 60px;\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n /* The 54px gutter exists to hold the block toolbar, which is only ever\n * displayed on the active block. On a phone that reserved a seventh of the\n * editor's width on every idle block at once, pushing the text into a narrow\n * column with empty space either side. Give the width back and let the\n * gutter open only under the block whose toolbar is actually showing. */\n /* No transition on padding-left. The block element is re-created as you\n * interact with it, so an animated gutter restarts from 0 every time and\n * never reaches its end value \u2014 the toolbar would sit on top of the text. */\n .clepit-block {\n padding-left: 0;\n }\n\n .clepit-block--active,\n .clepit-block:focus-within,\n .clepit-block:has(.clepit-block-toolbar--visible) {\n padding-left: 54px;\n }\n}\n";
2
+ export declare const style = "\n/* Blocks */\n.clepit-block {\n position: relative;\n margin: var(--block-margin-top, 8px) 0 var(--block-margin-bottom, 16px) 0;\n padding: 8px 0;\n padding-left: 54px;\n border-radius: 4px;\n transition: background-color 0.2s ease;\n display: flex;\n align-items: flex-start;\n gap: 6px;\n}\n\n.clepit-block:hover {\n background-color: var(--clepit-block-highlight-bg);\n}\n\n.clepit-block--active {\n background-color: var(--clepit-block-highlight-bg);\n}\n\n.clepit-block-content {\n outline: none;\n min-height: 1.2em;\n flex: 1;\n min-width: 0; /* Allow content to shrink */\n}\n\n/* Remove focus outline from editable elements */\n.clepit-block-content [contenteditable=\"true\"] {\n outline: none;\n}\n\n/* Placeholder styles for all text elements */\n.clepit-block-content:empty::before {\n content: attr(data-placeholder);\n color: var(--clepit-text-subtle);\n pointer-events: none;\n font-style: italic;\n}\n\n/* Menu animation classes */\n.clepit-block-type-selector--visible {\n display: block;\n transform: translateY(0);\n}\n\n.clepit-block-type-selector--hidden {\n display: none;\n}\n\n/* Block Operations Menu */\n.clepit-block-operations-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1001;\n background: var(--clepit-bg);\n border: 1px solid var(--clepit-border);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n padding: 8px;\n min-width: 200px;\n margin-top: 4px;\n display: none;\n transition: transform 0.2s ease;\n}\n\n.clepit-block-operations-menu--visible {\n display: block;\n transform: translateY(0);\n}\n\n.clepit-block-operations-menu--hidden {\n display: none;\n}\n\n/* Block Type Selector */\n.clepit-block-type-selector {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1001;\n background: var(--clepit-bg);\n border: 1px solid var(--clepit-border);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n padding: 8px;\n min-width: 150px;\n margin-top: 4px;\n display: none;\n transition: transform 0.2s ease;\n}\n\n.clepit-block-type-selector--visible {\n display: block;\n transform: translateY(0);\n}\n\n.clepit-block-type-selector--hidden {\n display: none;\n}\n\n/* Default menu item styles - package doesn't care about theme */\n.clepit-menu-item {\n padding: 8px 12px;\n cursor: pointer;\n border-radius: 4px;\n transition: background-color 0.2s;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.clepit-menu-item .clepit-menu-icon {\n width: 16px;\n height: 16px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n}\n\n.clepit-menu-item:hover {\n background-color: var(--clepit-selection-bg);\n}\n\n.clepit-menu-item.active {\n background-color: var(--clepit-primary-bg);\n color: var(--clepit-primary);\n font-weight: bold;\n}\n\n.clepit-menu-item--danger {\n color: var(--clepit-error);\n}\n\n/* Block type menu items - match font size with operations menu */\n.clepit-block-type-item {\n font-size: 14px;\n}\n\n/* Default menu divider styles */\n.clepit-menu-divider {\n height: 1px;\n background: linear-gradient(to right, transparent, var(--clepit-border), transparent);\n margin: 8px 0;\n}\n\n/* Default menu arrow styles */\n.clepit-menu-arrow {\n margin-left: auto;\n font-size: 12px;\n color: var(--clepit-text-muted);\n}\n\n/* Input styles for block type selector */\n.clepit-block-type-selector input {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid var(--clepit-input-border, var(--clepit-border));\n border-radius: 4px;\n font-size: 14px;\n margin-bottom: 8px;\n outline: none;\n transition: border-color 0.2s;\n background-color: var(--input-bg, var(--clepit-bg));\n color: var(--input-color, var(--clepit-text));\n}\n\n.clepit-block-type-selector input:focus {\n border-color: var(--clepit-primary);\n box-shadow: 0 0 0 2px var(--clepit-primary-bg);\n}\n\n.clepit-block-type-selector input::placeholder {\n color: var(--placeholder-color, var(--clepit-text-subtle));\n}\n\n.clepit-block-type-selector-items {\n max-height: 200px;\n overflow-y: auto;\n}\n\n.clepit-menu-icon {\n font-size: 14px;\n min-width: 20px;\n text-align: center;\n font-weight: bold;\n}\n\n.clepit-menu-shortcut {\n margin-left: auto;\n font-size: 9px;\n color: var(--clepit-text-muted);\n opacity: 0.7;\n}\n\n.clepit-menu-positioned {\n position: absolute;\n z-index: 1001;\n top: var(--menu-top, 0);\n left: var(--menu-left, 0);\n}\n\n/* The block menus are popovers in the top layer. Anchor positioning puts them\n * under the block toolbar without JavaScript measuring anything.\n *\n * Guarded: anchor positioning is Baseline 2026 (Chrome 125+, Firefox 132+,\n * Safari 18.2+), roughly 91% of traffic. Browsers without it get the popover's\n * default centred placement rather than a second JS positioning path \u2014 the menu\n * stays usable, just not adjacent to its button. */\n.clepit-block-type-selector:popover-open,\n.clepit-block-operations-menu:popover-open {\n /* The base menu rules above still say display:none from the pre-popover\n * mechanism, and an author rule outranks the UA's popover reveal. Without\n * an explicit display here every menu opens into the top layer invisible. */\n display: block;\n margin: 0;\n border: 1px solid var(--clepit-border);\n border-radius: 6px;\n background: var(--clepit-bg);\n padding: 8px;\n overflow: visible;\n}\n\n.clepit-convert-submenu:popover-open {\n display: block;\n}\n\n@supports (anchor-name: --a) {\n .clepit-block-type-selector:popover-open,\n .clepit-block-operations-menu:popover-open {\n position-anchor: --clepit-block-toolbar;\n position-area: block-end span-inline-end;\n margin-block-start: 5px;\n inset: auto;\n }\n}\n\n@supports (position-try-fallbacks: flip-block) {\n .clepit-block-type-selector:popover-open,\n .clepit-block-operations-menu:popover-open {\n position-try-fallbacks: flip-block;\n }\n}\n\n/* Convert Submenu */\n@supports (anchor-name: --a) {\n .clepit-convert-submenu:popover-open {\n position: fixed;\n position-area: inline-end span-block-end;\n margin: 0 0 0 4px;\n inset: auto;\n }\n\n .clepit-convert-submenu:popover-open {\n position-try-fallbacks: flip-inline;\n }\n}\n\n.clepit-convert-submenu {\n position: absolute;\n top: var(--submenu-top, 0);\n left: var(--submenu-left, 100%);\n z-index: var(--submenu-z-index, 1002);\n background: var(--clepit-bg);\n border: 1px solid var(--clepit-border);\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n padding: 8px;\n min-width: 140px;\n margin-left: 4px;\n display: none;\n transition: transform 0.2s ease;\n}\n\n.clepit-convert-submenu--visible {\n display: block;\n opacity: 1;\n transform: translateX(0);\n}\n\n.clepit-convert-submenu--hidden {\n display: none;\n opacity: 0;\n transform: translateX(-10px);\n}\n\n/* Input styles for convert submenu */\n.clepit-convert-submenu input {\n width: 100%;\n padding: 6px 8px;\n border: 1px solid var(--clepit-input-border, var(--clepit-border));\n border-radius: 4px;\n font-size: 12px;\n outline: none;\n background-color: var(--input-bg, var(--clepit-bg));\n color: var(--input-color, var(--clepit-text));\n}\n\n.clepit-convert-submenu input:focus {\n border-color: var(--clepit-input-focus-border, var(--clepit-primary));\n box-shadow: 0 0 0 2px var(--clepit-input-focus-shadow, var(--clepit-primary-bg));\n}\n\n.clepit-convert-submenu input::placeholder {\n color: var(--clepit-input-placeholder, var(--clepit-text-subtle));\n}\n\n.clepit-convert-submenu-items {\n max-height: 200px;\n overflow-y: auto;\n}\n\n.clepit-block-operation-item {\n padding: 8px 12px;\n cursor: pointer;\n border-radius: 4px;\n transition: background-color 0.2s;\n font-size: 14px;\n}\n\n.clepit-block-operation-item:hover {\n background-color: var(--clepit-hover-bg, var(--clepit-selection-bg));\n}\n\n/* Input styles for operations menu */\n.clepit-block-operations-menu input {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid var(--clepit-input-border, var(--clepit-border));\n border-radius: 4px;\n font-size: 14px;\n margin-bottom: 8px;\n outline: none;\n transition: border-color 0.2s;\n background-color: var(--input-bg, var(--clepit-bg));\n color: var(--input-color, var(--clepit-text));\n}\n\n.clepit-block-operations-menu input:focus {\n border-color: var(--clepit-primary);\n box-shadow: 0 0 0 2px var(--clepit-primary-bg);\n}\n\n.clepit-block-operations-menu input::placeholder {\n color: var(--placeholder-color, var(--clepit-text-subtle));\n}\n\n.clepit-block-operations-menu-items {\n max-height: 200px;\n overflow-y: auto;\n}\n\n.clepit-block-operation-item[data-operation=\"delete\"] {\n color: var(--clepit-error);\n}\n\n/* Convert submenu items */\n.clepit-convert-option-item {\n padding: 8px 12px;\n cursor: pointer;\n border-radius: 4px;\n transition: background-color 0.2s;\n font-size: 14px;\n}\n\n.clepit-convert-option-item:hover {\n background-color: var(--clepit-hover-bg, var(--clepit-selection-bg));\n}\n\n/* Indentation */\n.clepit-block[data-indent=\"1\"] .clepit-block-content {\n margin-left: 20px;\n}\n\n.clepit-block[data-indent=\"2\"] .clepit-block-content {\n margin-left: 40px;\n}\n\n.clepit-block[data-indent=\"3\"] .clepit-block-content {\n margin-left: 60px;\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n /* The 54px gutter exists to hold the block toolbar, which is only ever\n * displayed on the active block. On a phone that reserved a seventh of the\n * editor's width on every idle block at once, pushing the text into a narrow\n * column with empty space either side. Give the width back and let the\n * gutter open only under the block whose toolbar is actually showing. */\n /* No transition on padding-left. The block element is re-created as you\n * interact with it, so an animated gutter restarts from 0 every time and\n * never reaches its end value \u2014 the toolbar would sit on top of the text. */\n .clepit-block {\n padding-left: 0;\n }\n\n .clepit-block--active,\n .clepit-block:focus-within,\n .clepit-block:has(.clepit-block-toolbar--visible) {\n padding-left: 54px;\n }\n}\n";
3
3
  //# sourceMappingURL=css.d.ts.map
@@ -8,6 +8,9 @@ export declare class BlockManager {
8
8
  private static createBlockElement;
9
9
  static insertBlock(block: Block, containerId: string, index: number): HTMLElement | null;
10
10
  static removeBlock(containerId: string, index: number): null | undefined;
11
+ /** Insert first, then remove: `removeBlock` keeps the last block of a document,
12
+ * so replacing the only block by removing it first left the old block in
13
+ * place beside the new one. */
11
14
  static replaceBlock(containerId: string, index: number, block: Block<BlockToolType>): void;
12
15
  static moveBlock(containerId: string, fromIndex: number, toIndex: number): void;
13
16
  static getBlock(blockId: string): HTMLElement | null;
@@ -27,6 +30,7 @@ export declare class BlockManager {
27
30
  static renderBlock(block: HTMLElement, containerId: string, index: number): void;
28
31
  static setBlocks(blocks: Block[], containerId: string): void;
29
32
  static renderBlocks(blocks: Block[], containerId: string): void;
33
+ private static adoptStartingBlock;
30
34
  private static removeAllBlocks;
31
35
  static resetBlocks(containerId: string): void;
32
36
  static getEditorId(element: HTMLElement): string | null;
@@ -8,6 +8,11 @@ export declare class EventManager {
8
8
  } | null;
9
9
  private static handleClick;
10
10
  private static handleSelectionChange;
11
+ /**
12
+ * A paste is intercepted only when it is one this editor turns into a block:
13
+ * an image URL, a video URL, or a link a provider renders. Plain text and
14
+ * links no provider knows reach the browser's own paste untouched.
15
+ */
11
16
  static handlePaste(event: ClipboardEvent): void;
12
17
  }
13
18
  //# sourceMappingURL=event.d.ts.map
@@ -6,6 +6,7 @@ type Store = {
6
6
  addInstance: (containerId: string, instance: EditorInstance) => void;
7
7
  updateInstance: (containerId: string, instance: EditorInstance) => void;
8
8
  removeInstance: (containerId: string) => void;
9
+ seedBlocks: (containerId: string, blocks: Block[]) => void;
9
10
  getInstance: (containerId: string) => EditorInstance | null;
10
11
  setCurrentView: (containerId: string, currentView: EditorView) => void;
11
12
  getCurrentView: (containerId: string) => EditorView | null;
@@ -15,6 +15,7 @@ export declare class BlockToolbar {
15
15
  private static showCodeThemeSubmenu;
16
16
  static show(containerId: string, block: HTMLElement): void;
17
17
  static hide(containerId: string): void;
18
+ private static menuHost;
18
19
  private static hideAllToolbars;
19
20
  private static getCurrentHeadingLevel;
20
21
  /**
package/dist/index.d.ts CHANGED
@@ -6,9 +6,14 @@ export { activateBlocks } from './core/renderer/activate';
6
6
  export { drawBlock } from './core/renderer/draw';
7
7
  export { type PublicPage, pageStylesheet, renderBlocks, renderPage } from './core/renderer/page';
8
8
  export { type ThemeTokens, tokensToLightDarkBlock } from './core/theme';
9
+ export { EMBED_ENGLISH, EMBED_TOOL_NAMES, embedLabel } from './tools/block/embed/output';
10
+ export { EMBED_WIRE_KEYS, isEmbedProviderDisabled, resolveEmbed } from './tools/block/embed/providers';
9
11
  export type { OpenApiGroup, OpenApiModel, OpenApiOperation, OpenApiServer } from './tools/block/openapi/spec';
10
12
  export { parseOpenApi } from './tools/block/openapi/spec';
11
- export type { ALERT_VARIANTS, AlertData, AlertVariant, AudioData, Block, BlockClassNames, BlockMargins, BlockStyles, BlockTune, CodeData, CodeVariant, CSSProperties, CSSValue, DelimiterData, DelimiterVariant, EditorAPI, EditorClassNames, EditorConfig, EditorData, EditorStyles, GlanceData, GlanceRow, HeaderData, HeaderLevel, ImageData, InlineToolType as InlineTool, InputClassNames, InputConfig, InputStyles, ListData, ListType, OpenApiData, OpenApiFilter, OutputComponent, OutputConfig, ParagraphData, QuoteData, RendererConfig, TableData, ToolType as BlockType, UploadFunction, VideoData, } from './types';
12
- export { BLOCK_TOOLS, INLINE_TOOLS } from './types';
13
+ export { chipsInHtml, IssueChip, readChipMarkup, refreshChipsInHtml } from './tools/inline/issue';
14
+ export { ISSUE_ENGLISH, ISSUE_TOOL_NAMES, issueLabel, issueToolName } from './tools/inline/issue/labels';
15
+ export { classifyIssueUrl } from './tools/inline/issue/providers';
16
+ export type { ALERT_VARIANTS, AlertData, AlertVariant, AudioData, Block, BlockClassNames, BlockMargins, BlockStyles, BlockTune, CodeData, CodeVariant, ControlLabel, CSSProperties, CSSValue, DelimiterData, DelimiterVariant, EditorAPI, EditorClassNames, EditorConfig, EditorData, EditorStyles, EmbedAction, EmbedData, EmbedFile, EmbedGist, EmbedGistFile, EmbedLabel, EmbedProvider, EmbedReason, EmbedResolution, GlanceData, GlanceRow, HeaderData, HeaderLevel, ImageData, InlineRewriteAction, InlineToolType as InlineTool, InputClassNames, InputConfig, InputStyles, IssueAction, IssueCards, IssueCategory, IssueChipParts, IssueLabel, IssueReason, IssueSnapshot, IssuesData, IssueTarget, IssueTool, ListData, ListType, OpenApiData, OpenApiFilter, OutputComponent, OutputConfig, ParagraphData, PasteEmbeds, QuoteData, RendererConfig, TableData, ToolType as BlockType, UploadFunction, VideoData, } from './types';
17
+ export { BLOCK_TOOLS, CONTROL_LABELS, EMBED_LABELS, INLINE_REWRITE_ACTIONS, INLINE_TOOLS, ISSUE_LABELS, ISSUE_TOOLS } from './types';
13
18
  export { sanitizeHtmlWithoutDom } from './utils/sanitize-html-nodom';
14
19
  //# sourceMappingURL=index.d.ts.map