@ai-composer/dom 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +5 -2
- package/dist/icons.d.ts +11 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +46 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/mount.d.ts +41 -0
- package/dist/mount.d.ts.map +1 -0
- package/dist/mount.js +168 -0
- package/dist/mount.js.map +1 -0
- package/dist/package.json +25 -0
- package/dist/parse.d.ts +13 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +133 -0
- package/dist/parse.js.map +1 -0
- package/dist/popup.d.ts +34 -0
- package/dist/popup.d.ts.map +1 -0
- package/dist/popup.js +121 -0
- package/dist/popup.js.map +1 -0
- package/dist/render.d.ts +17 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +84 -0
- package/dist/render.js.map +1 -0
- package/dist/selection.d.ts +13 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +110 -0
- package/dist/selection.js.map +1 -0
- package/dist/suggestions.d.ts +23 -0
- package/dist/suggestions.d.ts.map +1 -0
- package/dist/suggestions.js +113 -0
- package/dist/suggestions.js.map +1 -0
- package/dist/surface.d.ts +40 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +372 -0
- package/dist/surface.js.map +1 -0
- package/dist/template.d.ts +26 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +47 -0
- package/dist/template.js.map +1 -0
- package/package.json +21 -3
- package/project.json +56 -0
- package/src/icons.ts +51 -0
- package/src/index.ts +50 -0
- package/src/mount.ts +218 -0
- package/src/parse.ts +135 -0
- package/src/popup.ts +153 -0
- package/src/render.ts +88 -0
- package/src/selection.ts +141 -0
- package/src/suggestions.ts +146 -0
- package/src/surface.test.ts +266 -0
- package/src/surface.ts +433 -0
- package/src/template.ts +80 -0
- package/tsconfig.json +15 -0
- package/tsconfig.lib.json +15 -0
- package/tsconfig.spec.json +16 -0
- package/vitest.config.ts +16 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Deepak Chopkar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ai-composer/dom
|
|
2
|
+
|
|
3
|
+
Browser layer: contentEditable surface, selection mapping, clipboard, templates and suggestion list.
|
|
4
|
+
|
|
5
|
+
See [`docs/`](../../docs/README.md) for guides and API reference.
|
|
2
6
|
|
|
3
|
-
This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
|
package/dist/icons.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline SVG icons for the default mount UI (lucide-derived geometry).
|
|
3
|
+
* Built with createElementNS so no innerHTML parsing is involved.
|
|
4
|
+
*/
|
|
5
|
+
type IconPaths = Array<Array<[string, Record<string, string | number>]>>;
|
|
6
|
+
declare const ICON_PATHS: Record<'send' | 'undo' | 'redo' | 'close', IconPaths>;
|
|
7
|
+
export declare function createIcon(name: keyof typeof ICON_PATHS, size?: number): SVGSVGElement;
|
|
8
|
+
/** Loading state for the send button (animated in themes css). */
|
|
9
|
+
export declare function createSpinner(): HTMLSpanElement;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=icons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,KAAK,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAEzE,QAAA,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,SAAS,CAWrE,CAAC;AAIF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,OAAO,UAAU,EAAE,IAAI,SAAK,GAAG,aAAa,CAoBlF;AAED,kEAAkE;AAClE,wBAAgB,aAAa,IAAI,eAAe,CAK/C"}
|
package/dist/icons.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline SVG icons for the default mount UI (lucide-derived geometry).
|
|
3
|
+
* Built with createElementNS so no innerHTML parsing is involved.
|
|
4
|
+
*/
|
|
5
|
+
const ICON_PATHS = {
|
|
6
|
+
send: [
|
|
7
|
+
[['M12 19V5', { 'stroke-width': 2.4 }], ['m5 12 7-7 7 7', { 'stroke-width': 2.4 }]],
|
|
8
|
+
],
|
|
9
|
+
undo: [
|
|
10
|
+
[['M3 7v6h6', {}], ['M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13', {}]],
|
|
11
|
+
],
|
|
12
|
+
redo: [
|
|
13
|
+
[['M21 7v6h-6', {}], ['M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3L21 13', {}]],
|
|
14
|
+
],
|
|
15
|
+
close: [[['M18 6 6 18', {}], ['m6 6 12 12', {}]]],
|
|
16
|
+
};
|
|
17
|
+
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
18
|
+
export function createIcon(name, size = 16) {
|
|
19
|
+
const svg = document.createElementNS(SVG_NS, 'svg');
|
|
20
|
+
svg.setAttribute('width', String(size));
|
|
21
|
+
svg.setAttribute('height', String(size));
|
|
22
|
+
svg.setAttribute('viewBox', '0 0 24 24');
|
|
23
|
+
svg.setAttribute('fill', 'none');
|
|
24
|
+
svg.setAttribute('stroke', 'currentColor');
|
|
25
|
+
svg.setAttribute('stroke-width', '2');
|
|
26
|
+
svg.setAttribute('stroke-linecap', 'round');
|
|
27
|
+
svg.setAttribute('stroke-linejoin', 'round');
|
|
28
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
29
|
+
for (const [d, attributes] of ICON_PATHS[name].flat()) {
|
|
30
|
+
const path = document.createElementNS(SVG_NS, 'path');
|
|
31
|
+
path.setAttribute('d', d);
|
|
32
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
33
|
+
path.setAttribute(key, String(value));
|
|
34
|
+
}
|
|
35
|
+
svg.appendChild(path);
|
|
36
|
+
}
|
|
37
|
+
return svg;
|
|
38
|
+
}
|
|
39
|
+
/** Loading state for the send button (animated in themes css). */
|
|
40
|
+
export function createSpinner() {
|
|
41
|
+
const spinner = document.createElement('span');
|
|
42
|
+
spinner.className = 'aic-spinner';
|
|
43
|
+
spinner.setAttribute('aria-hidden', 'true');
|
|
44
|
+
return spinner;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=icons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.js","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,UAAU,GAA0D;IACxE,IAAI,EAAE;QACJ,CAAC,CAAC,UAAU,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;KACpF;IACD,IAAI,EAAE;QACJ,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,2CAA2C,EAAE,EAAE,CAAC,CAAC;KACtE;IACD,IAAI,EAAE;QACJ,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,2CAA2C,EAAE,EAAE,CAAC,CAAC;KACxE;IACD,KAAK,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,GAAG,4BAA4B,CAAC;AAE5C,MAAM,UAAU,UAAU,CAAC,IAA6B,EAAE,IAAI,GAAG,EAAE;IACjE,MAAM,GAAG,GAAG,QAAQ,CAAC,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpD,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACxC,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACzC,GAAG,CAAC,YAAY,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACzC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IAC3C,GAAG,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACtC,GAAG,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IAC5C,GAAG,CAAC,YAAY,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;IAC7C,GAAG,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACxC,KAAK,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtD,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACxC,CAAC;QACD,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/C,OAAO,CAAC,SAAS,GAAG,aAAa,CAAC;IAClC,OAAO,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ai-composer/dom — browser layer over @ai-composer/core.
|
|
3
|
+
*
|
|
4
|
+
* surface contentEditable binding (input pipeline, keyboard, clipboard, IME)
|
|
5
|
+
* render model → DOM projection (chips carry data-aic-* attributes)
|
|
6
|
+
* parse DOM → model with strict sanitization
|
|
7
|
+
* selection DOM selection ↔ model selection mapping
|
|
8
|
+
* suggestions accessible suggestion listbox driven by state
|
|
9
|
+
* template structural mode presets (compact/default/chat/expanded)
|
|
10
|
+
* mount vanilla JS mountAIComposer()
|
|
11
|
+
*/
|
|
12
|
+
export { createEditableSurface, EDITABLE_SURFACE_CLASS, type EditableSurface, type EditableSurfaceOptions, } from './surface';
|
|
13
|
+
export { renderChip, renderDocument, renderTextRun } from './render';
|
|
14
|
+
export { htmlToPlainText, parseEditableHost, CLIPBOARD_MIME } from './parse';
|
|
15
|
+
export { applyModelSelection, caretToEnd, domSelectionToModel } from './selection';
|
|
16
|
+
export { createSuggestionList, type SuggestionList, type SuggestionListOptions, } from './suggestions';
|
|
17
|
+
export { attachCaretAnchoredPopup, measureCaret, type CaretAnchor, type CaretAnchorOptions, type CaretPoint, } from './popup';
|
|
18
|
+
export { CHAT_TEMPLATE, COMPACT_TEMPLATE, DEFAULT_TEMPLATE, EXPANDED_TEMPLATE, registerTemplate, SLOT_ATTRIBUTE, templateForMode, type AIComposerDomTemplate, type TemplateSlotName, } from './template';
|
|
19
|
+
export { mountAIComposer, type MountedEditor, type MountOptions, } from './mount';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,eAAe,EACpB,KAAK,sBAAsB,GAC5B,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EACL,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,qBAAqB,GAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,wBAAwB,EACxB,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,UAAU,GAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ai-composer/dom — browser layer over @ai-composer/core.
|
|
3
|
+
*
|
|
4
|
+
* surface contentEditable binding (input pipeline, keyboard, clipboard, IME)
|
|
5
|
+
* render model → DOM projection (chips carry data-aic-* attributes)
|
|
6
|
+
* parse DOM → model with strict sanitization
|
|
7
|
+
* selection DOM selection ↔ model selection mapping
|
|
8
|
+
* suggestions accessible suggestion listbox driven by state
|
|
9
|
+
* template structural mode presets (compact/default/chat/expanded)
|
|
10
|
+
* mount vanilla JS mountAIComposer()
|
|
11
|
+
*/
|
|
12
|
+
export { createEditableSurface, EDITABLE_SURFACE_CLASS, } from './surface';
|
|
13
|
+
export { renderChip, renderDocument, renderTextRun } from './render';
|
|
14
|
+
export { htmlToPlainText, parseEditableHost, CLIPBOARD_MIME } from './parse';
|
|
15
|
+
export { applyModelSelection, caretToEnd, domSelectionToModel } from './selection';
|
|
16
|
+
export { createSuggestionList, } from './suggestions';
|
|
17
|
+
export { attachCaretAnchoredPopup, measureCaret, } from './popup';
|
|
18
|
+
export { CHAT_TEMPLATE, COMPACT_TEMPLATE, DEFAULT_TEMPLATE, EXPANDED_TEMPLATE, registerTemplate, SLOT_ATTRIBUTE, templateForMode, } from './template';
|
|
19
|
+
export { mountAIComposer, } from './mount';
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GAGvB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EACL,oBAAoB,GAGrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,wBAAwB,EACxB,YAAY,GAIb,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,eAAe,GAGhB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,GAGhB,MAAM,SAAS,CAAC"}
|
package/dist/mount.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vanilla JS mount — proves the stack works with zero framework. Builds the
|
|
3
|
+
* DOM once (every slot exists), then applies the mode's structural preset and
|
|
4
|
+
* re-applies it live when the editor's mode changes — the draft, focus and
|
|
5
|
+
* undo history survive a compact ↔ chat ↔ expanded switch.
|
|
6
|
+
*/
|
|
7
|
+
import type { AIComposer, SuggestionItem } from '@ai-composer/core';
|
|
8
|
+
import { type EditableSurface } from './surface';
|
|
9
|
+
import { type AIComposerDomTemplate } from './template';
|
|
10
|
+
export interface MountOptions {
|
|
11
|
+
/** Initial mode preset; can be changed later via `editor.setMode()`. */
|
|
12
|
+
mode?: string;
|
|
13
|
+
/** Replace the structural preset (static — not mode-switched). */
|
|
14
|
+
template?: AIComposerDomTemplate;
|
|
15
|
+
/**
|
|
16
|
+
* Force slots on regardless of the mode preset (projection-friendly):
|
|
17
|
+
* attachments/header/toolbar/footer stay mountable in compact too.
|
|
18
|
+
*/
|
|
19
|
+
showSlots?: Array<'header' | 'attachments' | 'toolbar' | 'footer'>;
|
|
20
|
+
/**
|
|
21
|
+
* Auto-height ceiling — any CSS length ("160px", "40vh"). The box grows
|
|
22
|
+
* with content up to this height, then scrolls inside.
|
|
23
|
+
* Equivalent to the `--aic-input-max-height` token.
|
|
24
|
+
*/
|
|
25
|
+
maxHeight?: string;
|
|
26
|
+
/** Custom suggestion item renderer. */
|
|
27
|
+
renderSuggestionItem?: (item: SuggestionItem, active: boolean) => HTMLElement;
|
|
28
|
+
labels?: {
|
|
29
|
+
removeAttachment?: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export interface MountedEditor {
|
|
33
|
+
root: HTMLElement;
|
|
34
|
+
input: HTMLElement;
|
|
35
|
+
suggestions: HTMLElement;
|
|
36
|
+
surface: EditableSurface;
|
|
37
|
+
slots: Record<string, HTMLElement>;
|
|
38
|
+
destroy(): void;
|
|
39
|
+
}
|
|
40
|
+
export declare function mountAIComposer(container: HTMLElement, editor: AIComposer, options?: MountOptions): MountedEditor;
|
|
41
|
+
//# sourceMappingURL=mount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAe,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AAGxE,OAAO,EAAmC,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEzF,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,aAAa,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC;IACnE;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,KAAK,WAAW,CAAC;IAC9E,MAAM,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACnC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,WAAW,EACtB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,YAAiB,GACzB,aAAa,CA0Kf"}
|
package/dist/mount.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vanilla JS mount — proves the stack works with zero framework. Builds the
|
|
3
|
+
* DOM once (every slot exists), then applies the mode's structural preset and
|
|
4
|
+
* re-applies it live when the editor's mode changes — the draft, focus and
|
|
5
|
+
* undo history survive a compact ↔ chat ↔ expanded switch.
|
|
6
|
+
*/
|
|
7
|
+
import { createEditableSurface } from './surface';
|
|
8
|
+
import { createSuggestionList } from './suggestions';
|
|
9
|
+
import { createIcon, createSpinner } from './icons';
|
|
10
|
+
import { templateForMode, SLOT_ATTRIBUTE } from './template';
|
|
11
|
+
export function mountAIComposer(container, editor, options = {}) {
|
|
12
|
+
const labels = {
|
|
13
|
+
removeAttachment: options.labels?.removeAttachment ?? 'Remove',
|
|
14
|
+
};
|
|
15
|
+
const slots = {};
|
|
16
|
+
const disposables = [];
|
|
17
|
+
const makeSlot = (name, className) => {
|
|
18
|
+
const element = document.createElement('div');
|
|
19
|
+
element.className = className;
|
|
20
|
+
element.setAttribute(SLOT_ATTRIBUTE, name);
|
|
21
|
+
slots[name] = element;
|
|
22
|
+
return element;
|
|
23
|
+
};
|
|
24
|
+
const root = container;
|
|
25
|
+
root.classList.add('aic-root');
|
|
26
|
+
if (options.maxHeight) {
|
|
27
|
+
root.style.setProperty('--aic-input-max-height', options.maxHeight);
|
|
28
|
+
}
|
|
29
|
+
const header = makeSlot('header', 'aic-header');
|
|
30
|
+
const body = makeSlot('body', 'aic-body');
|
|
31
|
+
const toolbar = makeSlot('toolbar', 'aic-toolbar');
|
|
32
|
+
const footer = makeSlot('footer', 'aic-footer');
|
|
33
|
+
const attachments = makeSlot('attachments', 'aic-attachments');
|
|
34
|
+
const input = makeSlot('input', 'aic-input-host');
|
|
35
|
+
root.append(header, body, toolbar, footer);
|
|
36
|
+
// The input wrapper is the popup anchor: it occupies exactly the input's
|
|
37
|
+
// space — the suggestion list floats out of it (no DOM-space reservation).
|
|
38
|
+
const inputWrap = document.createElement('div');
|
|
39
|
+
inputWrap.className = 'aic-input-wrap';
|
|
40
|
+
inputWrap.setAttribute(SLOT_ATTRIBUTE, 'suggestions');
|
|
41
|
+
inputWrap.appendChild(input);
|
|
42
|
+
slots.suggestions = inputWrap;
|
|
43
|
+
body.append(attachments, inputWrap);
|
|
44
|
+
const surface = createEditableSurface(editor, input, { id: input.id || undefined });
|
|
45
|
+
const suggestionList = createSuggestionList(editor, undefined, {
|
|
46
|
+
renderItem: options.renderSuggestionItem,
|
|
47
|
+
inputHost: input,
|
|
48
|
+
});
|
|
49
|
+
inputWrap.appendChild(suggestionList.element);
|
|
50
|
+
// -- mode presets ----------------------------------------------------------
|
|
51
|
+
const applyTemplate = (mode) => {
|
|
52
|
+
const template = options.template ?? templateForMode(mode);
|
|
53
|
+
const present = new Set(template.slots);
|
|
54
|
+
for (const slotName of options.showSlots ?? [])
|
|
55
|
+
present.add(slotName);
|
|
56
|
+
root.setAttribute('data-aic-mode', template.name);
|
|
57
|
+
header.hidden = !present.has('header');
|
|
58
|
+
toolbar.hidden = !present.has('toolbar');
|
|
59
|
+
footer.hidden = !present.has('footer');
|
|
60
|
+
surface.setMultiline(template.multiline);
|
|
61
|
+
surface.setSubmitKey(template.submitKey);
|
|
62
|
+
if (toolbar) {
|
|
63
|
+
toolbar.textContent = '';
|
|
64
|
+
for (const action of template.toolbarButtons ?? []) {
|
|
65
|
+
const button = document.createElement('button');
|
|
66
|
+
button.type = 'button';
|
|
67
|
+
button.className = `aic-toolbar-${action}`;
|
|
68
|
+
button.setAttribute('data-aic-action', action);
|
|
69
|
+
if (action === 'submit') {
|
|
70
|
+
button.appendChild(createIcon('send'));
|
|
71
|
+
button.setAttribute('aria-label', 'Send');
|
|
72
|
+
button.addEventListener('click', () => void editor.executeCommand('submit'));
|
|
73
|
+
}
|
|
74
|
+
else if (action === 'undo') {
|
|
75
|
+
button.appendChild(createIcon('undo'));
|
|
76
|
+
button.setAttribute('aria-label', 'Undo');
|
|
77
|
+
button.addEventListener('click', () => void editor.executeCommand('undo'));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
button.appendChild(createIcon('redo'));
|
|
81
|
+
button.setAttribute('aria-label', 'Redo');
|
|
82
|
+
button.addEventListener('click', () => void editor.executeCommand('redo'));
|
|
83
|
+
}
|
|
84
|
+
toolbar.appendChild(button);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
// -- state-driven UI -------------------------------------------------------
|
|
89
|
+
let currentMode = '';
|
|
90
|
+
const syncUi = (state) => {
|
|
91
|
+
if (state.mode !== currentMode) {
|
|
92
|
+
currentMode = state.mode;
|
|
93
|
+
applyTemplate(state.mode);
|
|
94
|
+
}
|
|
95
|
+
const submit = toolbar.querySelector('[data-aic-action="submit"]');
|
|
96
|
+
if (submit) {
|
|
97
|
+
const canSubmit = !state.disabled && !state.readonly && !state.submitting &&
|
|
98
|
+
(!state.empty || state.attachments.length > 0);
|
|
99
|
+
submit.disabled = !canSubmit;
|
|
100
|
+
submit.setAttribute('aria-disabled', String(!canSubmit));
|
|
101
|
+
if (state.submitting) {
|
|
102
|
+
if (!submit.querySelector('.aic-spinner')) {
|
|
103
|
+
submit.textContent = '';
|
|
104
|
+
submit.appendChild(createSpinner());
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (!submit.querySelector('svg')) {
|
|
108
|
+
submit.textContent = '';
|
|
109
|
+
submit.appendChild(createIcon('send'));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const undo = toolbar.querySelector('[data-aic-action="undo"]');
|
|
113
|
+
if (undo)
|
|
114
|
+
undo.disabled = !state.canUndo;
|
|
115
|
+
const redo = toolbar.querySelector('[data-aic-action="redo"]');
|
|
116
|
+
if (redo)
|
|
117
|
+
redo.disabled = !state.canRedo;
|
|
118
|
+
// Attachments are projection-friendly: visible whenever they exist,
|
|
119
|
+
// regardless of the mode preset (apps add them via editor.addAttachment).
|
|
120
|
+
attachments.hidden = state.attachments.length === 0;
|
|
121
|
+
attachments.textContent = '';
|
|
122
|
+
for (const attachment of state.attachments) {
|
|
123
|
+
const item = document.createElement('span');
|
|
124
|
+
item.className = 'aic-attachment';
|
|
125
|
+
const name = document.createElement('span');
|
|
126
|
+
name.className = 'aic-attachment-name';
|
|
127
|
+
name.textContent = attachment.name;
|
|
128
|
+
const remove = document.createElement('button');
|
|
129
|
+
remove.type = 'button';
|
|
130
|
+
remove.className = 'aic-attachment-remove';
|
|
131
|
+
remove.setAttribute('aria-label', `${labels.removeAttachment} ${attachment.name}`);
|
|
132
|
+
remove.appendChild(createIcon('close', 12));
|
|
133
|
+
remove.addEventListener('click', () => editor.removeNode(attachment.key));
|
|
134
|
+
item.append(name, remove);
|
|
135
|
+
attachments.appendChild(item);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const unsubscribeState = editor.subscribe(syncUi);
|
|
139
|
+
disposables.push(unsubscribeState);
|
|
140
|
+
if (options.mode && options.mode !== editor.getState().mode) {
|
|
141
|
+
editor.setMode(options.mode);
|
|
142
|
+
}
|
|
143
|
+
// setMode emits to subscribers; ensure the initial template is applied even
|
|
144
|
+
// when the editor was already in the target mode.
|
|
145
|
+
currentMode = editor.getState().mode;
|
|
146
|
+
applyTemplate(currentMode);
|
|
147
|
+
syncUi(editor.getState());
|
|
148
|
+
const offDestroy = editor.on('destroy', () => mounted.destroy());
|
|
149
|
+
disposables.push(offDestroy);
|
|
150
|
+
const mounted = {
|
|
151
|
+
root,
|
|
152
|
+
input,
|
|
153
|
+
suggestions: suggestionList.element,
|
|
154
|
+
surface,
|
|
155
|
+
slots,
|
|
156
|
+
destroy() {
|
|
157
|
+
for (const dispose of disposables.splice(0))
|
|
158
|
+
dispose();
|
|
159
|
+
suggestionList.destroy();
|
|
160
|
+
surface.destroy();
|
|
161
|
+
root.textContent = '';
|
|
162
|
+
root.classList.remove('aic-root');
|
|
163
|
+
root.removeAttribute('data-aic-mode');
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
return mounted;
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=mount.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount.js","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ai-composer/dom",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@ai-composer/core": "^0.1.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@ai-composer/core": "workspace:*",
|
|
21
|
+
"vitest": "^2.1.8",
|
|
22
|
+
"jsdom": "^25.0.1"
|
|
23
|
+
},
|
|
24
|
+
"module": "./index.js"
|
|
25
|
+
}
|
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM → model parsing with strict sanitization: only elements we generate
|
|
3
|
+
* (data-aic-node chips) survive as structured nodes — everything else is
|
|
4
|
+
* flattened to plain text (see the serialization ADR).
|
|
5
|
+
*/
|
|
6
|
+
import { createDocument, type NodeRegistry } from '@ai-composer/core';
|
|
7
|
+
/** Parse the editable host back into a document. Never trusts markup: unknown nodes collapse to text. */
|
|
8
|
+
export declare function parseEditableHost(host: HTMLElement, registry: NodeRegistry): ReturnType<typeof createDocument>;
|
|
9
|
+
/** Extract safe plain text from arbitrary HTML (paste path). Tags are dropped, <br> becomes \n. */
|
|
10
|
+
export declare function htmlToPlainText(html: string): string;
|
|
11
|
+
/** Clipboard mime used for lossless internal copy/paste round-trips. */
|
|
12
|
+
export declare const CLIPBOARD_MIME = "application/x-ai-composer";
|
|
13
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,cAAc,EAEd,KAAK,YAAY,EAGlB,MAAM,mBAAmB,CAAC;AAS3B,yGAAyG;AACzG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAsC9G;AAiDD,mGAAmG;AACnG,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqBpD;AAED,wEAAwE;AACxE,eAAO,MAAM,cAAc,8BAAqB,CAAC"}
|
package/dist/parse.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM → model parsing with strict sanitization: only elements we generate
|
|
3
|
+
* (data-aic-node chips) survive as structured nodes — everything else is
|
|
4
|
+
* flattened to plain text (see the serialization ADR).
|
|
5
|
+
*/
|
|
6
|
+
import { createDocument, createNodeKey, } from '@ai-composer/core';
|
|
7
|
+
const INTERNAL_CLIP_MIME = 'application/x-ai-composer';
|
|
8
|
+
function textNode(text, previous) {
|
|
9
|
+
if (previous)
|
|
10
|
+
return { ...previous, text: previous.text + text };
|
|
11
|
+
return { type: 'text', key: createNodeKey('text'), text };
|
|
12
|
+
}
|
|
13
|
+
/** Parse the editable host back into a document. Never trusts markup: unknown nodes collapse to text. */
|
|
14
|
+
export function parseEditableHost(host, registry) {
|
|
15
|
+
const nodes = [];
|
|
16
|
+
const appendText = (text) => {
|
|
17
|
+
if (!text)
|
|
18
|
+
return;
|
|
19
|
+
const last = nodes[nodes.length - 1];
|
|
20
|
+
if (last && last.type === 'text')
|
|
21
|
+
nodes[nodes.length - 1] = textNode(text, last);
|
|
22
|
+
else
|
|
23
|
+
nodes.push(textNode(text));
|
|
24
|
+
};
|
|
25
|
+
const walk = (parent) => {
|
|
26
|
+
for (const child of Array.from(parent.childNodes)) {
|
|
27
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
28
|
+
appendText(child.textContent ?? '');
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (child.nodeName === 'BR') {
|
|
32
|
+
appendText('\n');
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (child instanceof HTMLElement) {
|
|
36
|
+
// Never surface executable/style content as text.
|
|
37
|
+
if (child.tagName === 'SCRIPT' || child.tagName === 'STYLE')
|
|
38
|
+
continue;
|
|
39
|
+
const type = child.getAttribute('data-aic-node');
|
|
40
|
+
const parsed = type ? chipFromElement(child, type, registry) : null;
|
|
41
|
+
if (parsed) {
|
|
42
|
+
nodes.push(parsed);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Unknown wrapper (browser artifact, pasted markup) — flatten to text only.
|
|
46
|
+
walk(child);
|
|
47
|
+
}
|
|
48
|
+
// Comments and other node types are dropped.
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
walk(host);
|
|
52
|
+
return createDocument(nodes);
|
|
53
|
+
}
|
|
54
|
+
function chipFromElement(element, type, registry) {
|
|
55
|
+
if (!registry.has(type))
|
|
56
|
+
return null;
|
|
57
|
+
const key = element.getAttribute('data-aic-key') ?? createNodeKey(type);
|
|
58
|
+
const attr = (name) => element.getAttribute(`data-aic-${name}`);
|
|
59
|
+
switch (type) {
|
|
60
|
+
case 'mention':
|
|
61
|
+
case 'command': {
|
|
62
|
+
const id = attr('id');
|
|
63
|
+
const label = attr('label');
|
|
64
|
+
if (!id || !label)
|
|
65
|
+
return null;
|
|
66
|
+
return type === 'mention'
|
|
67
|
+
? { type: 'mention', key, id, label }
|
|
68
|
+
: { type: 'command', key, id, label };
|
|
69
|
+
}
|
|
70
|
+
case 'variable': {
|
|
71
|
+
const name = attr('name');
|
|
72
|
+
if (!name)
|
|
73
|
+
return null;
|
|
74
|
+
return { type: 'variable', key, name };
|
|
75
|
+
}
|
|
76
|
+
case 'attachment': {
|
|
77
|
+
const id = attr('id');
|
|
78
|
+
const name = attr('name');
|
|
79
|
+
const mimeType = attr('mime') ?? 'application/octet-stream';
|
|
80
|
+
if (!id || !name)
|
|
81
|
+
return null;
|
|
82
|
+
const sizeAttr = attr('size');
|
|
83
|
+
const size = sizeAttr ? Number(sizeAttr) : undefined;
|
|
84
|
+
return { type: 'attachment', key, id, name, mimeType, ...(Number.isFinite(size) ? { size } : {}) };
|
|
85
|
+
}
|
|
86
|
+
case 'custom': {
|
|
87
|
+
const plugin = attr('plugin');
|
|
88
|
+
const nodeType = attr('type');
|
|
89
|
+
if (!plugin || !nodeType)
|
|
90
|
+
return null;
|
|
91
|
+
let data = null;
|
|
92
|
+
try {
|
|
93
|
+
data = JSON.parse(attr('data') ?? 'null');
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
data = null;
|
|
97
|
+
}
|
|
98
|
+
return { type: 'custom', key, plugin, nodeType, data };
|
|
99
|
+
}
|
|
100
|
+
default:
|
|
101
|
+
// Registered custom node types without a known chip shape degrade to text.
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Extract safe plain text from arbitrary HTML (paste path). Tags are dropped, <br> becomes \n. */
|
|
106
|
+
export function htmlToPlainText(html) {
|
|
107
|
+
const container = document.createElement('div');
|
|
108
|
+
container.innerHTML = html; // parsed, never inserted into the page
|
|
109
|
+
let text = '';
|
|
110
|
+
const walk = (parent) => {
|
|
111
|
+
for (const child of Array.from(parent.childNodes)) {
|
|
112
|
+
if (child.nodeType === Node.TEXT_NODE)
|
|
113
|
+
text += child.textContent ?? '';
|
|
114
|
+
else if (child.nodeName === 'BR')
|
|
115
|
+
text += '\n';
|
|
116
|
+
else if (child.nodeType === Node.ELEMENT_NODE) {
|
|
117
|
+
const element = child;
|
|
118
|
+
if (element.tagName === 'DIV' || element.tagName === 'P') {
|
|
119
|
+
walk(element);
|
|
120
|
+
text += '\n';
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
walk(element);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
walk(container);
|
|
129
|
+
return text;
|
|
130
|
+
}
|
|
131
|
+
/** Clipboard mime used for lossless internal copy/paste round-trips. */
|
|
132
|
+
export const CLIPBOARD_MIME = INTERNAL_CLIP_MIME;
|
|
133
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
|
package/dist/popup.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caret-anchored popup positioning. The suggestion menu opens where the
|
|
3
|
+
* trigger key (@, /, …) was hit — not just above the box — and reflows:
|
|
4
|
+
*
|
|
5
|
+
* - horizontal: popup's left edge follows the caret, clamped into the
|
|
6
|
+
* viewport so it never opens out of the window
|
|
7
|
+
* - vertical: prefers `placement` (Copilot-style: above), flips to the
|
|
8
|
+
* other side when the viewport lacks room, caps its max-height to the
|
|
9
|
+
* available space and scrolls inside
|
|
10
|
+
* - repositions on window resize and on any scroll (capture) while open
|
|
11
|
+
*
|
|
12
|
+
* The popup must live inside a positioned ancestor (`.aic-input-wrap`) —
|
|
13
|
+
* coordinates are computed relative to that offset parent each update.
|
|
14
|
+
*/
|
|
15
|
+
export interface CaretPoint {
|
|
16
|
+
/** Viewport x of the caret. */
|
|
17
|
+
x: number;
|
|
18
|
+
/** Viewport y of the caret line's top / bottom. */
|
|
19
|
+
top: number;
|
|
20
|
+
bottom: number;
|
|
21
|
+
}
|
|
22
|
+
export interface CaretAnchor {
|
|
23
|
+
/** Recompute position (call after content/visibility changes). */
|
|
24
|
+
update(): void;
|
|
25
|
+
destroy(): void;
|
|
26
|
+
}
|
|
27
|
+
export interface CaretAnchorOptions {
|
|
28
|
+
/** Preferred side; flips automatically when the viewport lacks room. */
|
|
29
|
+
placement?: 'above' | 'below';
|
|
30
|
+
}
|
|
31
|
+
/** Measure the caret inside a contenteditable host (viewport coordinates). */
|
|
32
|
+
export declare function measureCaret(host: HTMLElement): CaretPoint | null;
|
|
33
|
+
export declare function attachCaretAnchoredPopup(popup: HTMLElement, host: HTMLElement, options?: CaretAnchorOptions): CaretAnchor;
|
|
34
|
+
//# sourceMappingURL=popup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popup.d.ts","sourceRoot":"","sources":["../src/popup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB,+BAA+B;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC/B;AAKD,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,IAAI,CA6BjE;AAWD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,WAAW,EACjB,OAAO,GAAE,kBAAuB,GAC/B,WAAW,CAsEb"}
|