@dskripchenko/embed 0.0.0-stage → 1.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # @dskripchenko/embed
2
+
3
+ ## 1.0.0 — 2026-10-26
4
+
5
+ First release.
6
+
7
+ - **Loader** `dist/embed.js` (≈ 9 KB gzipped): a classic script that writes
8
+ the import map of the shared runtime — Vue 3.5.44, `@dskripchenko/ui`
9
+ 1.16.0, `wysiwyg` 0.5.1, `diagrams` 1.1.0, `openapi` 1.1.1, `flow` 1.0.2,
10
+ pinned — composes with a page's own import map, defines the elements and
11
+ `window.DskEmbed`, and fetches each plugin when its first element appears.
12
+ - **Fat bundle** `dist/embed.bundle.js` (≈ 563 KB gzipped): the same elements
13
+ and API with everything inside, no import map.
14
+ - **Elements**: `<dsk-video>`, `<dsk-audio>`, `<dsk-diagram>`,
15
+ `<dsk-openapi>` (reference, operation, block), `<dsk-flow>` (view with
16
+ `poll-url` / `toggle-url` / `subgraph-url` conventions, or the editor),
17
+ `<dsk-markdown>`, `<dsk-editor>` (form-ready: `name`, `input`, `input` /
18
+ `change` events). Light DOM, attributes ↔ properties, `loading="lazy"`.
19
+ - **Blocks**: a page-wide registry (`DskEmbed.blocks.register`) with the
20
+ live-block contract of the kit and the editor; built-in `mermaid`,
21
+ `openapi`, `flow`, `video`, `audio`; `DskEmbed.renderBlocks(root)` makes
22
+ stored HTML live, idempotently.
23
+ - **Configuration**: theme (`light`, `dark`, `system`, or the page's
24
+ `data-theme`) and locale (`en`, `ru`, or the page's `lang`) for every
25
+ element from one place; one Vue app context for every mount.
26
+ - **Events**: bubbling `dsk:*` DOM events and `DskEmbed.on/once/off/emit`;
27
+ `openapi-target` and `media-target` wire a process to an API reference and
28
+ a video declaratively.
29
+ - The kit's locale is provided app-wide (`provideAppLocale`); the editor's
30
+ toolbar follows it. Video chapters come from the kit (`chapters`,
31
+ `seekToChapter`), with `playChapter()` and `dsk:chapter` /
32
+ `dsk:chapterchange` on top. Flow toggles persist through `persistToggle`
33
+ and report `{ ok }` in `dsk:toggle`. `<dsk-openapi proxy>` sends try-it
34
+ through the host (`proxySend`); a fixed `height` also sizes the
35
+ reference's sticky navigation (`--oa-viewport-h`).
36
+ - The icons the packages import from `lucide-vue-next` (76, now only
37
+ openapi, flow and the editor's toolbar) are served as one file; pages
38
+ with players or markdown alone no longer fetch them.
39
+ - `scripts/mirror.mjs` for self-hosting in the CDN's layout;
40
+ `dist/importmap.json` for pages that write their own map.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Denis Skripchenko
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,128 @@
1
- # Temporary Holding Version
1
+ # @dskripchenko/embed
2
2
 
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.
3
+ [![npm](https://img.shields.io/npm/v/@dskripchenko/embed.svg)](https://www.npmjs.com/package/@dskripchenko/embed)
4
+ [![license](https://img.shields.io/npm/l/@dskripchenko/embed.svg)](LICENSE)
5
+
6
+ Video and audio players, Mermaid diagrams, OpenAPI references, process flows,
7
+ live markdown and a WYSIWYG editor — as HTML tags on **any** page: plain HTML,
8
+ a CMS, WordPress, Blade, React. One script from a CDN. The elements share one
9
+ Vue and one UI kit, render each other's blocks, and talk through DOM events.
10
+
11
+ [**English**](docs/en/getting-started.md) · [Русский](docs/ru/getting-started.md) · [Deutsch](docs/de/getting-started.md) · [中文](docs/zh/getting-started.md)
12
+
13
+ ```html
14
+ <head>
15
+ <script src="https://cdn.jsdelivr.net/npm/@dskripchenko/embed@1/dist/embed.js"></script>
16
+ </head>
17
+ <body>
18
+ <dsk-flow src="/process.json" poll-url="/process/state" openapi-target="#api" media-target="#tour"></dsk-flow>
19
+ <dsk-video id="tour" src="/tour.mp4">
20
+ <track kind="chapters" src="/tour.chapters.vtt" default>
21
+ </dsk-video>
22
+ <dsk-openapi id="api" src="/openapi.yaml" height="720"></dsk-openapi>
23
+
24
+ <dsk-markdown>
25
+ <script type="text/markdown">
26
+ ```mermaid
27
+ sequenceDiagram
28
+ Shop->>Payments: charge
29
+ ```
30
+ </script>
31
+ </dsk-markdown>
32
+
33
+ <form method="post"><dsk-editor name="body"></dsk-editor></form>
34
+ </body>
35
+ ```
36
+
37
+ Clicking a process step opens its API operation and plays its chapter of the
38
+ video; the markdown's diagram is live; the editor submits HTML with the form.
39
+
40
+ ## Elements
41
+
42
+ | | | Built on |
43
+ |---|---|---|
44
+ | `<dsk-video>` | player: sources and quality menu, subtitles, chapters, thumbnails, HLS | [`@dskripchenko/ui`](https://github.com/dskripchenko/ui) |
45
+ | `<dsk-audio>` | player, playlist, waveform | `@dskripchenko/ui` |
46
+ | `<dsk-diagram>` | Mermaid → SVG, 11 diagram types, no Vue loaded | [`@dskripchenko/diagrams`](https://github.com/dskripchenko/diagrams) |
47
+ | `<dsk-openapi>` | full reference (several specs), one operation, or a live API block | [`@dskripchenko/openapi`](https://github.com/dskripchenko/openapi) |
48
+ | `<dsk-flow>` | process view with monitoring, toggles and sub-processes over plain HTTP — or the editor | [`@dskripchenko/flow`](https://github.com/dskripchenko/flow) |
49
+ | `<dsk-markdown>` | markdown with live `mermaid` / `openapi` / `flow` / `video` / `audio` blocks | `@dskripchenko/ui` |
50
+ | `<dsk-editor>` | WYSIWYG with the same live blocks; a form field | [`@dskripchenko/wysiwyg`](https://github.com/dskripchenko/wysiwyg) |
51
+
52
+ All of them: attributes ↔ properties, `dsk:*` events, `ready`, `loading="lazy"`,
53
+ light DOM (your CSS applies). → [Elements](docs/en/elements.md)
54
+
55
+ ## Working together
56
+
57
+ - **One runtime.** The loader writes an import map pinning Vue, the kit and
58
+ the plugin packages to the exact versions of the release; every element and
59
+ live block renders in one Vue app context — one locale, one theme, one set
60
+ of block renderers.
61
+ - **Blocks.** `DskEmbed.blocks.register('chart', renderer)` — the live-block
62
+ contract of the kit and the editor — makes ```` ```chart ```` live in
63
+ markdown, in the editor and in `DskEmbed.renderBlocks(root)`, which turns
64
+ stored HTML (`<pre><code class="language-…">`) into widgets. → [Blocks](docs/en/blocks.md)
65
+ - **Configuration.** `DskEmbed.configure({ theme: 'light' | 'dark' | 'system', locale: 'en' | 'ru' })`,
66
+ or just the page's `<html data-theme lang>`. → [Configuration and events](docs/en/config-and-events.md)
67
+ - **Events.** `DskEmbed.on('node', …)`, or `addEventListener('dsk:node', …)` on
68
+ any ancestor; React and other hosts use refs and plain listeners. →
69
+ [Frameworks](docs/en/frameworks.md)
70
+
71
+ ## Loader or bundle
72
+
73
+ | | gzipped | |
74
+ |---|---|---|
75
+ | `dist/embed.js` — the loader | **9.1 KB** | Classic script; elements, API, import map. Everything else on demand. |
76
+ | `dist/embed.bundle.js` — the fat bundle | **563 KB** (2.0 MB) | Everything inside; no import map, no further requests. |
77
+
78
+ What the loader fetches on demand (gzip -9 of the published files; jsDelivr
79
+ serves Brotli, a little smaller):
80
+
81
+ | First element of… | Fetches | |
82
+ |---|---|---|
83
+ | any Vue element | Vue runtime 41.5 KB, `@dskripchenko/ui` 102.6 KB (its icons inside) + CSS 30.1 KB | once per page |
84
+ | `<dsk-video>` / `<dsk-audio>` | plugin 3.5 KB; the HLS adapter 14.6 KB only for `.m3u8` | |
85
+ | `<dsk-diagram>` | plugin 0.8 KB, `@dskripchenko/diagrams` 99.7 KB, kit tokens 2.4 KB | no Vue, no kit components |
86
+ | `<dsk-openapi>` | plugin 1.4 KB, `@dskripchenko/openapi` 180.5 KB + CSS 10.5 KB, icons 5.2 KB | |
87
+ | `<dsk-flow>` | plugin 2.5 KB, `@dskripchenko/flow` 79.9 KB + CSS 4.6 KB, icons 5.2 KB; the Mermaid import (2.4 KB + diagrams) only for Mermaid sources and the editor | |
88
+ | `<dsk-markdown>` | plugin 0.5 KB | blocks load their own plugins |
89
+ | `<dsk-editor>` | plugin 1.1 KB, `@dskripchenko/wysiwyg` 18.2 KB + CSS 2.3 KB, icons 5.2 KB | |
90
+
91
+ The icons file (76 icons, one file) is shared: fetched once, by the first of
92
+ openapi, flow and the editor.
93
+
94
+ → [Getting started](docs/en/getting-started.md): snippets, self-hosting,
95
+ composing with your own import map, CSP, versioning.
96
+
97
+ ## Browsers
98
+
99
+ Current Chromium, Safari and Firefox. The loader needs import maps; there is
100
+ no legacy build. Tested in Chromium and WebKit (Playwright): both builds,
101
+ both themes, both languages, desktop and phone.
102
+
103
+ ## Known limitations
104
+
105
+ - **The theme is page-wide** (`<html data-theme>`): the kit's themes are
106
+ defined on `:root`, and its menus and dialogs live in `<body>`.
107
+
108
+ ## Development
109
+
110
+ ```bash
111
+ pnpm install
112
+ pnpm build # dist/ and the size table
113
+ pnpm test # unit tests (vitest, jsdom)
114
+ pnpm lint && pnpm typecheck
115
+ node scripts/fixtures.mjs # media fixtures for the browser check (ffmpeg)
116
+ pnpm check:browser # Chromium + WebKit against a local mirror of the CDN
117
+ node scripts/mirror.mjs <dir> # self-hosting: the runtime in the CDN's layout
118
+ node scripts/demo.mjs <dir> --local # the demo page (examples/demo) for a host site
119
+ ```
120
+
121
+ The browser check serves `node_modules` the way jsDelivr serves npm
122
+ (`/npm/<pkg>@<version>/…`, exact versions only) from a second origin, so the
123
+ import map, cross-origin module loading and the version pins are exercised
124
+ for real.
125
+
126
+ ## License
127
+
128
+ [MIT](LICENSE)
@@ -0,0 +1 @@
1
+ var e=[{prop:`locale`,type:`string`},{prop:`loading`,type:`enum`,values:[`lazy`,`eager`]}],t=[{prop:`src`,type:`string`},{prop:`loop`,type:`boolean`},{prop:`preload`,type:`enum`,values:[`none`,`metadata`,`auto`]},{prop:`crossorigin`,type:`enum`,values:[``,`anonymous`,`use-credentials`]},{prop:`exclusive`,type:`boolean`},{prop:`resumeKey`,type:`string`},{prop:`rates`,type:`numbers`},{prop:`hotkeys`,type:`boolean`},{prop:`hlsOptions`,type:`json`}];[...e,...t],[...e,...t],[...e],[...e],[...e],[...e],[...e];function n(e){if(typeof e==`number`&&Number.isFinite(e))return String(e);if(typeof e==`string`&&e.trim()!==``)return e.trim().replace(/\s*[:/]\s*/,` / `)}function r(e,t){return typeof e==`number`&&Number.isFinite(e)?`${e}px`:typeof e==`string`&&e.trim()!==``?/^\d+(\.\d+)?$/.test(e.trim())?`${e.trim()}px`:e.trim():t}export{r as n,n as t};
@@ -0,0 +1 @@
1
+ import{computed as e,createApp as t,createVNode as n,defineComponent as r,h as i,render as a,shallowReactive as o,shallowRef as s}from"vue";import{en as c,markdownBlockRenderersKey as l,provideAppLocale as u,provideLocale as d,ru as f}from"@dskripchenko/ui";var p=o({locale:`en`,theme:`light`,renderers:{}}),m=null,h=null,g={};function _(e){if(h)return;h=e,m=t({render:()=>null}),m.config.errorHandler=(e,t,n)=>{console.error(`[dsk-embed] ${n}:`,e)},m.provide(l,g),u(m,()=>y(p.locale));let n=()=>{let t=e.config();p.locale=t.locale,p.theme=t.resolvedTheme},r=()=>{let t=e.blocks.all();for(let e of Object.keys(g))e in t||delete g[e];Object.assign(g,t),p.renderers=t};n(),r(),e.onConfig(n),e.blocks.subscribe(r)}function v(){if(!h)throw Error(`the embed runtime is not set up`);return h}function y(e){return e===`ru`?f:c}var b=r({name:`DskEmbedLocale`,props:{locale:{type:Function,required:!0},content:{type:Function,required:!0}},setup(t){return d(e(()=>y(t.locale()))),()=>t.content()}}),x=r({name:`DskEmbedContent`,props:{content:{type:Function,required:!0}},setup(e){return()=>e.content()}});function S(e,t,r){if(!m)throw Error(`the embed runtime is not set up`);let i=r?n(b,{locale:r,content:t}):n(x,{content:t});return i.appContext=m._context,a(i,e),{unmount(){a(null,e)}}}function C(e,t){let n=o({props:e.props(),source:e.source(),children:e.children(),locale:e.locale()}),r=e.host.onConfig(()=>{n.locale=e.locale()}),i=e.el.hasAttribute(`locale`)||typeof e.props().locale==`string`,a=S(e.container,()=>t(n),i?()=>n.locale:void 0);return{view:n,refresh(){n.props=e.props(),n.source=e.source(),n.children=e.children(),n.locale=e.locale()},unmount(){r(),a.unmount()}}}function w(){let e=s(null);return{ref:e,set:t=>{e.value=t??null}}}var T=/* @__PURE__ */ new Map;function E(e){let t=T.get(e);return t||(t=fetch(e,{credentials:`same-origin`}).then(t=>{if(!t.ok)throw Error(`${e}: HTTP ${t.status}`);return t.text()}),t.catch(()=>T.delete(e)),T.set(e,t)),t}function D(e){return e instanceof Error?e.message:String(e)}var O=i;export{C as a,_ as c,w as i,p as l,E as n,S as o,O as r,v as s,D as t};