@uibot/creation-kit 0.0.0-stage → 0.2.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.
@@ -0,0 +1,81 @@
1
+ import type { CreationContent, CreationDesignPreferences, CreationDomain, CreationProject, CreationProjectCreateInput, CreationProjectSummary, CreationRunInput } from "@uibot/contracts";
2
+ /**
3
+ * Typed client for the creation HTTP contract served by `@uibot/application`'s
4
+ * `createUIBotStudioCreationHandler` (ADR 0012: headless logic ships in a package,
5
+ * hosts only supply transport). Studio and InfoOS call the same routes; the host
6
+ * passes a `fetch` that adds its own base path, credentials and tenant headers.
7
+ */
8
+ export interface CreationClientOptions {
9
+ /** Route base the handler is mounted on, e.g. `/api/studio/creation`. */
10
+ base: string;
11
+ /** Host fetch (auth, tenant, cookies). Defaults to `globalThis.fetch`. */
12
+ fetch?: (url: string, init: RequestInit) => Promise<Response>;
13
+ /**
14
+ * Path of the configuration route relative to `base`. The handler serves it at `base` itself
15
+ * (`""`, the default); a host proxy that needs a named segment (Studio uses `/configuration`)
16
+ * overrides it.
17
+ */
18
+ configurationPath?: string;
19
+ }
20
+ export interface CreationConfiguration {
21
+ domains: CreationDomain[];
22
+ generation: {
23
+ ready: boolean;
24
+ modelId: string;
25
+ reconciliationAvailable?: boolean;
26
+ };
27
+ }
28
+ export interface CreationProjectList {
29
+ projects: CreationProjectSummary[];
30
+ issues?: Array<{
31
+ projectId: string;
32
+ code: string;
33
+ message: string;
34
+ }>;
35
+ }
36
+ export interface CreationSourceInput {
37
+ kind: "reference" | "brand";
38
+ title: string;
39
+ content: string;
40
+ design?: CreationDesignPreferences;
41
+ /** Required with `design` on a brand source: project version the change is based on. */
42
+ expectedVersion?: number;
43
+ }
44
+ export interface CreationArtifactInput {
45
+ kind: string;
46
+ title: string;
47
+ source: CreationContent;
48
+ }
49
+ export interface CreationRevisionInput {
50
+ /** Current tip the edit is based on; a stale value fails with `revision-conflict`. */
51
+ expectedRevisionId: string | null;
52
+ source: CreationContent;
53
+ /** Restore-as-new-revision: the historical revision this source was copied from. */
54
+ restoredFrom?: string;
55
+ }
56
+ /** Error returned by the creation handler (`{ error: { code, message } }`). */
57
+ export declare class CreationApiError extends Error {
58
+ readonly code: string;
59
+ readonly status: number;
60
+ constructor(code: string, message: string, status: number);
61
+ }
62
+ export interface CreationClient {
63
+ /** Low-level request for routes this client does not wrap yet (path is relative to `base`). */
64
+ request<T>(path: string, init?: {
65
+ method?: string;
66
+ body?: unknown;
67
+ signal?: AbortSignal;
68
+ }): Promise<T>;
69
+ getConfiguration(signal?: AbortSignal): Promise<CreationConfiguration>;
70
+ listProjects(signal?: AbortSignal): Promise<CreationProjectList>;
71
+ createProject(input: CreationProjectCreateInput): Promise<CreationProject>;
72
+ getProject(projectId: string, signal?: AbortSignal): Promise<CreationProject>;
73
+ addSource(projectId: string, input: CreationSourceInput): Promise<CreationProject>;
74
+ appendArtifact(projectId: string, input: CreationArtifactInput): Promise<CreationProject>;
75
+ saveRevision(projectId: string, artifactId: string, input: CreationRevisionInput): Promise<CreationProject>;
76
+ startRun(projectId: string, input: CreationRunInput): Promise<CreationProject>;
77
+ cancelRun(projectId: string, runId: string): Promise<CreationProject>;
78
+ resumeRun(projectId: string, runId: string): Promise<CreationProject>;
79
+ }
80
+ export declare function creationProjectPath(projectId: string): string;
81
+ export declare function createCreationClient(options: CreationClientOptions): CreationClient;
package/dist/client.js ADDED
@@ -0,0 +1 @@
1
+ class d extends Error{code;status;constructor(o,c,p){super(c);this.code=o;this.status=p;this.name="CreationApiError"}}function C(o){return`/projects/${encodeURIComponent(o)}`}function l(o){let c=o.base.replace(/\/$/,""),p=o.fetch??((e,t)=>globalThis.fetch(e,t));async function n(e,t={}){let u=t.method??(t.body===void 0?"GET":"POST"),i=await p(`${c}${e}`,{method:u,headers:{accept:"application/json",...t.body===void 0?{}:{"content-type":"application/json"}},...t.body===void 0?{}:{body:JSON.stringify(t.body)},...t.signal?{signal:t.signal}:{}}),g=await i.text(),s=null;if(g)try{s=JSON.parse(g)}catch{s=null}if(!i.ok){let a=s?.error;throw new d(typeof a?.code==="string"?a.code:"request-failed",typeof a?.message==="string"?a.message:`Request failed (${i.status})`,i.status)}if(s===null)throw new d("invalid-response","Empty or non-JSON response",i.status);return s}let r=C;return{request:n,getConfiguration:(e)=>n(o.configurationPath??"",{method:"GET",...e?{signal:e}:{}}),listProjects:(e)=>n("/projects",{method:"GET",...e?{signal:e}:{}}),createProject:(e)=>n("/projects",{body:e}),getProject:(e,t)=>n(r(e),{method:"GET",...t?{signal:t}:{}}),addSource:(e,t)=>n(`${r(e)}/sources`,{body:t}),appendArtifact:(e,t)=>n(`${r(e)}/artifacts`,{body:t}),saveRevision:(e,t,u)=>n(`${r(e)}/artifacts/${encodeURIComponent(t)}/revisions`,{body:u}),startRun:(e,t)=>n(`${r(e)}/runs`,{body:t}),cancelRun:(e,t)=>n(`${r(e)}/runs/${encodeURIComponent(t)}/cancel`,{body:{}}),resumeRun:(e,t)=>n(`${r(e)}/runs/${encodeURIComponent(t)}/resume`,{body:{}})}}export{d as CreationApiError,l as createCreationClient,C as creationProjectPath};
package/dist/cn.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { type ClassValue } from "clsx";
2
+ /** Class-name helper shared by kind packages (same semantics as the host's `cn`). */
3
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,162 @@
1
+ import type { ComponentType, LazyExoticComponent, ReactNode } from "react";
2
+ import type { ArtifactExportFormat, CreationArtifact, CreationContent, CreationProject, CreationRevision, CreationSourceFormat } from "@uibot/contracts";
3
+ /** Bumped only on a breaking change to the plugin contract; checked at registration. */
4
+ export declare const CREATION_KIT_API: 1;
5
+ export type CreationLocale = "zh" | "en";
6
+ export interface LocalizedText {
7
+ zh: string;
8
+ en: string;
9
+ }
10
+ /** Project-level assets the host knows how to pin to artifact revisions. */
11
+ export type CreationAssetSlot = "brand" | "theme";
12
+ export interface CreationKindPlugin {
13
+ /** Unique plugin id, e.g. "logo". */
14
+ id: string;
15
+ version: string;
16
+ kitApi: typeof CREATION_KIT_API;
17
+ /** Workbench-style task kinds (no server domain required). */
18
+ kinds?: CreationTaskKindContribution[];
19
+ /** Editors resolved by `CreationDomain.editors[format]`. */
20
+ editors?: CreationEditorContribution[];
21
+ /** Read-only artifact views, e.g. slide-by-slide navigation. */
22
+ views?: CreationArtifactViewContribution[];
23
+ projectAssets?: CreationProjectAssetContribution[];
24
+ }
25
+ export interface CreationTaskKindContribution {
26
+ /** Value of the `?kind=` route parameter. Must not collide with a server artifactKind. */
27
+ kind: string;
28
+ label: LocalizedText;
29
+ icon?: ComponentType<{
30
+ className?: string;
31
+ }>;
32
+ order?: number;
33
+ /** Shown in the conversation pane before any activity. */
34
+ emptyChat?: LocalizedText;
35
+ /** Composer placeholder while this kind is selected; the host's generic prompt otherwise. */
36
+ placeholder?: LocalizedText;
37
+ layout?: {
38
+ viewportClassName?: string;
39
+ frameClassName?: string;
40
+ };
41
+ /** Consumes this kind's own route parameters; invalid values are dropped. */
42
+ parseSearch?(raw: Record<string, unknown>): Record<string, string>;
43
+ /** Project-context menu entry that starts this kind from an existing project. */
44
+ menu?: {
45
+ title: LocalizedText;
46
+ description: LocalizedText;
47
+ };
48
+ /** Opens a project asset dialog straight from route parameters (e.g. a pre-selected draft). */
49
+ initialAsset?(search: Record<string, string>): {
50
+ slot: CreationAssetSlot;
51
+ draft: unknown;
52
+ } | null;
53
+ Workbench: CreationComponent<CreationWorkbenchProps>;
54
+ }
55
+ export type CreationComponent<P> = ComponentType<P> | LazyExoticComponent<ComponentType<P>>;
56
+ export interface CreationWorkbenchProps {
57
+ project: CreationProject;
58
+ locale: CreationLocale;
59
+ /** Hidden workbenches stay mounted so in-progress work survives kind switches. */
60
+ active: boolean;
61
+ /** Host-owned slot where the workbench renders its composer controls. */
62
+ composerTarget: HTMLElement | null;
63
+ kindPicker: ReactNode;
64
+ initialIntent?: string;
65
+ /** Route parameters from `parseSearch`. */
66
+ search: Record<string, string>;
67
+ selection: {
68
+ artifactId: string;
69
+ revisionId: string;
70
+ } | null;
71
+ onBusyChange(busy: boolean): void;
72
+ onFocusComposer(): void;
73
+ /** Mirrors workbench activity into the conversation pane. */
74
+ onActivity(role: "user" | "assistant", text: string): void;
75
+ /** Opens the host dialog for a project asset slot, optionally with a draft. */
76
+ openProjectAsset(slot: CreationAssetSlot, draft?: unknown, options?: {
77
+ returnFocusId?: string;
78
+ }): void;
79
+ /** Receives the updated project after any write. */
80
+ onProject(project: CreationProject): void;
81
+ }
82
+ export interface CreationEditorContribution {
83
+ /** Matches `CreationDomain.editors[format]`. */
84
+ id: string;
85
+ format: CreationSourceFormat;
86
+ label: LocalizedText;
87
+ Editor: CreationComponent<CreationEditorProps>;
88
+ }
89
+ export interface CreationEditorProps {
90
+ source: CreationContent;
91
+ locale: CreationLocale;
92
+ toolbarTarget?: HTMLElement | null;
93
+ onDirtyChange(dirty: boolean): void;
94
+ /** The host owns CAS, brand restoration and revision commit. */
95
+ onSave(content: string): Promise<void>;
96
+ }
97
+ export interface CreationViewContext {
98
+ artifactKind: string;
99
+ format: CreationSourceFormat;
100
+ content: string;
101
+ }
102
+ export interface CreationArtifactViewContribution {
103
+ id: string;
104
+ label: LocalizedText;
105
+ match(ctx: CreationViewContext): boolean;
106
+ View: CreationComponent<CreationArtifactViewProps>;
107
+ }
108
+ export interface CreationArtifactViewProps {
109
+ content: string;
110
+ title: string;
111
+ locale: CreationLocale;
112
+ active: boolean;
113
+ }
114
+ export interface CreationProjectAssetContribution {
115
+ slot: CreationAssetSlot;
116
+ label: LocalizedText;
117
+ /** Task kind that creates a new asset, e.g. brand → "logo". */
118
+ createKind?: string;
119
+ /** Entry shown in the project-context menu, whether or not the asset exists yet. */
120
+ summary(project: CreationProject, locale: CreationLocale): ProjectAssetSummary;
121
+ /** Short state shown on the context-menu trigger, e.g. "Brand saved". */
122
+ status(project: CreationProject, locale: CreationLocale): string;
123
+ /** Title of the dialog hosting `Panel`. */
124
+ dialog: {
125
+ title: LocalizedText;
126
+ description: LocalizedText;
127
+ settingsLabel: LocalizedText;
128
+ };
129
+ revisionBadge?(project: CreationProject, revision: CreationRevision, locale: CreationLocale): string | null;
130
+ exportConstraint?(revision: CreationRevision): {
131
+ formats?: ArtifactExportFormat[];
132
+ note?: LocalizedText;
133
+ } | null;
134
+ Panel: CreationComponent<ProjectAssetPanelProps>;
135
+ }
136
+ export interface ProjectAssetSummary {
137
+ title: string;
138
+ subtitle: string;
139
+ ariaLabel: string;
140
+ thumbnailUrl?: string;
141
+ }
142
+ /** Outcome shown by the host after the asset panel saves or applies something. */
143
+ export interface ProjectAssetReceipt {
144
+ message: string;
145
+ artifactId?: string;
146
+ revisionId?: string;
147
+ }
148
+ export interface ProjectAssetPanelProps {
149
+ project: CreationProject;
150
+ selection: {
151
+ artifact?: CreationArtifact;
152
+ revision?: CreationRevision;
153
+ dirty: boolean;
154
+ };
155
+ draft?: unknown;
156
+ locale: CreationLocale;
157
+ onProject(project: CreationProject): void;
158
+ onReceipt(receipt: ProjectAssetReceipt): void;
159
+ onApplied(artifactId: string, revisionId: string): void;
160
+ /** Starts the task kind that creates this asset (`createKind`), closing the dialog. */
161
+ onCreateKind?(): void;
162
+ }
@@ -0,0 +1,3 @@
1
+ import { type CreationKindPlugin } from "./contract.js";
2
+ /** Built-in plugin: slide-by-slide viewing for presentation artifacts. */
3
+ export declare const presentationDeckPlugin: CreationKindPlugin;
@@ -0,0 +1,7 @@
1
+ import type { CreationArtifactViewProps } from "./contract.js";
2
+ /**
3
+ * Read-only slide viewer: one sandboxed iframe per slide, keyboard stepping
4
+ * (←/→/Space/PageUp/PageDown/Home/End, N notes, F fullscreen), a numbered strip, speaker
5
+ * notes and print-to-PDF. Shortcuts ignore editable targets and only listen while `active`.
6
+ */
7
+ export declare function DeckViewer({ content, locale, active }: CreationArtifactViewProps): import("react").JSX.Element;
package/dist/deck.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Slide-deck helpers for presentation artifacts.
3
+ *
4
+ * Presentation artifacts are one HTML document whose slides are top-level
5
+ * `<section data-slide>` elements (the `presentation` domain contract), with that slide's
6
+ * speaker notes in an `<aside data-notes>`. Older Canvas decks used `<section class="slide">`
7
+ * with `<aside class="notes">`; both shapes are accepted. This module splits the document
8
+ * into one standalone document per slide so a viewer can render and step through them.
9
+ * Pure string work: no DOM, so it is testable and safe to run anywhere.
10
+ */
11
+ export interface DeckSlide {
12
+ /** Standalone HTML doc (head + just-this-slide body) for srcdoc. */
13
+ html: string;
14
+ /** Speaker notes plain text, if any. */
15
+ notes: string;
16
+ /** Slide id from data-slide / data-slide-id, or the 1-based index. */
17
+ id: string;
18
+ /** Best-effort section background for the thumbnail strip. */
19
+ bg?: string;
20
+ }
21
+ export interface DeckParsed {
22
+ isDeck: boolean;
23
+ slides: DeckSlide[];
24
+ head: string;
25
+ bodyClass: string;
26
+ bodyStyle: string;
27
+ title: string;
28
+ }
29
+ /** Cheap test: does the document contain at least one slide section? */
30
+ export declare function isDeck(html: string): boolean;
31
+ /** Parse a full deck HTML document into per-slide standalone documents. */
32
+ export declare function parseDeck(fullHtml: string): DeckParsed;
33
+ /**
34
+ * Trusted print-window shell: `@page` CSS, one empty sandboxed iframe per slide and the
35
+ * `window.print()` trigger. It takes only a count, so no AI-authored text can reach it.
36
+ */
37
+ export declare function buildPrintShellHtml(slideCount: number): string;
38
+ /**
39
+ * Open a print window (one slide per page) so the browser can "Save as PDF". Returns false
40
+ * when the popup is blocked.
41
+ *
42
+ * Deck HTML is AI-authored and may carry scripts. The popup is same-origin with Studio, so
43
+ * its document only ever receives `buildPrintShellHtml`'s trusted markup; each slide's
44
+ * standalone document is assigned through the iframe `srcdoc` DOM property into a
45
+ * `sandbox="allow-scripts"` frame (no `allow-same-origin`), and the opener reference is cut.
46
+ */
47
+ export declare function printDeckToPdf(parsed: DeckParsed, open?: (url: string, target: string) => Window | null): boolean;
@@ -0,0 +1,2 @@
1
+ /** HTTP status carried by a host request error (e.g. Studio's `StudioApiError`), if any. */
2
+ export declare function creationErrorStatus(error: unknown): number | undefined;
@@ -0,0 +1,11 @@
1
+ export * from "./contract.js";
2
+ export * from "./registry.js";
3
+ export * from "./transport.js";
4
+ export * from "./cn.js";
5
+ export * from "./errors.js";
6
+ export * from "./locale.js";
7
+ export * from "./ui.js";
8
+ export * from "./deck.js";
9
+ export * from "./deck-plugin.js";
10
+ export { DeckViewer } from "./deck-viewer.js";
11
+ export * from "./client.js";
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ var v=1;var I=/^[a-z][a-z0-9-]{0,39}$/;function R(e,c,r,t){if(c.has(r))throw Error(`Duplicate creation ${e} "${r}" (plugin ${t})`);c.add(r)}function xe(e){let c=new Set,r=new Map,t=new Map,l=new Set,a=[],n=new Map;for(let o of e){if(o.kitApi!==v)throw Error(`Creation plugin "${o.id}" targets kit API ${String(o.kitApi)}, expected ${v}`);R("plugin",c,o.id,o.id);for(let i of o.kinds??[]){if(!I.test(i.kind))throw Error(`Invalid creation kind "${i.kind}" (plugin ${o.id})`);if(r.has(i.kind))throw Error(`Duplicate creation kind "${i.kind}" (plugin ${o.id})`);r.set(i.kind,i)}for(let i of o.editors??[]){let p=`${i.id}:${i.format}`;if(t.has(p))throw Error(`Duplicate creation editor "${p}" (plugin ${o.id})`);t.set(p,i)}for(let i of o.views??[])R("view",l,i.id,o.id),a.push(i);for(let i of o.projectAssets??[]){if(n.has(i.slot))throw Error(`Duplicate creation asset slot "${i.slot}" (plugin ${o.id})`);n.set(i.slot,i)}}let f=[...r.values()].sort((o,i)=>(o.order??100)-(i.order??100)||o.kind.localeCompare(i.kind));return{plugins:e,taskKind:(o)=>o?r.get(o):void 0,taskKinds:()=>[...f],kindOptions(o,i,p){let g=o.map((d)=>({value:d.artifactKind,label:p?.(d)??d.title,disabled:!d.enabled})),C=new Set(g.map((d)=>d.value));for(let d of f){if(C.has(d.kind)){console.error(`Creation workbench kind "${d.kind}" shadows a server domain and is hidden`);continue}g.push({value:d.kind,label:d.label[i]})}return g},label:(o,i,p)=>r.get(o)?.label[i]??p??o,editor:(o,i)=>o?t.get(`${o}:${i}`):void 0,views:(o)=>a.filter((i)=>i.match(o)),projectAssets:()=>[...n.values()],projectAsset:(o)=>n.get(o),parseRouteSearch(o){let i=typeof o.kind==="string"&&I.test(o.kind)?o.kind:void 0,p={};for(let g of f){if(!g.parseSearch)continue;Object.assign(p,g.parseSearch(o))}return{...i?{kind:i}:{},extra:p}}}}import{createContext as M,useContext as H}from"react";import{jsx as q}from"react/jsx-runtime";var U=M(null);function Pe(e){return q(U.Provider,{value:e.transport,children:e.children})}function he(){let e=H(U);if(!e)throw Error("useCreationTransport requires a CreationTransportProvider");return e}import{clsx as _}from"clsx";import{twMerge as G}from"tailwind-merge";function De(...e){return G(_(e))}function je(e){if(!(e instanceof Error))return;let{status:c}=e;return typeof c==="number"?c:void 0}import{createContext as J,useContext as W}from"react";import{jsx as Y}from"react/jsx-runtime";var N=J("en");function Ue(e){return Y(N.Provider,{value:e.locale,children:e.children})}function Ne(){return W(N)}import{createContext as X,useContext as Z}from"react";import{jsx as L}from"react/jsx-runtime";var E=X(null);function Oe(e){return L(E.Provider,{value:e.primitives,children:e.children})}function Q(){let e=Z(E);if(!e)throw Error("Creation UI primitives require a CreationUiProvider");return e}function s(e,c){let r=(t)=>{let l=e(Q());return L(l,{...t})};return r.displayName=c,r}var Ke=s((e)=>e.Button,"Button"),Ve=s((e)=>e.Badge,"Badge"),ze=s((e)=>e.Label,"Label"),Fe=s((e)=>e.Input,"Input"),Be=s((e)=>e.Textarea,"Textarea"),Me=s((e)=>e.Checkbox,"Checkbox"),He=s((e)=>e.Collapsible,"Collapsible"),qe=s((e)=>e.CollapsibleTrigger,"CollapsibleTrigger"),_e=s((e)=>e.CollapsibleContent,"CollapsibleContent"),Ge=s((e)=>e.ScrollArea,"ScrollArea"),Je=s((e)=>e.ScrollBar,"ScrollBar"),We=s((e)=>e.Select,"Select"),Ye=s((e)=>e.SelectTrigger,"SelectTrigger"),Xe=s((e)=>e.SelectValue,"SelectValue"),Ze=s((e)=>e.SelectContent,"SelectContent"),Qe=s((e)=>e.SelectItem,"SelectItem"),et=s((e)=>e.Tabs,"Tabs"),tt=s((e)=>e.TabsList,"TabsList"),ot=s((e)=>e.TabsTrigger,"TabsTrigger"),nt=s((e)=>e.TabsContent,"TabsContent"),rt=s((e)=>e.Tooltip,"Tooltip"),it=s((e)=>e.TooltipProvider,"TooltipProvider"),at=s((e)=>e.TooltipTrigger,"TooltipTrigger"),st=s((e)=>e.TooltipContent,"TooltipContent"),lt=s((e)=>e.Popover,"Popover"),ct=s((e)=>e.PopoverTrigger,"PopoverTrigger"),pt=s((e)=>e.PopoverContent,"PopoverContent"),dt=s((e)=>e.Dialog,"Dialog"),ut=s((e)=>e.DialogTrigger,"DialogTrigger"),gt=s((e)=>e.DialogContent,"DialogContent"),Ct=s((e)=>e.DialogHeader,"DialogHeader"),mt=s((e)=>e.DialogFooter,"DialogFooter"),ft=s((e)=>e.DialogTitle,"DialogTitle"),bt=s((e)=>e.DialogDescription,"DialogDescription"),xt=s((e)=>e.Prompt,"Prompt");var w=/<(\/?)section\b[^>]*>/gi;function T(e,c){let r=new RegExp(`\\b${c}(?:\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+)))?`,"i").exec(e);if(!r)return;return r[1]??r[2]??r[3]??""}function ee(e){if(T(e,"data-slide")!==void 0)return!0;return/\bslide\b/i.test(T(e,"class")??"")}function k(e,c){return e.exec(c)?.[1]??""}function te(e){return e.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'")}function O(e){return te(e.replace(/<[^>]+>/g," ")).replace(/\s+/g," ").trim()}function K(e){let c=[];w.lastIndex=0;let r=0,t=-1;for(let l=w.exec(e);l;l=w.exec(e)){if(l[1]==="/"){if(r===0)continue;if(r-=1,r===0&&t>=0)c.push(e.slice(t,l.index+l[0].length)),t=-1;continue}if(r===0){if(!ee(l[0]))continue;t=l.index}r+=1}return c}function V(e){return Boolean(e)&&K(e).length>0}var oe=/<aside\b([^>]*)>([\s\S]*?)<\/aside>/i;function ne(e){let c="";return{slide:e.replace(oe,(t,l,a)=>{if(!(T(`<aside ${l}>`,"data-notes")!==void 0||/\bnotes\b/i.test(T(`<aside ${l}>`,"class")??"")))return t;return c=O(a),""}),notes:c}}function z(e){let c=K(e);if(c.length===0)return{isDeck:!1,slides:[],head:"",bodyClass:"",bodyStyle:"",title:"deck"};let r=k(/<head\b[^>]*>([\s\S]*?)<\/head>/i,e),t=`<body ${k(/<body\b([^>]*)>/i,e)}>`,l=T(t,"class")??"",a=T(t,"style")??"",n=O(k(/<title\b[^>]*>([\s\S]*?)<\/title>/i,r))||"deck",f=new Set;return{isDeck:!0,slides:c.map((i,p)=>{let g=k(/(<section\b[^>]*>)/i,i),C=T(g,"data-slide")||T(g,"data-slide-id")||String(p+1),d=f.has(C)?`${C}-${p+1}`:C;f.add(d);let x=k(/background(?:-color)?\s*:\s*([^;"']+)/i,T(g,"style")??"").trim()||void 0,{slide:m,notes:u}=ne(i);return{html:`<!DOCTYPE html><html><head>${r}
2
+ <style>
3
+ html, body { margin:0; padding:0; }
4
+ body { display:flex; align-items:center; justify-content:center; min-height:100vh; }
5
+ .slide, [data-slide] { transform-origin: center center !important; }
6
+ </style></head><body class="${l}" style="${a}">${m}</body></html>`,notes:u,id:d,...x?{bg:x}:{}}}),head:r,bodyClass:l,bodyStyle:a,title:n}}function re(e){return`<!DOCTYPE html><html><head>
7
+ <style>
8
+ @page { size: 1280px 720px; margin: 0; }
9
+ html, body { margin:0; padding:0; }
10
+ .page { width: 1280px; height: 720px; overflow: hidden; page-break-after: always; }
11
+ .page:last-child { page-break-after: auto; }
12
+ .page iframe.slide { display:block; width:100%; height:100%; border:0; }
13
+ </style></head><body>
14
+ ${Array.from({length:e},(r,t)=>`<div class="page"><iframe class="slide" data-slide-index="${t}" sandbox="allow-scripts"></iframe></div>`).join(`
15
+ `)}
16
+ <script>window.onload=()=>setTimeout(()=>window.print(),300)</script>
17
+ </body></html>`}function F(e,c=(r,t)=>window.open(r,t)){let r=c("","_blank");if(!r)return!1;r.opener=null,r.document.write(re(e.slides.length)),r.document.close();let t=r.document.querySelectorAll("iframe.slide");return e.slides.forEach((l,a)=>{let n=t[a];if(n)n.srcdoc=l.html}),!0}import{ChevronLeft as ie,ChevronRight as ae,FileDown as se,Maximize2 as le,StickyNote as ce}from"lucide-react";import{useEffect as B,useMemo as pe,useRef as de,useState as S}from"react";import{jsx as b,jsxs as y}from"react/jsx-runtime";var ue={empty:"No slides found (each slide must be a <section data-slide>).",prev:"Previous slide",next:"Next slide",notes:"Notes",notesTitle:"Speaker notes (N)",notesHeading:"Speaker notes",present:"Present",presentTitle:"Full screen (F)",pdfTitle:"Print to PDF",blocked:"The popup was blocked. Allow popups for this site and try again.",page:"Slide"},ge={empty:"未检测到幻灯片(每页需为 <section data-slide>)。",prev:"上一页",next:"下一页",notes:"备注",notesTitle:"演讲者备注(N)",notesHeading:"演讲者备注",present:"放映",presentTitle:"全屏放映(F)",pdfTitle:"打印为 PDF",blocked:"弹窗被拦截,请允许本站弹出窗口后重试。",page:"第"},Ce={en:ue,zh:ge},D="inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs";function A({content:e,locale:c,active:r}){let t=Ce[c],l=pe(()=>z(e),[e]),[a,n]=S(0),[f,o]=S(!1),[i,p]=S(""),g=de(null),C=l.slides.length,d=Math.min(a,Math.max(0,C-1)),x=l.slides[d];if(B(()=>n(0),[e]),B(()=>{if(!r||C===0)return;let m=(u)=>{let P=u.target;if(P&&(P.tagName==="INPUT"||P.tagName==="TEXTAREA"||P.isContentEditable))return;if(u.key==="ArrowRight"||u.key==="PageDown"||u.key===" ")u.preventDefault(),n((h)=>Math.min(h+1,C-1));else if(u.key==="ArrowLeft"||u.key==="PageUp")u.preventDefault(),n((h)=>Math.max(h-1,0));else if(u.key==="Home")n(0);else if(u.key==="End")n(C-1);else if(u.key.toLowerCase()==="n")o((h)=>!h);else if(u.key.toLowerCase()==="f")if(document.fullscreenElement)document.exitFullscreen();else g.current?.requestFullscreen?.()};return window.addEventListener("keydown",m),()=>window.removeEventListener("keydown",m)},[r,C]),!x)return b("div",{className:"flex h-full items-center justify-center p-6 text-sm text-muted-foreground",children:t.empty});return y("div",{ref:g,className:"flex h-full min-h-72 flex-col bg-muted/30",children:[y("div",{className:"relative min-h-0 flex-1",children:[b("iframe",{title:`slide-${x.id}`,srcDoc:x.html,sandbox:"allow-scripts",className:"h-full w-full border-0 bg-white"},x.id),b("button",{type:"button",onClick:()=>n((m)=>Math.max(m-1,0)),disabled:d===0,className:"absolute left-2 top-1/2 -translate-y-1/2 rounded-full border bg-background/80 p-1.5 shadow backdrop-blur disabled:opacity-30","aria-label":t.prev,children:b(ie,{className:"size-4"})}),b("button",{type:"button",onClick:()=>n((m)=>Math.min(m+1,C-1)),disabled:d===C-1,className:"absolute right-2 top-1/2 -translate-y-1/2 rounded-full border bg-background/80 p-1.5 shadow backdrop-blur disabled:opacity-30","aria-label":t.next,children:b(ae,{className:"size-4"})}),y("div",{className:"absolute bottom-2 left-1/2 -translate-x-1/2 rounded-full bg-foreground/80 px-2.5 py-0.5 text-xs tabular-nums text-background",children:[d+1," / ",C]}),f&&x.notes&&y("div",{className:"absolute inset-x-3 bottom-10 max-h-[40%] overflow-y-auto rounded-md border bg-background/95 p-3 text-sm shadow-lg",children:[b("div",{className:"mb-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground",children:t.notesHeading}),x.notes]})]}),i&&b("p",{role:"status",className:"border-t bg-background px-3 py-1 text-xs text-destructive",children:i}),y("div",{className:"flex items-center gap-2 border-t bg-background px-3 py-2",children:[b("div",{className:"flex min-w-0 flex-1 gap-1.5 overflow-x-auto",children:l.slides.map((m,u)=>b("button",{type:"button",onClick:()=>n(u),"aria-label":`${t.page} ${u+1}`,"aria-current":u===d?"true":void 0,...m.bg?{style:{background:m.bg}}:{},className:`flex size-7 shrink-0 items-center justify-center rounded border text-[11px] tabular-nums ${u===d?"border-primary ring-2 ring-primary/30":"text-muted-foreground hover:border-foreground/30"}`,children:u+1},m.id))}),y("button",{type:"button",onClick:()=>o((m)=>!m),className:`${D} ${f?"border-primary bg-primary/10 text-primary":"text-muted-foreground hover:bg-accent"}`,title:t.notesTitle,children:[b(ce,{className:"size-3.5"}),t.notes]}),y("button",{type:"button",onClick:()=>{if(!document.fullscreenElement)g.current?.requestFullscreen?.()},className:`${D} text-muted-foreground hover:bg-accent`,title:t.presentTitle,children:[b(le,{className:"size-3.5"}),t.present]}),y("button",{type:"button",onClick:()=>p(F(l)?"":t.blocked),className:`${D} text-muted-foreground hover:bg-accent`,title:t.pdfTitle,children:[b(se,{className:"size-3.5"}),"PDF"]})]})]})}var It={id:"presentation-deck",version:"1.0.0",kitApi:v,views:[{id:"slides",label:{zh:"幻灯片",en:"Slides"},match:(e)=>e.artifactKind==="presentation"&&e.format==="html"&&V(e.content),View:A}]};class j extends Error{code;status;constructor(e,c,r){super(c);this.code=e;this.status=r;this.name="CreationApiError"}}function me(e){return`/projects/${encodeURIComponent(e)}`}function Ut(e){let c=e.base.replace(/\/$/,""),r=e.fetch??((a,n)=>globalThis.fetch(a,n));async function t(a,n={}){let f=n.method??(n.body===void 0?"GET":"POST"),o=await r(`${c}${a}`,{method:f,headers:{accept:"application/json",...n.body===void 0?{}:{"content-type":"application/json"}},...n.body===void 0?{}:{body:JSON.stringify(n.body)},...n.signal?{signal:n.signal}:{}}),i=await o.text(),p=null;if(i)try{p=JSON.parse(i)}catch{p=null}if(!o.ok){let g=p?.error;throw new j(typeof g?.code==="string"?g.code:"request-failed",typeof g?.message==="string"?g.message:`Request failed (${o.status})`,o.status)}if(p===null)throw new j("invalid-response","Empty or non-JSON response",o.status);return p}let l=me;return{request:t,getConfiguration:(a)=>t(e.configurationPath??"",{method:"GET",...a?{signal:a}:{}}),listProjects:(a)=>t("/projects",{method:"GET",...a?{signal:a}:{}}),createProject:(a)=>t("/projects",{body:a}),getProject:(a,n)=>t(l(a),{method:"GET",...n?{signal:n}:{}}),addSource:(a,n)=>t(`${l(a)}/sources`,{body:n}),appendArtifact:(a,n)=>t(`${l(a)}/artifacts`,{body:n}),saveRevision:(a,n,f)=>t(`${l(a)}/artifacts/${encodeURIComponent(n)}/revisions`,{body:f}),startRun:(a,n)=>t(`${l(a)}/runs`,{body:n}),cancelRun:(a,n)=>t(`${l(a)}/runs/${encodeURIComponent(n)}/cancel`,{body:{}}),resumeRun:(a,n)=>t(`${l(a)}/runs/${encodeURIComponent(n)}/resume`,{body:{}})}}export{Ve as Badge,Ke as Button,v as CREATION_KIT_API,Me as Checkbox,He as Collapsible,_e as CollapsibleContent,qe as CollapsibleTrigger,j as CreationApiError,Ue as CreationLocaleProvider,Pe as CreationTransportProvider,Oe as CreationUiProvider,A as DeckViewer,dt as Dialog,gt as DialogContent,bt as DialogDescription,mt as DialogFooter,Ct as DialogHeader,ft as DialogTitle,ut as DialogTrigger,Fe as Input,ze as Label,lt as Popover,pt as PopoverContent,ct as PopoverTrigger,xt as Prompt,Ge as ScrollArea,Je as ScrollBar,We as Select,Ze as SelectContent,Qe as SelectItem,Ye as SelectTrigger,Xe as SelectValue,et as Tabs,nt as TabsContent,tt as TabsList,ot as TabsTrigger,Be as Textarea,rt as Tooltip,st as TooltipContent,it as TooltipProvider,at as TooltipTrigger,re as buildPrintShellHtml,De as cn,Ut as createCreationClient,xe as createCreationKindRegistry,je as creationErrorStatus,me as creationProjectPath,V as isDeck,z as parseDeck,It as presentationDeckPlugin,F as printDeckToPdf,Ne as useCreationLocale,he as useCreationTransport};
@@ -0,0 +1,8 @@
1
+ import { type ReactNode } from "react";
2
+ import type { CreationLocale } from "./contract.js";
3
+ /** Host-owned locale; kind packages never read Studio's locale store. */
4
+ export declare function CreationLocaleProvider(props: {
5
+ locale: CreationLocale;
6
+ children: ReactNode;
7
+ }): import("react").JSX.Element;
8
+ export declare function useCreationLocale(): CreationLocale;
@@ -0,0 +1,25 @@
1
+ import type { CreationDomain, CreationSourceFormat } from "@uibot/contracts";
2
+ import { type CreationArtifactViewContribution, type CreationAssetSlot, type CreationEditorContribution, type CreationKindPlugin, type CreationLocale, type CreationProjectAssetContribution, type CreationTaskKindContribution, type CreationViewContext } from "./contract.js";
3
+ export interface CreationKindOption {
4
+ value: string;
5
+ label: string;
6
+ disabled?: boolean;
7
+ }
8
+ export interface CreationKindRegistry {
9
+ plugins: readonly CreationKindPlugin[];
10
+ taskKind(kind: string | undefined): CreationTaskKindContribution | undefined;
11
+ taskKinds(): CreationTaskKindContribution[];
12
+ /** Server domains first, then workbench kinds. A workbench kind that shadows a domain is hidden. */
13
+ kindOptions(domains: readonly CreationDomain[], locale: CreationLocale, domainLabel?: (domain: CreationDomain) => string): CreationKindOption[];
14
+ label(kind: string, locale: CreationLocale, fallback?: string): string;
15
+ editor(id: string | undefined, format: CreationSourceFormat): CreationEditorContribution | undefined;
16
+ views(ctx: CreationViewContext): CreationArtifactViewContribution[];
17
+ projectAssets(): CreationProjectAssetContribution[];
18
+ projectAsset(slot: CreationAssetSlot): CreationProjectAssetContribution | undefined;
19
+ /** Splits route search into a syntactically valid `kind` and each kind's own parameters. */
20
+ parseRouteSearch(raw: Record<string, unknown>): {
21
+ kind?: string;
22
+ extra: Record<string, string>;
23
+ };
24
+ }
25
+ export declare function createCreationKindRegistry(plugins: readonly CreationKindPlugin[]): CreationKindRegistry;
@@ -0,0 +1,13 @@
1
+ import { type ReactNode } from "react";
2
+ import { type CreationLocale, type CreationUiPrimitives } from "@uibot/creation-kit";
3
+ /**
4
+ * Unstyled stand-ins for the host's UI primitives: every slot renders a plain element that
5
+ * forwards its props. Enough for markup-level tests of kind components; interactive widgets
6
+ * (menus, dialogs) belong in host integration tests with the real component library.
7
+ */
8
+ export declare function createPlainUi(): CreationUiPrimitives;
9
+ /** Wraps children with plain UI primitives and a locale, the minimum a kind component needs. */
10
+ export declare function PlainKitProviders(props: {
11
+ locale?: CreationLocale;
12
+ children: ReactNode;
13
+ }): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import{createElement as Pe}from"react";var h=1;var j=/^[a-z][a-z0-9-]{0,39}$/;function I(e,c,n,t){if(c.has(n))throw Error(`Duplicate creation ${e} "${n}" (plugin ${t})`);c.add(n)}function we(e){let c=new Set,n=new Map,t=new Map,l=new Set,a=[],r=new Map;for(let o of e){if(o.kitApi!==h)throw Error(`Creation plugin "${o.id}" targets kit API ${String(o.kitApi)}, expected ${h}`);I("plugin",c,o.id,o.id);for(let i of o.kinds??[]){if(!j.test(i.kind))throw Error(`Invalid creation kind "${i.kind}" (plugin ${o.id})`);if(n.has(i.kind))throw Error(`Duplicate creation kind "${i.kind}" (plugin ${o.id})`);n.set(i.kind,i)}for(let i of o.editors??[]){let p=`${i.id}:${i.format}`;if(t.has(p))throw Error(`Duplicate creation editor "${p}" (plugin ${o.id})`);t.set(p,i)}for(let i of o.views??[])I("view",l,i.id,o.id),a.push(i);for(let i of o.projectAssets??[]){if(r.has(i.slot))throw Error(`Duplicate creation asset slot "${i.slot}" (plugin ${o.id})`);r.set(i.slot,i)}}let C=[...n.values()].sort((o,i)=>(o.order??100)-(i.order??100)||o.kind.localeCompare(i.kind));return{plugins:e,taskKind:(o)=>o?n.get(o):void 0,taskKinds:()=>[...C],kindOptions(o,i,p){let g=o.map((d)=>({value:d.artifactKind,label:p?.(d)??d.title,disabled:!d.enabled})),m=new Set(g.map((d)=>d.value));for(let d of C){if(m.has(d.kind)){console.error(`Creation workbench kind "${d.kind}" shadows a server domain and is hidden`);continue}g.push({value:d.kind,label:d.label[i]})}return g},label:(o,i,p)=>n.get(o)?.label[i]??p??o,editor:(o,i)=>o?t.get(`${o}:${i}`):void 0,views:(o)=>a.filter((i)=>i.match(o)),projectAssets:()=>[...r.values()],projectAsset:(o)=>r.get(o),parseRouteSearch(o){let i=typeof o.kind==="string"&&j.test(o.kind)?o.kind:void 0,p={};for(let g of C){if(!g.parseSearch)continue;Object.assign(p,g.parseSearch(o))}return{...i?{kind:i}:{},extra:p}}}}import{createContext as _,useContext as G}from"react";import{jsx as J}from"react/jsx-runtime";var U=_(null);function Ae(e){return J(U.Provider,{value:e.transport,children:e.children})}function Re(){let e=G(U);if(!e)throw Error("useCreationTransport requires a CreationTransportProvider");return e}import{clsx as W}from"clsx";import{twMerge as Y}from"tailwind-merge";function Le(...e){return Y(W(e))}function $e(e){if(!(e instanceof Error))return;let{status:c}=e;return typeof c==="number"?c:void 0}import{createContext as X,useContext as Z}from"react";import{jsx as Q}from"react/jsx-runtime";var N=X("en");function L(e){return Q(N.Provider,{value:e.locale,children:e.children})}function Ve(){return Z(N)}import{createContext as ee,useContext as te}from"react";import{jsx as K}from"react/jsx-runtime";var E=ee(null);function O(e){return K(E.Provider,{value:e.primitives,children:e.children})}function oe(){let e=te(E);if(!e)throw Error("Creation UI primitives require a CreationUiProvider");return e}function s(e,c){let n=(t)=>{let l=e(oe());return K(l,{...t})};return n.displayName=c,n}var Me=s((e)=>e.Button,"Button"),He=s((e)=>e.Badge,"Badge"),qe=s((e)=>e.Label,"Label"),_e=s((e)=>e.Input,"Input"),Ge=s((e)=>e.Textarea,"Textarea"),Je=s((e)=>e.Checkbox,"Checkbox"),We=s((e)=>e.Collapsible,"Collapsible"),Ye=s((e)=>e.CollapsibleTrigger,"CollapsibleTrigger"),Xe=s((e)=>e.CollapsibleContent,"CollapsibleContent"),Ze=s((e)=>e.ScrollArea,"ScrollArea"),Qe=s((e)=>e.ScrollBar,"ScrollBar"),et=s((e)=>e.Select,"Select"),tt=s((e)=>e.SelectTrigger,"SelectTrigger"),ot=s((e)=>e.SelectValue,"SelectValue"),nt=s((e)=>e.SelectContent,"SelectContent"),rt=s((e)=>e.SelectItem,"SelectItem"),it=s((e)=>e.Tabs,"Tabs"),at=s((e)=>e.TabsList,"TabsList"),st=s((e)=>e.TabsTrigger,"TabsTrigger"),lt=s((e)=>e.TabsContent,"TabsContent"),ct=s((e)=>e.Tooltip,"Tooltip"),pt=s((e)=>e.TooltipProvider,"TooltipProvider"),dt=s((e)=>e.TooltipTrigger,"TooltipTrigger"),ut=s((e)=>e.TooltipContent,"TooltipContent"),gt=s((e)=>e.Popover,"Popover"),Ct=s((e)=>e.PopoverTrigger,"PopoverTrigger"),mt=s((e)=>e.PopoverContent,"PopoverContent"),ft=s((e)=>e.Dialog,"Dialog"),bt=s((e)=>e.DialogTrigger,"DialogTrigger"),xt=s((e)=>e.DialogContent,"DialogContent"),Pt=s((e)=>e.DialogHeader,"DialogHeader"),yt=s((e)=>e.DialogFooter,"DialogFooter"),Tt=s((e)=>e.DialogTitle,"DialogTitle"),vt=s((e)=>e.DialogDescription,"DialogDescription"),ht=s((e)=>e.Prompt,"Prompt");var w=/<(\/?)section\b[^>]*>/gi;function P(e,c){let n=new RegExp(`\\b${c}(?:\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+)))?`,"i").exec(e);if(!n)return;return n[1]??n[2]??n[3]??""}function ne(e){if(P(e,"data-slide")!==void 0)return!0;return/\bslide\b/i.test(P(e,"class")??"")}function k(e,c){return e.exec(c)?.[1]??""}function re(e){return e.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'")}function V(e){return re(e.replace(/<[^>]+>/g," ")).replace(/\s+/g," ").trim()}function z(e){let c=[];w.lastIndex=0;let n=0,t=-1;for(let l=w.exec(e);l;l=w.exec(e)){if(l[1]==="/"){if(n===0)continue;if(n-=1,n===0&&t>=0)c.push(e.slice(t,l.index+l[0].length)),t=-1;continue}if(n===0){if(!ne(l[0]))continue;t=l.index}n+=1}return c}function F(e){return Boolean(e)&&z(e).length>0}var ie=/<aside\b([^>]*)>([\s\S]*?)<\/aside>/i;function ae(e){let c="";return{slide:e.replace(ie,(t,l,a)=>{if(!(P(`<aside ${l}>`,"data-notes")!==void 0||/\bnotes\b/i.test(P(`<aside ${l}>`,"class")??"")))return t;return c=V(a),""}),notes:c}}function B(e){let c=z(e);if(c.length===0)return{isDeck:!1,slides:[],head:"",bodyClass:"",bodyStyle:"",title:"deck"};let n=k(/<head\b[^>]*>([\s\S]*?)<\/head>/i,e),t=`<body ${k(/<body\b([^>]*)>/i,e)}>`,l=P(t,"class")??"",a=P(t,"style")??"",r=V(k(/<title\b[^>]*>([\s\S]*?)<\/title>/i,n))||"deck",C=new Set;return{isDeck:!0,slides:c.map((i,p)=>{let g=k(/(<section\b[^>]*>)/i,i),m=P(g,"data-slide")||P(g,"data-slide-id")||String(p+1),d=C.has(m)?`${m}-${p+1}`:m;C.add(d);let x=k(/background(?:-color)?\s*:\s*([^;"']+)/i,P(g,"style")??"").trim()||void 0,{slide:f,notes:u}=ae(i);return{html:`<!DOCTYPE html><html><head>${n}
2
+ <style>
3
+ html, body { margin:0; padding:0; }
4
+ body { display:flex; align-items:center; justify-content:center; min-height:100vh; }
5
+ .slide, [data-slide] { transform-origin: center center !important; }
6
+ </style></head><body class="${l}" style="${a}">${f}</body></html>`,notes:u,id:d,...x?{bg:x}:{}}}),head:n,bodyClass:l,bodyStyle:a,title:r}}function se(e){return`<!DOCTYPE html><html><head>
7
+ <style>
8
+ @page { size: 1280px 720px; margin: 0; }
9
+ html, body { margin:0; padding:0; }
10
+ .page { width: 1280px; height: 720px; overflow: hidden; page-break-after: always; }
11
+ .page:last-child { page-break-after: auto; }
12
+ .page iframe.slide { display:block; width:100%; height:100%; border:0; }
13
+ </style></head><body>
14
+ ${Array.from({length:e},(n,t)=>`<div class="page"><iframe class="slide" data-slide-index="${t}" sandbox="allow-scripts"></iframe></div>`).join(`
15
+ `)}
16
+ <script>window.onload=()=>setTimeout(()=>window.print(),300)</script>
17
+ </body></html>`}function M(e,c=(n,t)=>window.open(n,t)){let n=c("","_blank");if(!n)return!1;n.opener=null,n.document.write(se(e.slides.length)),n.document.close();let t=n.document.querySelectorAll("iframe.slide");return e.slides.forEach((l,a)=>{let r=t[a];if(r)r.srcdoc=l.html}),!0}import{ChevronLeft as le,ChevronRight as ce,FileDown as pe,Maximize2 as de,StickyNote as ue}from"lucide-react";import{useEffect as H,useMemo as ge,useRef as Ce,useState as S}from"react";import{jsx as b,jsxs as y}from"react/jsx-runtime";var me={empty:"No slides found (each slide must be a <section data-slide>).",prev:"Previous slide",next:"Next slide",notes:"Notes",notesTitle:"Speaker notes (N)",notesHeading:"Speaker notes",present:"Present",presentTitle:"Full screen (F)",pdfTitle:"Print to PDF",blocked:"The popup was blocked. Allow popups for this site and try again.",page:"Slide"},fe={empty:"未检测到幻灯片(每页需为 <section data-slide>)。",prev:"上一页",next:"下一页",notes:"备注",notesTitle:"演讲者备注(N)",notesHeading:"演讲者备注",present:"放映",presentTitle:"全屏放映(F)",pdfTitle:"打印为 PDF",blocked:"弹窗被拦截,请允许本站弹出窗口后重试。",page:"第"},be={en:me,zh:fe},D="inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs";function A({content:e,locale:c,active:n}){let t=be[c],l=ge(()=>B(e),[e]),[a,r]=S(0),[C,o]=S(!1),[i,p]=S(""),g=Ce(null),m=l.slides.length,d=Math.min(a,Math.max(0,m-1)),x=l.slides[d];if(H(()=>r(0),[e]),H(()=>{if(!n||m===0)return;let f=(u)=>{let T=u.target;if(T&&(T.tagName==="INPUT"||T.tagName==="TEXTAREA"||T.isContentEditable))return;if(u.key==="ArrowRight"||u.key==="PageDown"||u.key===" ")u.preventDefault(),r((v)=>Math.min(v+1,m-1));else if(u.key==="ArrowLeft"||u.key==="PageUp")u.preventDefault(),r((v)=>Math.max(v-1,0));else if(u.key==="Home")r(0);else if(u.key==="End")r(m-1);else if(u.key.toLowerCase()==="n")o((v)=>!v);else if(u.key.toLowerCase()==="f")if(document.fullscreenElement)document.exitFullscreen();else g.current?.requestFullscreen?.()};return window.addEventListener("keydown",f),()=>window.removeEventListener("keydown",f)},[n,m]),!x)return b("div",{className:"flex h-full items-center justify-center p-6 text-sm text-muted-foreground",children:t.empty});return y("div",{ref:g,className:"flex h-full min-h-72 flex-col bg-muted/30",children:[y("div",{className:"relative min-h-0 flex-1",children:[b("iframe",{title:`slide-${x.id}`,srcDoc:x.html,sandbox:"allow-scripts",className:"h-full w-full border-0 bg-white"},x.id),b("button",{type:"button",onClick:()=>r((f)=>Math.max(f-1,0)),disabled:d===0,className:"absolute left-2 top-1/2 -translate-y-1/2 rounded-full border bg-background/80 p-1.5 shadow backdrop-blur disabled:opacity-30","aria-label":t.prev,children:b(le,{className:"size-4"})}),b("button",{type:"button",onClick:()=>r((f)=>Math.min(f+1,m-1)),disabled:d===m-1,className:"absolute right-2 top-1/2 -translate-y-1/2 rounded-full border bg-background/80 p-1.5 shadow backdrop-blur disabled:opacity-30","aria-label":t.next,children:b(ce,{className:"size-4"})}),y("div",{className:"absolute bottom-2 left-1/2 -translate-x-1/2 rounded-full bg-foreground/80 px-2.5 py-0.5 text-xs tabular-nums text-background",children:[d+1," / ",m]}),C&&x.notes&&y("div",{className:"absolute inset-x-3 bottom-10 max-h-[40%] overflow-y-auto rounded-md border bg-background/95 p-3 text-sm shadow-lg",children:[b("div",{className:"mb-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground",children:t.notesHeading}),x.notes]})]}),i&&b("p",{role:"status",className:"border-t bg-background px-3 py-1 text-xs text-destructive",children:i}),y("div",{className:"flex items-center gap-2 border-t bg-background px-3 py-2",children:[b("div",{className:"flex min-w-0 flex-1 gap-1.5 overflow-x-auto",children:l.slides.map((f,u)=>b("button",{type:"button",onClick:()=>r(u),"aria-label":`${t.page} ${u+1}`,"aria-current":u===d?"true":void 0,...f.bg?{style:{background:f.bg}}:{},className:`flex size-7 shrink-0 items-center justify-center rounded border text-[11px] tabular-nums ${u===d?"border-primary ring-2 ring-primary/30":"text-muted-foreground hover:border-foreground/30"}`,children:u+1},f.id))}),y("button",{type:"button",onClick:()=>o((f)=>!f),className:`${D} ${C?"border-primary bg-primary/10 text-primary":"text-muted-foreground hover:bg-accent"}`,title:t.notesTitle,children:[b(ue,{className:"size-3.5"}),t.notes]}),y("button",{type:"button",onClick:()=>{if(!document.fullscreenElement)g.current?.requestFullscreen?.()},className:`${D} text-muted-foreground hover:bg-accent`,title:t.presentTitle,children:[b(de,{className:"size-3.5"}),t.present]}),y("button",{type:"button",onClick:()=>p(M(l)?"":t.blocked),className:`${D} text-muted-foreground hover:bg-accent`,title:t.pdfTitle,children:[b(pe,{className:"size-3.5"}),"PDF"]})]})]})}var Et={id:"presentation-deck",version:"1.0.0",kitApi:h,views:[{id:"slides",label:{zh:"幻灯片",en:"Slides"},match:(e)=>e.artifactKind==="presentation"&&e.format==="html"&&F(e.content),View:A}]};class R extends Error{code;status;constructor(e,c,n){super(c);this.code=e;this.status=n;this.name="CreationApiError"}}function xe(e){return`/projects/${encodeURIComponent(e)}`}function Ot(e){let c=e.base.replace(/\/$/,""),n=e.fetch??((a,r)=>globalThis.fetch(a,r));async function t(a,r={}){let C=r.method??(r.body===void 0?"GET":"POST"),o=await n(`${c}${a}`,{method:C,headers:{accept:"application/json",...r.body===void 0?{}:{"content-type":"application/json"}},...r.body===void 0?{}:{body:JSON.stringify(r.body)},...r.signal?{signal:r.signal}:{}}),i=await o.text(),p=null;if(i)try{p=JSON.parse(i)}catch{p=null}if(!o.ok){let g=p?.error;throw new R(typeof g?.code==="string"?g.code:"request-failed",typeof g?.message==="string"?g.message:`Request failed (${o.status})`,o.status)}if(p===null)throw new R("invalid-response","Empty or non-JSON response",o.status);return p}let l=xe;return{request:t,getConfiguration:(a)=>t(e.configurationPath??"",{method:"GET",...a?{signal:a}:{}}),listProjects:(a)=>t("/projects",{method:"GET",...a?{signal:a}:{}}),createProject:(a)=>t("/projects",{body:a}),getProject:(a,r)=>t(l(a),{method:"GET",...r?{signal:r}:{}}),addSource:(a,r)=>t(`${l(a)}/sources`,{body:r}),appendArtifact:(a,r)=>t(`${l(a)}/artifacts`,{body:r}),saveRevision:(a,r,C)=>t(`${l(a)}/artifacts/${encodeURIComponent(r)}/revisions`,{body:C}),startRun:(a,r)=>t(`${l(a)}/runs`,{body:r}),cancelRun:(a,r)=>t(`${l(a)}/runs/${encodeURIComponent(r)}/cancel`,{body:{}}),resumeRun:(a,r)=>t(`${l(a)}/runs/${encodeURIComponent(r)}/resume`,{body:{}})}}import{jsx as q}from"react/jsx-runtime";var ye={Button:"button",Input:"input",Label:"label",Textarea:"textarea",DialogTitle:"h2",DialogDescription:"p"},Te=new Set(["asChild","viewportProps","showCloseButton","sideOffset","align","side"]);function ve(){let e=new Map;return new Proxy({},{get(c,n){let t=e.get(n);if(!t){let l=ye[n]??"div";t=(a)=>{let r={};for(let[C,o]of Object.entries(a))if(!Te.has(C))r[C]=o;return Pe(l,r)},t.displayName=n,e.set(n,t)}return t}})}function Qt(e){return q(O,{primitives:ve(),children:q(L,{locale:e.locale??"en",children:e.children})})}export{Qt as PlainKitProviders,ve as createPlainUi};
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from "react";
2
+ /** Host-owned HTTP seam: kind packages never hardcode URLs or fetch directly. */
3
+ export interface CreationHostTransport {
4
+ request<T>(base: string, path: string, init?: {
5
+ method?: string;
6
+ body?: unknown;
7
+ signal?: AbortSignal;
8
+ }): Promise<T>;
9
+ url(base: string, path: string): string;
10
+ }
11
+ export declare function CreationTransportProvider(props: {
12
+ transport: CreationHostTransport;
13
+ children: ReactNode;
14
+ }): import("react").JSX.Element;
15
+ export declare function useCreationTransport(): CreationHostTransport;
package/dist/ui.d.ts ADDED
@@ -0,0 +1,301 @@
1
+ import { type ComponentProps, type ComponentType, type ReactNode } from "react";
2
+ import type { PromptInputProps } from "@uibot/ai-chat";
3
+ /**
4
+ * UI primitives are owned by the host (Studio's shadcn components) and injected, so kind
5
+ * packages never import radix and Studio keeps a single copy of every primitive. Each prop
6
+ * type lists only what kinds use; the host assigns its own component to the slot, so the
7
+ * compiler checks the host satisfies this contract.
8
+ */
9
+ type Dom<T extends keyof React.JSX.IntrinsicElements> = Omit<ComponentProps<T>, "dir"> & {
10
+ dir?: "ltr" | "rtl";
11
+ };
12
+ export type ButtonVariant = "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
13
+ export type ButtonSize = "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg";
14
+ export interface UiButtonProps extends Dom<"button"> {
15
+ variant?: ButtonVariant;
16
+ size?: ButtonSize;
17
+ asChild?: boolean;
18
+ }
19
+ export interface UiBadgeProps extends Dom<"div"> {
20
+ variant?: "default" | "secondary" | "destructive" | "outline";
21
+ }
22
+ type CheckedState = boolean | "indeterminate";
23
+ export interface UiCheckboxProps extends Omit<Dom<"button">, "defaultValue" | "defaultChecked"> {
24
+ checked?: CheckedState;
25
+ defaultChecked?: CheckedState;
26
+ onCheckedChange?(checked: CheckedState): void;
27
+ required?: boolean;
28
+ name?: string;
29
+ value?: string;
30
+ }
31
+ export interface UiCollapsibleProps extends Dom<"div"> {
32
+ open?: boolean;
33
+ defaultOpen?: boolean;
34
+ onOpenChange?(open: boolean): void;
35
+ disabled?: boolean;
36
+ }
37
+ export interface UiTriggerProps extends Dom<"button"> {
38
+ asChild?: boolean;
39
+ }
40
+ export interface UiScrollAreaProps extends Dom<"div"> {
41
+ viewportProps?: Dom<"div"> & Record<`data-${string}`, string | undefined>;
42
+ }
43
+ export interface UiScrollBarProps extends Dom<"div"> {
44
+ orientation?: "vertical" | "horizontal";
45
+ }
46
+ export interface UiSelectProps {
47
+ value?: string;
48
+ defaultValue?: string;
49
+ onValueChange?(value: string): void;
50
+ open?: boolean;
51
+ onOpenChange?(open: boolean): void;
52
+ disabled?: boolean;
53
+ required?: boolean;
54
+ name?: string;
55
+ children?: ReactNode;
56
+ }
57
+ export interface UiSelectTriggerProps extends Dom<"button"> {
58
+ size?: "sm" | "default";
59
+ }
60
+ export interface UiSelectValueProps extends Dom<"span"> {
61
+ placeholder?: ReactNode;
62
+ }
63
+ export interface UiSelectContentProps extends Dom<"div"> {
64
+ position?: "item-aligned" | "popper";
65
+ align?: "start" | "center" | "end";
66
+ sideOffset?: number;
67
+ }
68
+ export interface UiSelectItemProps extends Dom<"div"> {
69
+ value: string;
70
+ disabled?: boolean;
71
+ textValue?: string;
72
+ }
73
+ export interface UiTabsProps extends Omit<Dom<"div">, "defaultValue"> {
74
+ value?: string;
75
+ defaultValue?: string;
76
+ onValueChange?(value: string): void;
77
+ orientation?: "horizontal" | "vertical";
78
+ }
79
+ export interface UiTabsListProps extends Dom<"div"> {
80
+ variant?: "default" | "line";
81
+ }
82
+ export interface UiTabsTriggerProps extends Dom<"button"> {
83
+ value: string;
84
+ }
85
+ export interface UiTabsContentProps extends Dom<"div"> {
86
+ value: string;
87
+ forceMount?: true;
88
+ }
89
+ export interface UiOverlayRootProps {
90
+ open?: boolean;
91
+ defaultOpen?: boolean;
92
+ onOpenChange?(open: boolean): void;
93
+ children?: ReactNode;
94
+ }
95
+ export interface UiTooltipProviderProps {
96
+ delayDuration?: number;
97
+ children: ReactNode;
98
+ }
99
+ export interface UiTooltipRootProps extends UiOverlayRootProps {
100
+ delayDuration?: number;
101
+ }
102
+ export interface UiFloatingContentProps extends Dom<"div"> {
103
+ side?: "top" | "right" | "bottom" | "left";
104
+ align?: "start" | "center" | "end";
105
+ sideOffset?: number;
106
+ }
107
+ export interface UiDialogContentProps extends Omit<Dom<"div">, "title"> {
108
+ showCloseButton?: boolean;
109
+ onCloseAutoFocus?(event: Event): void;
110
+ onOpenAutoFocus?(event: Event): void;
111
+ onEscapeKeyDown?(event: KeyboardEvent): void;
112
+ }
113
+ export interface CreationUiPrimitives {
114
+ Button: ComponentType<UiButtonProps>;
115
+ Badge: ComponentType<UiBadgeProps>;
116
+ Label: ComponentType<Dom<"label">>;
117
+ Input: ComponentType<Dom<"input">>;
118
+ Textarea: ComponentType<Dom<"textarea">>;
119
+ Checkbox: ComponentType<UiCheckboxProps>;
120
+ Collapsible: ComponentType<UiCollapsibleProps>;
121
+ CollapsibleTrigger: ComponentType<UiTriggerProps>;
122
+ CollapsibleContent: ComponentType<Dom<"div"> & {
123
+ forceMount?: true;
124
+ }>;
125
+ ScrollArea: ComponentType<UiScrollAreaProps>;
126
+ ScrollBar: ComponentType<UiScrollBarProps>;
127
+ Select: ComponentType<UiSelectProps>;
128
+ SelectTrigger: ComponentType<UiSelectTriggerProps>;
129
+ SelectValue: ComponentType<UiSelectValueProps>;
130
+ SelectContent: ComponentType<UiSelectContentProps>;
131
+ SelectItem: ComponentType<UiSelectItemProps>;
132
+ Tabs: ComponentType<UiTabsProps>;
133
+ TabsList: ComponentType<UiTabsListProps>;
134
+ TabsTrigger: ComponentType<UiTabsTriggerProps>;
135
+ TabsContent: ComponentType<UiTabsContentProps>;
136
+ Tooltip: ComponentType<UiTooltipRootProps>;
137
+ TooltipProvider: ComponentType<UiTooltipProviderProps>;
138
+ TooltipTrigger: ComponentType<UiTriggerProps>;
139
+ TooltipContent: ComponentType<UiFloatingContentProps>;
140
+ Popover: ComponentType<UiOverlayRootProps>;
141
+ PopoverTrigger: ComponentType<UiTriggerProps>;
142
+ PopoverContent: ComponentType<UiFloatingContentProps>;
143
+ Dialog: ComponentType<UiOverlayRootProps>;
144
+ DialogTrigger: ComponentType<UiTriggerProps>;
145
+ DialogContent: ComponentType<UiDialogContentProps>;
146
+ DialogHeader: ComponentType<Dom<"div">>;
147
+ DialogFooter: ComponentType<Dom<"div">>;
148
+ DialogTitle: ComponentType<Dom<"h2">>;
149
+ DialogDescription: ComponentType<Dom<"p">>;
150
+ /** The host's chat prompt (ai-chat `PromptInput` with Studio styling). */
151
+ Prompt: ComponentType<PromptInputProps>;
152
+ }
153
+ export declare function CreationUiProvider(props: {
154
+ primitives: CreationUiPrimitives;
155
+ children: ReactNode;
156
+ }): import("react").JSX.Element;
157
+ export declare const Button: {
158
+ (props: UiButtonProps): import("react").JSX.Element;
159
+ displayName: string;
160
+ };
161
+ export declare const Badge: {
162
+ (props: UiBadgeProps): import("react").JSX.Element;
163
+ displayName: string;
164
+ };
165
+ export declare const Label: {
166
+ (props: Dom<"label">): import("react").JSX.Element;
167
+ displayName: string;
168
+ };
169
+ export declare const Input: {
170
+ (props: Dom<"input">): import("react").JSX.Element;
171
+ displayName: string;
172
+ };
173
+ export declare const Textarea: {
174
+ (props: Dom<"textarea">): import("react").JSX.Element;
175
+ displayName: string;
176
+ };
177
+ export declare const Checkbox: {
178
+ (props: UiCheckboxProps): import("react").JSX.Element;
179
+ displayName: string;
180
+ };
181
+ export declare const Collapsible: {
182
+ (props: UiCollapsibleProps): import("react").JSX.Element;
183
+ displayName: string;
184
+ };
185
+ export declare const CollapsibleTrigger: {
186
+ (props: UiTriggerProps): import("react").JSX.Element;
187
+ displayName: string;
188
+ };
189
+ export declare const CollapsibleContent: {
190
+ (props: Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "dir"> & {
191
+ dir?: "ltr" | "rtl";
192
+ } & {
193
+ forceMount?: true;
194
+ }): import("react").JSX.Element;
195
+ displayName: string;
196
+ };
197
+ export declare const ScrollArea: {
198
+ (props: UiScrollAreaProps): import("react").JSX.Element;
199
+ displayName: string;
200
+ };
201
+ export declare const ScrollBar: {
202
+ (props: UiScrollBarProps): import("react").JSX.Element;
203
+ displayName: string;
204
+ };
205
+ export declare const Select: {
206
+ (props: UiSelectProps): import("react").JSX.Element;
207
+ displayName: string;
208
+ };
209
+ export declare const SelectTrigger: {
210
+ (props: UiSelectTriggerProps): import("react").JSX.Element;
211
+ displayName: string;
212
+ };
213
+ export declare const SelectValue: {
214
+ (props: UiSelectValueProps): import("react").JSX.Element;
215
+ displayName: string;
216
+ };
217
+ export declare const SelectContent: {
218
+ (props: UiSelectContentProps): import("react").JSX.Element;
219
+ displayName: string;
220
+ };
221
+ export declare const SelectItem: {
222
+ (props: UiSelectItemProps): import("react").JSX.Element;
223
+ displayName: string;
224
+ };
225
+ export declare const Tabs: {
226
+ (props: UiTabsProps): import("react").JSX.Element;
227
+ displayName: string;
228
+ };
229
+ export declare const TabsList: {
230
+ (props: UiTabsListProps): import("react").JSX.Element;
231
+ displayName: string;
232
+ };
233
+ export declare const TabsTrigger: {
234
+ (props: UiTabsTriggerProps): import("react").JSX.Element;
235
+ displayName: string;
236
+ };
237
+ export declare const TabsContent: {
238
+ (props: UiTabsContentProps): import("react").JSX.Element;
239
+ displayName: string;
240
+ };
241
+ export declare const Tooltip: {
242
+ (props: UiTooltipRootProps): import("react").JSX.Element;
243
+ displayName: string;
244
+ };
245
+ export declare const TooltipProvider: {
246
+ (props: UiTooltipProviderProps): import("react").JSX.Element;
247
+ displayName: string;
248
+ };
249
+ export declare const TooltipTrigger: {
250
+ (props: UiTriggerProps): import("react").JSX.Element;
251
+ displayName: string;
252
+ };
253
+ export declare const TooltipContent: {
254
+ (props: UiFloatingContentProps): import("react").JSX.Element;
255
+ displayName: string;
256
+ };
257
+ export declare const Popover: {
258
+ (props: UiOverlayRootProps): import("react").JSX.Element;
259
+ displayName: string;
260
+ };
261
+ export declare const PopoverTrigger: {
262
+ (props: UiTriggerProps): import("react").JSX.Element;
263
+ displayName: string;
264
+ };
265
+ export declare const PopoverContent: {
266
+ (props: UiFloatingContentProps): import("react").JSX.Element;
267
+ displayName: string;
268
+ };
269
+ export declare const Dialog: {
270
+ (props: UiOverlayRootProps): import("react").JSX.Element;
271
+ displayName: string;
272
+ };
273
+ export declare const DialogTrigger: {
274
+ (props: UiTriggerProps): import("react").JSX.Element;
275
+ displayName: string;
276
+ };
277
+ export declare const DialogContent: {
278
+ (props: UiDialogContentProps): import("react").JSX.Element;
279
+ displayName: string;
280
+ };
281
+ export declare const DialogHeader: {
282
+ (props: Dom<"div">): import("react").JSX.Element;
283
+ displayName: string;
284
+ };
285
+ export declare const DialogFooter: {
286
+ (props: Dom<"div">): import("react").JSX.Element;
287
+ displayName: string;
288
+ };
289
+ export declare const DialogTitle: {
290
+ (props: Dom<"h2">): import("react").JSX.Element;
291
+ displayName: string;
292
+ };
293
+ export declare const DialogDescription: {
294
+ (props: Dom<"p">): import("react").JSX.Element;
295
+ displayName: string;
296
+ };
297
+ export declare const Prompt: {
298
+ (props: PromptInputProps): import("react").JSX.Element;
299
+ displayName: string;
300
+ };
301
+ export {};
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
1
  {
2
2
  "name": "@uibot/creation-kit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/Foundj/uibot.git",
8
+ "directory": "packages/creation-kit"
9
+ },
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ },
19
+ "./testing": {
20
+ "types": "./dist/testing.d.ts",
21
+ "default": "./dist/testing.js"
22
+ },
23
+ "./client": {
24
+ "types": "./dist/client.d.ts",
25
+ "default": "./dist/client.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "!dist/**/*.map",
31
+ "!dist/**/*.test.d.ts",
32
+ "!dist/**/*.spec.d.ts"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "peerDependencies": {
38
+ "react": "^19",
39
+ "react-dom": "^19"
40
+ },
41
+ "dependencies": {
42
+ "@uibot/contracts": "^0.2.0",
43
+ "@uibot/ai-chat": "^0.2.0",
44
+ "lucide-react": "^1.56.0",
45
+ "clsx": "^2.1.1",
46
+ "tailwind-merge": "^3.7.0"
47
+ },
48
+ "scripts": {
49
+ "build": "rm -rf dist && bun build --minify ./src/index.ts ./src/testing.tsx ./src/client.ts --outdir ./dist --format esm --target browser --packages external && bun x tsc -p tsconfig.json",
50
+ "typecheck": "bun x tsc -p tsconfig.json --noEmit",
51
+ "test": "bun test"
52
+ }
53
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
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.