@moku-labs/web 1.9.0 → 1.11.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/dist/browser.d.mts +69 -3
- package/dist/browser.mjs +84 -1
- package/dist/index.cjs +409 -30
- package/dist/index.d.cts +89 -3
- package/dist/index.d.mts +89 -3
- package/dist/index.mjs +408 -31
- package/package.json +1 -1
package/dist/browser.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EmitFn } from "@moku-labs/core";
|
|
2
|
-
import { ComponentChildren, VNode } from "preact";
|
|
2
|
+
import { ComponentChildren, FunctionComponent, VNode } from "preact";
|
|
3
3
|
import { BundledTheme, ThemeRegistrationAny } from "shiki";
|
|
4
4
|
import { Pluggable, Processor } from "unified";
|
|
5
5
|
|
|
@@ -1734,6 +1734,13 @@ declare const headPlugin: import("@moku-labs/core").PluginInstance<"head", Confi
|
|
|
1734
1734
|
*/
|
|
1735
1735
|
declare function createComponent(name: string, hooks: ComponentHooks): ComponentDef;
|
|
1736
1736
|
//#endregion
|
|
1737
|
+
//#region src/plugins/spa/lazy-embed.d.ts
|
|
1738
|
+
/**
|
|
1739
|
+
* Lazy-embed island: facade button click → real `<iframe loading="lazy">`.
|
|
1740
|
+
* The companion of the content pipeline's `::embed` directive.
|
|
1741
|
+
*/
|
|
1742
|
+
declare const lazyEmbed: ComponentDef;
|
|
1743
|
+
//#endregion
|
|
1737
1744
|
//#region src/plugins/spa/index.d.ts
|
|
1738
1745
|
/**
|
|
1739
1746
|
* SPA plugin — progressive client-side navigation layered over the static site:
|
|
@@ -1920,7 +1927,7 @@ type DataProvider = {
|
|
|
1920
1927
|
*/
|
|
1921
1928
|
declare const dataPlugin: import("@moku-labs/core").PluginInstance<"data", DataConfig, DataState, DataProvider, {}> & Record<never, never>;
|
|
1922
1929
|
declare namespace types_d_exports {
|
|
1923
|
-
export { Api, Article, ArticleCard, ComputedFields, Config, ContentApiContext, ContentEvents, ContentProvider, ContentProviderState, FileSystemContentOptions, Frontmatter, LoadAllOptions, MermaidDiagramOptions, State };
|
|
1930
|
+
export { Api, Article, ArticleCard, ComputedFields, Config, ContentApiContext, ContentEvents, ContentProvider, ContentProviderState, EmbedFacade, EmbedFacadeProps, EmbedOptions, FileSystemContentOptions, Frontmatter, LoadAllOptions, MermaidDiagramOptions, State };
|
|
1924
1931
|
}
|
|
1925
1932
|
/**
|
|
1926
1933
|
* YAML frontmatter parsed from each article file.
|
|
@@ -2064,6 +2071,55 @@ type MermaidDiagramOptions = {
|
|
|
2064
2071
|
*/
|
|
2065
2072
|
renderDiagrams?: (sources: readonly string[], mermaidConfig?: Record<string, unknown>) => Promise<readonly string[]>;
|
|
2066
2073
|
};
|
|
2074
|
+
/**
|
|
2075
|
+
* Props handed to an `::embed` facade component (the click-to-activate placeholder
|
|
2076
|
+
* the framework renders to static markup at build time). `width`/`height` are the
|
|
2077
|
+
* parsed pixel dimensions when the directive set them; `attributes` is the full raw
|
|
2078
|
+
* directive attribute bag, so a custom facade can read arbitrary extra options
|
|
2079
|
+
* (e.g. `::embed{… poster="/p.jpg" label="Play"}`).
|
|
2080
|
+
*
|
|
2081
|
+
* @example
|
|
2082
|
+
* ```tsx
|
|
2083
|
+
* const Facade = ({ title, attributes }: EmbedFacadeProps) => (
|
|
2084
|
+
* <button type="button" class="lazy-embed-button">
|
|
2085
|
+
* {attributes.poster ? <img src={attributes.poster} alt="" /> : null}
|
|
2086
|
+
* <span class="lazy-embed-title">{title}</span>
|
|
2087
|
+
* </button>
|
|
2088
|
+
* );
|
|
2089
|
+
* ```
|
|
2090
|
+
*/
|
|
2091
|
+
type EmbedFacadeProps = {
|
|
2092
|
+
/** The embed target exactly as written in the directive (the provider resolves it later). */src: string; /** The human-readable embed title (default label + iframe title). */
|
|
2093
|
+
title: string; /** Reserved-box width in pixels, when the directive set `width`/`height`. */
|
|
2094
|
+
width?: number; /** Reserved-box height in pixels, when the directive set `width`/`height`. */
|
|
2095
|
+
height?: number; /** The full raw directive attribute bag (custom options live here). */
|
|
2096
|
+
attributes: Readonly<Record<string, string>>;
|
|
2097
|
+
};
|
|
2098
|
+
/**
|
|
2099
|
+
* A consumer-supplied facade component: a Preact function component over
|
|
2100
|
+
* {@link EmbedFacadeProps}, rendered (at build time, to static markup) as the
|
|
2101
|
+
* facade's inner content — inside the framework-owned `<figure>` that carries the
|
|
2102
|
+
* island hooks + reserved-box sizing. Defaults to the built-in `EmbedFacadeButton`.
|
|
2103
|
+
*/
|
|
2104
|
+
type EmbedFacade = FunctionComponent<EmbedFacadeProps>;
|
|
2105
|
+
/**
|
|
2106
|
+
* Options for the `::embed` lazy-iframe feature (the `embed` key of
|
|
2107
|
+
* {@link FileSystemContentOptions}). `embed: true` uses the default facade;
|
|
2108
|
+
* `embed: { facade }` swaps in a consumer Preact component for the placeholder.
|
|
2109
|
+
*
|
|
2110
|
+
* @example
|
|
2111
|
+
* ```ts
|
|
2112
|
+
* fileSystemContent({ contentDir: "./content", trustedContent: true, embed: { facade: MyFacade } });
|
|
2113
|
+
* ```
|
|
2114
|
+
*/
|
|
2115
|
+
type EmbedOptions = {
|
|
2116
|
+
/**
|
|
2117
|
+
* Consumer Preact component rendering the facade's inner content (SSR'd to
|
|
2118
|
+
* static markup at build — no client JS). Receives {@link EmbedFacadeProps}.
|
|
2119
|
+
* Defaults to the built-in `EmbedFacadeButton`.
|
|
2120
|
+
*/
|
|
2121
|
+
facade?: EmbedFacade;
|
|
2122
|
+
};
|
|
2067
2123
|
/**
|
|
2068
2124
|
* Options for the node filesystem provider {@link ContentProvider} `fileSystemContent`.
|
|
2069
2125
|
* These are the markdown-pipeline + source concerns that used to live on the content
|
|
@@ -2098,6 +2154,16 @@ type FileSystemContentOptions = {
|
|
|
2098
2154
|
* (plus playwright with an installed browser). Defaults to disabled.
|
|
2099
2155
|
*/
|
|
2100
2156
|
mermaid?: boolean | MermaidDiagramOptions;
|
|
2157
|
+
/**
|
|
2158
|
+
* Lazy iframe embeds: rewrite `::embed{src="…" title="…"}` leaf directives
|
|
2159
|
+
* into static click-to-activate facades (no iframe — and none of the target's
|
|
2160
|
+
* network/JS cost — until the reader clicks). Pair with the `lazyEmbed` SPA
|
|
2161
|
+
* island, which swaps the facade for the real `<iframe loading="lazy">`.
|
|
2162
|
+
* `true` enables with the default facade; an object passes {@link EmbedOptions}
|
|
2163
|
+
* (e.g. a consumer `facade` Preact component). Requires `trustedContent: true`
|
|
2164
|
+
* (the facade is raw HTML the sanitize pass would strip). Defaults to disabled.
|
|
2165
|
+
*/
|
|
2166
|
+
embed?: boolean | EmbedOptions;
|
|
2101
2167
|
};
|
|
2102
2168
|
/**
|
|
2103
2169
|
* Internal mutable state of the filesystem provider: the lazy unified processor and
|
|
@@ -2400,4 +2466,4 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
|
|
|
2400
2466
|
*/
|
|
2401
2467
|
declare const createPlugin: import("@moku-labs/core").BoundCreatePluginFunction<Config$5, Events, import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", LogConfig, LogState, LogApi>, import("@moku-labs/core").CorePluginInstance<"env", EnvConfig, EnvState, EnvApi>]>>;
|
|
2402
2468
|
//#endregion
|
|
2403
|
-
export { types_d_exports as Content, types_d_exports$1 as Data, types_d_exports$2 as Env, types_d_exports$3 as Head, types_d_exports$4 as Log, types_d_exports$5 as Router, types_d_exports$6 as Spa, browserEnv, buildArticleHead, canonical, contentPlugin, createApp, createComponent, createPlugin, createUrls, dataPlugin, defineRoutes, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, logPlugin, meta, og, route, routerPlugin, sitePlugin, spaPlugin, twitter };
|
|
2469
|
+
export { types_d_exports as Content, types_d_exports$1 as Data, types_d_exports$2 as Env, types_d_exports$3 as Head, types_d_exports$4 as Log, types_d_exports$5 as Router, types_d_exports$6 as Spa, browserEnv, buildArticleHead, canonical, contentPlugin, createApp, createComponent, createPlugin, createUrls, dataPlugin, defineRoutes, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, lazyEmbed, logPlugin, meta, og, route, routerPlugin, sitePlugin, spaPlugin, twitter };
|
package/dist/browser.mjs
CHANGED
|
@@ -4428,6 +4428,89 @@ function disposeSpa() {
|
|
|
4428
4428
|
}
|
|
4429
4429
|
}
|
|
4430
4430
|
//#endregion
|
|
4431
|
+
//#region src/plugins/spa/lazy-embed.ts
|
|
4432
|
+
/**
|
|
4433
|
+
* @file `lazyEmbed` island — activates the static embed facades emitted by the
|
|
4434
|
+
* content pipeline's `::embed` directive (pipeline/embed.ts). Mounts on every
|
|
4435
|
+
* `[data-component="lazy-embed"]` figure; a click on the facade's button swaps
|
|
4436
|
+
* it for the real `<iframe loading="lazy">`. Until that click the embedded
|
|
4437
|
+
* document costs the page nothing — no request, no third-party JS, no
|
|
4438
|
+
* scroll-jacking. Register it in `pluginConfigs.spa.components`; all visual
|
|
4439
|
+
* chrome (`.lazy-embed*` classes) is consumer CSS.
|
|
4440
|
+
*/
|
|
4441
|
+
/** CSS class on the injected `<iframe>` (consumer CSS sizes it). */
|
|
4442
|
+
const EMBED_FRAME_CLASS = "lazy-embed-frame";
|
|
4443
|
+
/**
|
|
4444
|
+
* Swap a facade `<figure>`'s content for its real `<iframe>`. The iframe
|
|
4445
|
+
* carries `loading="lazy"` plus fullscreen permission, and the figure gains
|
|
4446
|
+
* `data-embed-active` so consumer CSS can restyle the activated state.
|
|
4447
|
+
*
|
|
4448
|
+
* @param figure - The facade element carrying `data-embed-src`/`data-embed-title`.
|
|
4449
|
+
* @example
|
|
4450
|
+
* ```ts
|
|
4451
|
+
* activateEmbed(figure);
|
|
4452
|
+
* ```
|
|
4453
|
+
*/
|
|
4454
|
+
function activateEmbed(figure) {
|
|
4455
|
+
const src = figure.dataset.embedSrc;
|
|
4456
|
+
if (!src) return;
|
|
4457
|
+
const iframe = document.createElement("iframe");
|
|
4458
|
+
iframe.src = src;
|
|
4459
|
+
iframe.title = figure.dataset.embedTitle ?? "";
|
|
4460
|
+
iframe.className = EMBED_FRAME_CLASS;
|
|
4461
|
+
iframe.setAttribute("loading", "lazy");
|
|
4462
|
+
iframe.allow = "fullscreen; autoplay; gamepad";
|
|
4463
|
+
iframe.allowFullscreen = true;
|
|
4464
|
+
figure.replaceChildren(iframe);
|
|
4465
|
+
figure.dataset.embedActive = "";
|
|
4466
|
+
}
|
|
4467
|
+
/**
|
|
4468
|
+
* Shared click handler (module-level so mount/unmount detach the same
|
|
4469
|
+
* reference): any click on the not-yet-active facade activates the embed. It
|
|
4470
|
+
* fires on the whole facade — not a specific button class — so a consumer's
|
|
4471
|
+
* custom facade markup (see content `embed.facade`) works without re-wiring;
|
|
4472
|
+
* the default facade's `<button>` keeps it keyboard-accessible. Once active
|
|
4473
|
+
* (`data-embed-active`), clicks fall through to the live iframe.
|
|
4474
|
+
*
|
|
4475
|
+
* @param event - The click event from the facade figure.
|
|
4476
|
+
* @example
|
|
4477
|
+
* ```ts
|
|
4478
|
+
* element.addEventListener("click", onFacadeClick);
|
|
4479
|
+
* ```
|
|
4480
|
+
*/
|
|
4481
|
+
function onFacadeClick(event) {
|
|
4482
|
+
const figure = event.currentTarget;
|
|
4483
|
+
if (!(figure instanceof HTMLElement)) return;
|
|
4484
|
+
if (figure.dataset.embedActive !== void 0) return;
|
|
4485
|
+
activateEmbed(figure);
|
|
4486
|
+
}
|
|
4487
|
+
/**
|
|
4488
|
+
* Lazy-embed island: facade button click → real `<iframe loading="lazy">`.
|
|
4489
|
+
* The companion of the content pipeline's `::embed` directive.
|
|
4490
|
+
*/
|
|
4491
|
+
const lazyEmbed = createComponent("lazy-embed", {
|
|
4492
|
+
/**
|
|
4493
|
+
* Bind the activation click handler when a facade mounts.
|
|
4494
|
+
*
|
|
4495
|
+
* @param ctx - The island lifecycle context.
|
|
4496
|
+
* @example
|
|
4497
|
+
* onMount(ctx);
|
|
4498
|
+
*/
|
|
4499
|
+
onMount(ctx) {
|
|
4500
|
+
ctx.el.addEventListener("click", onFacadeClick);
|
|
4501
|
+
},
|
|
4502
|
+
/**
|
|
4503
|
+
* Remove the activation click handler when the facade is destroyed.
|
|
4504
|
+
*
|
|
4505
|
+
* @param ctx - The island lifecycle context.
|
|
4506
|
+
* @example
|
|
4507
|
+
* onDestroy(ctx);
|
|
4508
|
+
*/
|
|
4509
|
+
onDestroy(ctx) {
|
|
4510
|
+
ctx.el.removeEventListener("click", onFacadeClick);
|
|
4511
|
+
}
|
|
4512
|
+
});
|
|
4513
|
+
//#endregion
|
|
4431
4514
|
//#region src/plugins/spa/index.ts
|
|
4432
4515
|
/**
|
|
4433
4516
|
* @file spa — Complex Plugin (WIRING ONLY, ≤30 lines). All logic lives in the
|
|
@@ -5115,4 +5198,4 @@ const createApp = core.createApp;
|
|
|
5115
5198
|
*/
|
|
5116
5199
|
const createPlugin = core.createPlugin;
|
|
5117
5200
|
//#endregion
|
|
5118
|
-
export { types_exports as Content, types_exports$1 as Data, types_exports$2 as Env, types_exports$3 as Head, types_exports$4 as Log, types_exports$5 as Router, types_exports$6 as Spa, browserEnv, buildArticleHead, canonical, contentPlugin, createApp, createComponent, createPlugin, createUrls, dataPlugin, defineRoutes, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, logPlugin, meta, og, route, routerPlugin, sitePlugin, spaPlugin, twitter };
|
|
5201
|
+
export { types_exports as Content, types_exports$1 as Data, types_exports$2 as Env, types_exports$3 as Head, types_exports$4 as Log, types_exports$5 as Router, types_exports$6 as Spa, browserEnv, buildArticleHead, canonical, contentPlugin, createApp, createComponent, createPlugin, createUrls, dataPlugin, defineRoutes, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, lazyEmbed, logPlugin, meta, og, route, routerPlugin, sitePlugin, spaPlugin, twitter };
|