Extract smartblock into a standalone wasm plugin repo

Convert the bundled backend/internal/plugins/smartblock package into a
standalone reactor-mode wasm plugin:

- package smartblock -> package main; add wasip1 main.go with
  wasmguest.Serve(Registration).
- Add go.mod (module git.dev.alexdunmow.com/block/smartblock) pinning
  block/core v0.18.2; no replace directives.
- Flesh out plugin.mod (scope=ninja, kind=plugin, categories, tags,
  display_name, description).
- Point web/eslint.config.js at ../../../cms/web/eslint.config.js and
  switch @block-ninja/ui from workspace:* to the ^0.1.0 registry version;
  rebuild web/dist.
- Add .gitignore (*.bnp, *.wasm, web/node_modules).

Behavior unchanged: registers the "smart" block; assets served from
web/dist. Builds to smartblock-1.0.0.bnp; check-safety passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Alex Dunmow 2026-07-05 02:48:09 +08:00
commit 38d5fdf4b1
38 changed files with 188181 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.bnp
*.wasm
/web/node_modules/

80
block.go Normal file
View File

@ -0,0 +1,80 @@
package main
import (
"context"
"fmt"
"html"
"strings"
)
// SmartBlockFunc renders a Smart Block by substituting {{field}} placeholders with content values.
func SmartBlockFunc(ctx context.Context, content map[string]any) string {
// Get metadata
meta, ok := content["_meta"].(map[string]any)
if !ok {
return renderEmptyState()
}
generations, ok := meta["generations"].([]any)
if !ok || len(generations) == 0 {
return renderEmptyState()
}
currentID, _ := meta["currentGenerationId"].(string)
if currentID == "" {
return renderEmptyState()
}
// Find the current generation
var template string
for _, gen := range generations {
genMap, ok := gen.(map[string]any)
if !ok {
continue
}
if genMap["id"] == currentID {
template, _ = genMap["template"].(string)
break
}
}
if template == "" {
return renderEmptyState()
}
// Replace {{field}} placeholders with content values
result := template
for key, value := range content {
// Skip meta fields
if strings.HasPrefix(key, "_") {
continue
}
placeholder := "{{" + key + "}}"
var valueStr string
switch v := value.(type) {
case string:
valueStr = html.EscapeString(v)
case float64:
valueStr = fmt.Sprintf("%v", v)
case int:
valueStr = fmt.Sprintf("%d", v)
case bool:
valueStr = fmt.Sprintf("%t", v)
default:
valueStr = fmt.Sprintf("%v", v)
}
result = strings.ReplaceAll(result, placeholder, valueStr)
}
return result
}
func renderEmptyState() string {
return `<div class="smart-block-empty p-8 border-2 border-dashed border-gray-300 rounded-lg text-center text-gray-500">
<p class="text-lg font-medium">Smart Block</p>
<p class="text-sm">Use the editor to generate content with AI</p>
</div>`
}

17
go.mod Normal file
View File

@ -0,0 +1,17 @@
module git.dev.alexdunmow.com/block/smartblock
go 1.26.4
require git.dev.alexdunmow.com/block/core v0.18.2
require (
connectrpc.com/connect v1.20.0 // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.9.2 // indirect
golang.org/x/mod v0.34.0 // indirect
golang.org/x/text v0.36.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
)

44
go.sum Normal file
View File

@ -0,0 +1,44 @@
connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ=
connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4=
git.dev.alexdunmow.com/block/core v0.18.2 h1:+3OfZ424yoc1k1CucXYARk8TBkh8Z693HRkhf5Ci2wU=
git.dev.alexdunmow.com/block/core v0.18.2/go.mod h1:GGuUu826AoJepC/hKLGJ7BX3PQaDss9ueCT0se6Ao2w=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU=
github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

13
main.go Normal file
View File

@ -0,0 +1,13 @@
//go:build wasip1
// Package main compiles the Smart Block plugin to a wasip1 reactor-mode wasm
// guest. `ninja plugin build` produces the .bnp artifact the registry serves;
// the host runs `_initialize` once per pooled instance, invoking this init
// (hence wasmguest.Serve) before any ABI hook call. main is never called.
package main
import "git.dev.alexdunmow.com/block/core/plugin/wasmguest"
func init() { wasmguest.Serve(Registration) }
func main() {} // never called — reactor mode

9
plugin.mod Normal file
View File

@ -0,0 +1,9 @@
[plugin]
name = "smartblock"
display_name = "Smart Block"
scope = "ninja"
version = "1.0.0"
description = "AI-powered block that generates custom content structures from natural-language prompts and renders them by substituting {{field}} placeholders."
kind = "plugin"
categories = ["content"]
tags = ["ai", "content", "blocks", "generative"]

46
register.go Normal file
View File

@ -0,0 +1,46 @@
package main
import (
"embed"
"io/fs"
"net/http"
"git.dev.alexdunmow.com/block/core/blocks"
"git.dev.alexdunmow.com/block/core/templates"
)
//go:embed all:web/dist/assets
var distAssets embed.FS
//go:embed plugin.mod
var pluginModBytes []byte
// Register is the plugin entry point that registers the Smart Block.
// Returns an error if registration fails.
func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error {
// Register the Smart Block
br.Register(SmartBlockMeta, SmartBlockFunc)
return nil
}
// SmartBlockMeta defines the Smart Block metadata
var SmartBlockMeta = blocks.BlockMeta{
Key: "smart",
Title: "Smart Block",
Description: "AI-powered block that generates custom content structures from natural language prompts",
Source: "smartblock",
EditorJS: "remoteEntry.js",
}
// AssetsHandler serves static assets including the Module Federation remote entry.
// This is called by the plugin loader to mount assets at /api/plugins/smartblock/
func AssetsHandler() http.Handler {
// Create a sub-filesystem pointing to web/dist/assets
subFS, err := fs.Sub(distAssets, "web/dist/assets")
if err != nil {
// Fallback to 404 handler if embed fails
return http.NotFoundHandler()
}
return http.FileServer(http.FS(subFS))
}

19
registration.go Normal file
View File

@ -0,0 +1,19 @@
package main
import (
"net/http"
"git.dev.alexdunmow.com/block/core/blocks"
"git.dev.alexdunmow.com/block/core/templates"
"git.dev.alexdunmow.com/block/core/plugin"
)
// Registration is the compile-time plugin registration for the Smart Block.
var Registration = plugin.PluginRegistration{
Name: "smartblock",
Version: plugin.ParseModVersion(pluginModBytes),
Register: func(tr templates.TemplateRegistry, br blocks.BlockRegistry) error {
return Register(tr, br)
},
Assets: func() http.Handler { return AssetsHandler() },
}

View File

@ -0,0 +1,443 @@
import { importShared } from "./__federation_fn_import-hlt2XzeI.js";
import { j as jsxRuntimeExports } from "./jsx-runtime-CvJTHeKY.js";
const { useState, useCallback, useMemo } = await importShared("react");
const {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
ScrollArea,
Button,
Input,
Textarea,
Label,
Badge,
Monaco,
Loader2,
Sparkles,
Check,
Code,
History,
Wand2,
Send,
Edit3,
useAIGenerate,
useAIAssist,
} = await importShared("@block-ninja/ui");
function SmartBlockEditor({ content, onChange }) {
const [activeTab, setActiveTab] = useState("generate");
const [prompt, setPrompt] = useState("");
const [assistPrompt, setAssistPrompt] = useState("");
const [lastExplanation, setLastExplanation] = useState("");
const { generate, isLoading: isGenerating } = useAIGenerate();
const { assist, isLoading: isAssisting } = useAIAssist();
const meta = useMemo(() => {
return content._meta ?? { currentGenerationId: "", generations: [] };
}, [content._meta]);
const currentGeneration = useMemo(() => meta.generations?.find((g) => g.id === meta.currentGenerationId), [meta]);
const currentTemplate = currentGeneration?.template || "";
const contentFields = useMemo(() => {
const fields = {};
for (const [key, value] of Object.entries(content)) {
if (!key.startsWith("_")) {
fields[key] = value;
}
}
return fields;
}, [content]);
const handleGenerate = useCallback(async () => {
if (!prompt.trim()) return;
try {
const result = await generate(prompt, currentTemplate);
const newGeneration = {
id: result.generationId,
prompt,
schema: result.schema,
template: result.template,
createdAt: /* @__PURE__ */ new Date().toISOString(),
};
const updatedMeta = {
currentGenerationId: result.generationId,
generations: [...(meta.generations || []), newGeneration],
};
const newContent = { _meta: updatedMeta };
if (result.schema?.properties) {
for (const key of Object.keys(result.schema.properties)) {
newContent[key] = "";
}
}
onChange(newContent);
setLastExplanation(result.explanation);
setPrompt("");
setActiveTab("content");
} catch (error) {
console.error("Generation failed:", error);
}
}, [prompt, currentTemplate, meta, generate, onChange]);
const handleAssist = useCallback(async () => {
if (!assistPrompt.trim() || !currentTemplate) return;
try {
const result = await assist(assistPrompt, currentTemplate, currentGeneration?.schema ? JSON.stringify(currentGeneration.schema) : void 0);
const updatedGenerations = (meta.generations || []).map((g) => (g.id === meta.currentGenerationId ? { ...g, template: result.template } : g));
onChange({
...content,
_meta: { ...meta, generations: updatedGenerations },
});
setLastExplanation(result.explanation);
setAssistPrompt("");
} catch (error) {
console.error("Assist failed:", error);
}
}, [assistPrompt, currentTemplate, currentGeneration, meta, content, assist, onChange]);
const handleTemplateChange = useCallback(
(newTemplate) => {
if (!newTemplate || !currentGeneration) return;
const updatedGenerations = (meta.generations || []).map((g) => (g.id === meta.currentGenerationId ? { ...g, template: newTemplate } : g));
onChange({
...content,
_meta: { ...meta, generations: updatedGenerations },
});
},
[currentGeneration, meta, content, onChange],
);
const handleSwitchGeneration = useCallback(
(generationId) => {
const generation = meta.generations?.find((g) => g.id === generationId);
if (!generation) return;
const newContent = {
_meta: { ...meta, currentGenerationId: generationId },
};
if (generation.schema?.properties) {
for (const key of Object.keys(generation.schema.properties)) {
newContent[key] = content[key] ?? "";
}
}
onChange(newContent);
setActiveTab("content");
},
[meta, content, onChange],
);
const handleFieldChange = useCallback(
(fieldName, value) => {
onChange({ ...content, [fieldName]: value });
},
[content, onChange],
);
const renderContentFields = () => {
const schema = currentGeneration?.schema;
if (!schema?.properties) {
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", {
className: "text-center py-8 text-muted-foreground",
children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Generate content structure first" }),
});
}
const properties = Object.entries(schema.properties);
const requiredFields = new Set(schema.required || []);
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, {
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, {
className: "pb-2",
children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { className: "text-sm", children: "Content Fields" }),
}),
/* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, {
className: "space-y-4",
children: properties.map(([name, prop]) => {
const propObj = prop;
const isRequired = requiredFields.has(name);
const editor = propObj["x-editor"] || "text";
let inputType = "text";
if (editor === "number") {
inputType = "number";
} else if (editor === "url") {
inputType = "url";
}
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
"div",
{
className: "space-y-2",
children: [
/* @__PURE__ */ jsxRuntimeExports.jsxs(Label, {
htmlFor: name,
children: [
propObj.title || propObj.description || name,
isRequired && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive ml-1", children: "*" }),
],
}),
editor === "textarea" ?
/* @__PURE__ */ jsxRuntimeExports.jsx(Textarea, {
id: name,
value: contentFields[name] || "",
onChange: (e) => handleFieldChange(name, e.target.value),
placeholder: propObj.description,
rows: 4,
})
: /* @__PURE__ */ jsxRuntimeExports.jsx(Input, {
id: name,
type: inputType,
value: contentFields[name] || "",
onChange: (e) => handleFieldChange(name, e.target.value),
placeholder: propObj.description,
}),
],
},
name,
);
}),
}),
],
});
};
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", {
className: "space-y-4",
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, {
value: activeTab,
onValueChange: setActiveTab,
children: [
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, {
className: "grid w-full grid-cols-4",
children: [
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsTrigger, {
value: "generate",
className: "gap-2",
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Wand2, { className: "h-4 w-4" }), "Generate"],
}),
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsTrigger, {
value: "content",
className: "gap-2",
disabled: !currentGeneration,
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Edit3, { className: "h-4 w-4" }), "Content"],
}),
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsTrigger, {
value: "html",
className: "gap-2",
disabled: !currentGeneration,
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Code, { className: "h-4 w-4" }), "HTML"],
}),
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsTrigger, {
value: "history",
className: "gap-2",
disabled: (meta.generations?.length || 0) === 0,
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(History, { className: "h-4 w-4" }), "History"],
}),
],
}),
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsContent, {
value: "generate",
className: "space-y-4",
children: [
/* @__PURE__ */ jsxRuntimeExports.jsxs(Card, {
children: [
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, {
children: [
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, {
className: "flex items-center gap-2",
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { className: "h-5 w-5 text-primary" }), "Generate Content Structure"],
}),
/* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, {
children: "Describe the content you want to create. AI will generate both the form fields and HTML template.",
}),
],
}),
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, {
className: "space-y-4",
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx(Textarea, {
placeholder: "E.g., Create a testimonial card with author photo, quote, name, title, and 5-star rating...",
value: prompt,
onChange: (e) => setPrompt(e.target.value),
className: "min-h-[100px]",
}),
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
className: "flex justify-between items-center",
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx("p", {
className: "text-sm text-muted-foreground",
children: currentGeneration ? "Generating will create a new version" : "Start by describing your content",
}),
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, {
action: "create",
entity: "smart-block-generation",
onClick: handleGenerate,
disabled: isGenerating || !prompt.trim(),
children:
isGenerating ?
/* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }), "Generating..."],
})
: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Wand2, { className: "h-4 w-4 mr-2" }), "Generate"],
}),
}),
],
}),
],
}),
],
}),
lastExplanation &&
/* @__PURE__ */ jsxRuntimeExports.jsxs(Card, {
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, {
className: "pb-2",
children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { className: "text-sm", children: "AI Response" }),
}),
/* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, {
children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: lastExplanation }),
}),
],
}),
!currentGeneration &&
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
className: "text-center py-8 text-muted-foreground",
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx(Wand2, { className: "h-12 w-12 mx-auto mb-4 opacity-50" }),
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "No content structure generated yet" }),
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm", children: "Enter a prompt above to get started" }),
],
}),
],
}),
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "content", children: renderContentFields() }),
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsContent, {
value: "html",
className: "space-y-4",
children: [
/* @__PURE__ */ jsxRuntimeExports.jsxs(Card, {
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, {
className: "pb-2",
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, {
className: "text-sm flex items-center justify-between",
children: [
"HTML Template",
/* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, {
variant: "outline",
children: [Object.keys(currentGeneration?.schema?.properties || {}).length, " fields"],
}),
],
}),
}),
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, {
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx(Monaco, { value: currentTemplate, onChange: handleTemplateChange, language: "html", height: "300px", theme: "vs-dark" }),
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", {
className: "text-xs text-muted-foreground mt-2",
children: ["Use ", "{{fieldName}}", " placeholders to insert content values"],
}),
],
}),
],
}),
/* @__PURE__ */ jsxRuntimeExports.jsxs(Card, {
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, {
className: "pb-2",
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, {
className: "text-sm flex items-center gap-2",
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { className: "h-4 w-4" }), "AI Assistant"],
}),
}),
/* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, {
className: "space-y-2",
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
className: "flex gap-2",
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx(Input, {
placeholder: "E.g., Make the quote italic and add a shadow...",
value: assistPrompt,
onChange: (e) => setAssistPrompt(e.target.value),
onKeyDown: (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleAssist();
}
},
}),
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, {
action: "edit",
entity: "smart-block-template",
onClick: handleAssist,
disabled: isAssisting || !assistPrompt.trim(),
size: "icon",
children:
isAssisting ?
/* @__PURE__ */ jsxRuntimeExports.jsx(Loader2, { className: "h-4 w-4 animate-spin" })
: /* @__PURE__ */ jsxRuntimeExports.jsx(Send, { className: "h-4 w-4" }),
}),
],
}),
}),
],
}),
],
}),
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, {
value: "history",
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, {
children: [
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, {
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { className: "text-sm", children: "Generation History" }),
/* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: "Switch between previous generations or regenerate from a prompt" }),
],
}),
/* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, {
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, {
className: "h-[300px]",
children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", {
className: "space-y-2",
children: [...(meta.generations || [])].reverse().map((gen) =>
/* @__PURE__ */ jsxRuntimeExports.jsx(
Button,
{
type: "button",
variant: "ghost",
action: "select",
entity: "smart-block-generation",
className: `w-full h-auto justify-start p-3 rounded-lg border text-left font-normal whitespace-normal transition-colors hover:text-foreground [&_svg]:size-3 ${gen.id === meta.currentGenerationId ? "bg-primary/10 border-primary hover:bg-primary/10" : "hover:bg-muted"}`,
onClick: () => handleSwitchGeneration(gen.id),
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
className: "w-full flex items-start justify-between gap-2",
children: [
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
className: "flex-1 min-w-0",
children: [
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium truncate", children: gen.prompt }),
/* @__PURE__ */ jsxRuntimeExports.jsx("p", {
className: "text-xs text-muted-foreground",
children: new Date(gen.createdAt).toLocaleString(),
}),
],
}),
gen.id === meta.currentGenerationId &&
/* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, {
variant: "default",
className: "shrink-0",
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "h-3 w-3 mr-1" }), "Active"],
}),
],
}),
},
gen.id,
),
),
}),
}),
}),
],
}),
}),
],
}),
});
}
export { SmartBlockEditor as default };

View File

@ -0,0 +1,392 @@
const buildIdentifier = "[0-9A-Za-z-]+";
const build = `(?:\\+(${buildIdentifier}(?:\\.${buildIdentifier})*))`;
const numericIdentifier = "0|[1-9]\\d*";
const numericIdentifierLoose = "[0-9]+";
const nonNumericIdentifier = "\\d*[a-zA-Z-][a-zA-Z0-9-]*";
const preReleaseIdentifierLoose = `(?:${numericIdentifierLoose}|${nonNumericIdentifier})`;
const preReleaseLoose = `(?:-?(${preReleaseIdentifierLoose}(?:\\.${preReleaseIdentifierLoose})*))`;
const preReleaseIdentifier = `(?:${numericIdentifier}|${nonNumericIdentifier})`;
const preRelease = `(?:-(${preReleaseIdentifier}(?:\\.${preReleaseIdentifier})*))`;
const xRangeIdentifier = `${numericIdentifier}|x|X|\\*`;
const xRangePlain = `[v=\\s]*(${xRangeIdentifier})(?:\\.(${xRangeIdentifier})(?:\\.(${xRangeIdentifier})(?:${preRelease})?${build}?)?)?`;
const hyphenRange = `^\\s*(${xRangePlain})\\s+-\\s+(${xRangePlain})\\s*$`;
const mainVersionLoose = `(${numericIdentifierLoose})\\.(${numericIdentifierLoose})\\.(${numericIdentifierLoose})`;
const loosePlain = `[v=\\s]*${mainVersionLoose}${preReleaseLoose}?${build}?`;
const gtlt = "((?:<|>)?=?)";
const comparatorTrim = `(\\s*)${gtlt}\\s*(${loosePlain}|${xRangePlain})`;
const loneTilde = "(?:~>?)";
const tildeTrim = `(\\s*)${loneTilde}\\s+`;
const loneCaret = "(?:\\^)";
const caretTrim = `(\\s*)${loneCaret}\\s+`;
const star = "(<|>)?=?\\s*\\*";
const caret = `^${loneCaret}${xRangePlain}$`;
const mainVersion = `(${numericIdentifier})\\.(${numericIdentifier})\\.(${numericIdentifier})`;
const fullPlain = `v?${mainVersion}${preRelease}?${build}?`;
const tilde = `^${loneTilde}${xRangePlain}$`;
const xRange = `^${gtlt}\\s*${xRangePlain}$`;
const comparator = `^${gtlt}\\s*(${fullPlain})$|^$`;
const gte0 = "^\\s*>=\\s*0.0.0\\s*$";
function parseRegex(source) {
return new RegExp(source);
}
function isXVersion(version) {
return !version || version.toLowerCase() === "x" || version === "*";
}
function pipe(...fns) {
return (x) => {
return fns.reduce((v, f) => f(v), x);
};
}
function extractComparator(comparatorString) {
return comparatorString.match(parseRegex(comparator));
}
function combineVersion(major, minor, patch, preRelease2) {
const mainVersion2 = `${major}.${minor}.${patch}`;
if (preRelease2) {
return `${mainVersion2}-${preRelease2}`;
}
return mainVersion2;
}
function parseHyphen(range) {
return range.replace(parseRegex(hyphenRange), (_range, from, fromMajor, fromMinor, fromPatch, _fromPreRelease, _fromBuild, to, toMajor, toMinor, toPatch, toPreRelease) => {
if (isXVersion(fromMajor)) {
from = "";
} else if (isXVersion(fromMinor)) {
from = `>=${fromMajor}.0.0`;
} else if (isXVersion(fromPatch)) {
from = `>=${fromMajor}.${fromMinor}.0`;
} else {
from = `>=${from}`;
}
if (isXVersion(toMajor)) {
to = "";
} else if (isXVersion(toMinor)) {
to = `<${+toMajor + 1}.0.0-0`;
} else if (isXVersion(toPatch)) {
to = `<${toMajor}.${+toMinor + 1}.0-0`;
} else if (toPreRelease) {
to = `<=${toMajor}.${toMinor}.${toPatch}-${toPreRelease}`;
} else {
to = `<=${to}`;
}
return `${from} ${to}`.trim();
});
}
function parseComparatorTrim(range) {
return range.replace(parseRegex(comparatorTrim), "$1$2$3");
}
function parseTildeTrim(range) {
return range.replace(parseRegex(tildeTrim), "$1~");
}
function parseCaretTrim(range) {
return range.replace(parseRegex(caretTrim), "$1^");
}
function parseCarets(range) {
return range
.trim()
.split(/\s+/)
.map((rangeVersion) => {
return rangeVersion.replace(parseRegex(caret), (_, major, minor, patch, preRelease2) => {
if (isXVersion(major)) {
return "";
} else if (isXVersion(minor)) {
return `>=${major}.0.0 <${+major + 1}.0.0-0`;
} else if (isXVersion(patch)) {
if (major === "0") {
return `>=${major}.${minor}.0 <${major}.${+minor + 1}.0-0`;
} else {
return `>=${major}.${minor}.0 <${+major + 1}.0.0-0`;
}
} else if (preRelease2) {
if (major === "0") {
if (minor === "0") {
return `>=${major}.${minor}.${patch}-${preRelease2} <${major}.${minor}.${+patch + 1}-0`;
} else {
return `>=${major}.${minor}.${patch}-${preRelease2} <${major}.${+minor + 1}.0-0`;
}
} else {
return `>=${major}.${minor}.${patch}-${preRelease2} <${+major + 1}.0.0-0`;
}
} else {
if (major === "0") {
if (minor === "0") {
return `>=${major}.${minor}.${patch} <${major}.${minor}.${+patch + 1}-0`;
} else {
return `>=${major}.${minor}.${patch} <${major}.${+minor + 1}.0-0`;
}
}
return `>=${major}.${minor}.${patch} <${+major + 1}.0.0-0`;
}
});
})
.join(" ");
}
function parseTildes(range) {
return range
.trim()
.split(/\s+/)
.map((rangeVersion) => {
return rangeVersion.replace(parseRegex(tilde), (_, major, minor, patch, preRelease2) => {
if (isXVersion(major)) {
return "";
} else if (isXVersion(minor)) {
return `>=${major}.0.0 <${+major + 1}.0.0-0`;
} else if (isXVersion(patch)) {
return `>=${major}.${minor}.0 <${major}.${+minor + 1}.0-0`;
} else if (preRelease2) {
return `>=${major}.${minor}.${patch}-${preRelease2} <${major}.${+minor + 1}.0-0`;
}
return `>=${major}.${minor}.${patch} <${major}.${+minor + 1}.0-0`;
});
})
.join(" ");
}
function parseXRanges(range) {
return range
.split(/\s+/)
.map((rangeVersion) => {
return rangeVersion.trim().replace(parseRegex(xRange), (ret, gtlt2, major, minor, patch, preRelease2) => {
const isXMajor = isXVersion(major);
const isXMinor = isXMajor || isXVersion(minor);
const isXPatch = isXMinor || isXVersion(patch);
if (gtlt2 === "=" && isXPatch) {
gtlt2 = "";
}
preRelease2 = "";
if (isXMajor) {
if (gtlt2 === ">" || gtlt2 === "<") {
return "<0.0.0-0";
} else {
return "*";
}
} else if (gtlt2 && isXPatch) {
if (isXMinor) {
minor = 0;
}
patch = 0;
if (gtlt2 === ">") {
gtlt2 = ">=";
if (isXMinor) {
major = +major + 1;
minor = 0;
patch = 0;
} else {
minor = +minor + 1;
patch = 0;
}
} else if (gtlt2 === "<=") {
gtlt2 = "<";
if (isXMinor) {
major = +major + 1;
} else {
minor = +minor + 1;
}
}
if (gtlt2 === "<") {
preRelease2 = "-0";
}
return `${gtlt2 + major}.${minor}.${patch}${preRelease2}`;
} else if (isXMinor) {
return `>=${major}.0.0${preRelease2} <${+major + 1}.0.0-0`;
} else if (isXPatch) {
return `>=${major}.${minor}.0${preRelease2} <${major}.${+minor + 1}.0-0`;
}
return ret;
});
})
.join(" ");
}
function parseStar(range) {
return range.trim().replace(parseRegex(star), "");
}
function parseGTE0(comparatorString) {
return comparatorString.trim().replace(parseRegex(gte0), "");
}
function compareAtom(rangeAtom, versionAtom) {
rangeAtom = +rangeAtom || rangeAtom;
versionAtom = +versionAtom || versionAtom;
if (rangeAtom > versionAtom) {
return 1;
}
if (rangeAtom === versionAtom) {
return 0;
}
return -1;
}
function comparePreRelease(rangeAtom, versionAtom) {
const { preRelease: rangePreRelease } = rangeAtom;
const { preRelease: versionPreRelease } = versionAtom;
if (rangePreRelease === void 0 && !!versionPreRelease) {
return 1;
}
if (!!rangePreRelease && versionPreRelease === void 0) {
return -1;
}
if (rangePreRelease === void 0 && versionPreRelease === void 0) {
return 0;
}
for (let i = 0, n = rangePreRelease.length; i <= n; i++) {
const rangeElement = rangePreRelease[i];
const versionElement = versionPreRelease[i];
if (rangeElement === versionElement) {
continue;
}
if (rangeElement === void 0 && versionElement === void 0) {
return 0;
}
if (!rangeElement) {
return 1;
}
if (!versionElement) {
return -1;
}
return compareAtom(rangeElement, versionElement);
}
return 0;
}
function compareVersion(rangeAtom, versionAtom) {
return (
compareAtom(rangeAtom.major, versionAtom.major) ||
compareAtom(rangeAtom.minor, versionAtom.minor) ||
compareAtom(rangeAtom.patch, versionAtom.patch) ||
comparePreRelease(rangeAtom, versionAtom)
);
}
function eq(rangeAtom, versionAtom) {
return rangeAtom.version === versionAtom.version;
}
function compare(rangeAtom, versionAtom) {
switch (rangeAtom.operator) {
case "":
case "=":
return eq(rangeAtom, versionAtom);
case ">":
return compareVersion(rangeAtom, versionAtom) < 0;
case ">=":
return eq(rangeAtom, versionAtom) || compareVersion(rangeAtom, versionAtom) < 0;
case "<":
return compareVersion(rangeAtom, versionAtom) > 0;
case "<=":
return eq(rangeAtom, versionAtom) || compareVersion(rangeAtom, versionAtom) > 0;
case void 0: {
return true;
}
default:
return false;
}
}
function parseComparatorString(range) {
return pipe(parseCarets, parseTildes, parseXRanges, parseStar)(range);
}
function parseRange(range) {
return pipe(parseHyphen, parseComparatorTrim, parseTildeTrim, parseCaretTrim)(range.trim()).split(/\s+/).join(" ");
}
function satisfy(version, range) {
if (!version) {
return false;
}
const parsedRange = parseRange(range);
const parsedComparator = parsedRange
.split(" ")
.map((rangeVersion) => parseComparatorString(rangeVersion))
.join(" ");
const comparators = parsedComparator.split(/\s+/).map((comparator2) => parseGTE0(comparator2));
const extractedVersion = extractComparator(version);
if (!extractedVersion) {
return false;
}
const [, versionOperator, , versionMajor, versionMinor, versionPatch, versionPreRelease] = extractedVersion;
const versionAtom = {
version: combineVersion(versionMajor, versionMinor, versionPatch, versionPreRelease),
major: versionMajor,
minor: versionMinor,
patch: versionPatch,
preRelease: versionPreRelease == null ? void 0 : versionPreRelease.split("."),
};
for (const comparator2 of comparators) {
const extractedComparator = extractComparator(comparator2);
if (!extractedComparator) {
return false;
}
const [, rangeOperator, , rangeMajor, rangeMinor, rangePatch, rangePreRelease] = extractedComparator;
const rangeAtom = {
operator: rangeOperator,
version: combineVersion(rangeMajor, rangeMinor, rangePatch, rangePreRelease),
major: rangeMajor,
minor: rangeMinor,
patch: rangePatch,
preRelease: rangePreRelease == null ? void 0 : rangePreRelease.split("."),
};
if (!compare(rangeAtom, versionAtom)) {
return false;
}
}
return true;
}
const currentImports = {};
// eslint-disable-next-line no-undef
const moduleMap = {
"react": { get: () => () => __federation_import(new URL("__federation_shared_react-DoKb58Ht.js", import.meta.url).href), import: true },
"react-dom": { get: () => () => __federation_import(new URL("__federation_shared_react-dom-DU2-P0kt.js", import.meta.url).href), import: true },
"@block-ninja/ui": { get: () => () => __federation_import(new URL("__federation_shared_@block-ninja/ui-C3CAr7wz.js", import.meta.url).href), import: true },
};
const moduleCache = Object.create(null);
async function importShared(name, shareScope = "default") {
return moduleCache[name] ? new Promise((r) => r(moduleCache[name])) : (await getSharedFromRuntime(name, shareScope)) || getSharedFromLocal(name);
}
// eslint-disable-next-line
async function __federation_import(name) {
currentImports[name] ??= import(name);
return currentImports[name];
}
async function getSharedFromRuntime(name, shareScope) {
let module = null;
if (globalThis?.__federation_shared__?.[shareScope]?.[name]) {
const versionObj = globalThis.__federation_shared__[shareScope][name];
const requiredVersion = moduleMap[name]?.requiredVersion;
const hasRequiredVersion = !!requiredVersion;
if (hasRequiredVersion) {
const versionKey = Object.keys(versionObj).find((version) => satisfy(version, requiredVersion));
if (versionKey) {
const versionValue = versionObj[versionKey];
module = await (await versionValue.get())();
} else {
console.log(`provider support ${name}(${versionKey}) is not satisfied requiredVersion(\${moduleMap[name].requiredVersion})`);
}
} else {
const versionKey = Object.keys(versionObj)[0];
const versionValue = versionObj[versionKey];
module = await (await versionValue.get())();
}
}
if (module) {
return flattenModule(module, name);
}
}
async function getSharedFromLocal(name) {
if (moduleMap[name]?.import) {
let module = await (await moduleMap[name].get())();
return flattenModule(module, name);
} else {
console.error(`consumer config import=false,so cant use callback shared module`);
}
}
function flattenModule(module, name) {
// use a shared module which export default a function will getting error 'TypeError: xxx is not a function'
if (typeof module.default === "function") {
Object.keys(module).forEach((key) => {
if (key !== "default") {
module.default[key] = module[key];
}
});
moduleCache[name] = module.default;
return module.default;
}
if (module.default) module = Object.assign({}, module.default, module);
moduleCache[name] = module;
return module;
}
export { importShared, getSharedFromLocal as importSharedLocal, getSharedFromRuntime as importSharedRuntime };

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,7 @@
import { g as getDefaultExportFromCjs } from "./_commonjsHelpers-B85MJLTf.js";
import { r as requireReact } from "./index-DQGM2Mpm.js";
var reactExports = requireReact();
const index = /*@__PURE__*/ getDefaultExportFromCjs(reactExports);
export { index as default };

View File

@ -0,0 +1,7 @@
import { g as getDefaultExportFromCjs } from "./_commonjsHelpers-B85MJLTf.js";
import { r as requireReactDom } from "./index-eoEhLOdg.js";
var reactDomExports = requireReactDom();
const index = /*@__PURE__*/ getDefaultExportFromCjs(reactDomExports);
export { index as default };

View File

@ -0,0 +1,5 @@
function getDefaultExportFromCjs(x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}
export { getDefaultExportFromCjs as g };

File diff suppressed because it is too large Load Diff

77744
web/dist/assets/index-BSkdT-DR.js vendored Normal file

File diff suppressed because one or more lines are too long

22886
web/dist/assets/index-Bs--Ol2m.js vendored Normal file

File diff suppressed because it is too large Load Diff

105
web/dist/assets/index-Coq9nAfE.js vendored Normal file
View File

@ -0,0 +1,105 @@
import { importShared } from "./__federation_fn_import-hlt2XzeI.js";
import { X as requireShim } from "./index-Bs--Ol2m.js";
/**
* @license lucide-react v0.468.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/
const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
const mergeClasses = (...classes) =>
classes
.filter((className, index, array) => {
return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
})
.join(" ")
.trim();
/**
* @license lucide-react v0.468.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/
var defaultAttributes = {
xmlns: "http://www.w3.org/2000/svg",
width: 24,
height: 24,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: 2,
strokeLinecap: "round",
strokeLinejoin: "round",
};
/**
* @license lucide-react v0.468.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/
const { forwardRef: forwardRef$1, createElement: createElement$1 } = await importShared("react");
const Icon = forwardRef$1(({ color = "currentColor", size = 24, strokeWidth = 2, absoluteStrokeWidth, className = "", children, iconNode, ...rest }, ref) => {
return createElement$1(
"svg",
{
ref,
...defaultAttributes,
width: size,
height: size,
stroke: color,
strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
className: mergeClasses("lucide", className),
...rest,
},
[...iconNode.map(([tag, attrs]) => createElement$1(tag, attrs)), ...(Array.isArray(children) ? children : [children])],
);
});
/**
* @license lucide-react v0.468.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/
const { forwardRef, createElement } = await importShared("react");
const createLucideIcon = (iconName, iconNode) => {
const Component = forwardRef(({ className, ...props }, ref) =>
createElement(Icon, {
ref,
iconNode,
className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),
...props,
}),
);
Component.displayName = `${iconName}`;
return Component;
};
/**
* @license lucide-react v0.468.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/
const List = createLucideIcon("List", [
["path", { d: "M3 12h.01", key: "nlz23k" }],
["path", { d: "M3 18h.01", key: "1tta3j" }],
["path", { d: "M3 6h.01", key: "1rqtza" }],
["path", { d: "M8 12h13", key: "1za7za" }],
["path", { d: "M8 18h13", key: "1lx6n3" }],
["path", { d: "M8 6h13", key: "ik3vkj" }],
]);
var shimExports = requireShim();
export { List as L, createLucideIcon as c, shimExports as s };

959
web/dist/assets/index-D75C7LcR.js vendored Normal file
View File

@ -0,0 +1,959 @@
import { importShared } from "./__federation_fn_import-hlt2XzeI.js";
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
function _arrayWithHoles(r) {
if (Array.isArray(r)) return r;
}
function _defineProperty$1(e, r, t) {
return (
(r = _toPropertyKey(r)) in e ?
Object.defineProperty(e, r, {
value: t,
enumerable: true,
configurable: true,
writable: true,
})
: (e[r] = t),
e
);
}
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : ("undefined" != typeof Symbol && r[Symbol.iterator]) || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = true,
o = false;
try {
if (((i = (t = t.call(r)).next), 0 === l));
else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
((o = true), (n = r));
} finally {
try {
if (!f && null != t.return && ((u = t.return()), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function ownKeys$1(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
(r &&
(o = o.filter(function (r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})),
t.push.apply(t, o));
}
return t;
}
function _objectSpread2$1(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ?
ownKeys$1(Object(t), true).forEach(function (r) {
_defineProperty$1(e, r, t[r]);
})
: Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t))
: ownKeys$1(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
function _objectWithoutProperties(e, t) {
if (null == e) return {};
var o,
r,
i = _objectWithoutPropertiesLoose(e, t);
if (Object.getOwnPropertySymbols) {
var n = Object.getOwnPropertySymbols(e);
for (r = 0; r < n.length; r++) ((o = n[r]), -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]));
}
return i;
}
function _objectWithoutPropertiesLoose(r, e) {
if (null == r) return {};
var t = {};
for (var n in r)
if ({}.hasOwnProperty.call(r, n)) {
if (-1 !== e.indexOf(n)) continue;
t[n] = r[n];
}
return t;
}
function _slicedToArray(r, e) {
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _toPrimitive(t, r) {
if ("object" != typeof t || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r);
if ("object" != typeof i) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == typeof i ? i : i + "";
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return (
"Object" === t && r.constructor && (t = r.constructor.name),
"Map" === t || "Set" === t ? Array.from(r)
: "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a)
: void 0
);
}
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true,
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly)
symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function compose$1() {
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) {
fns[_key] = arguments[_key];
}
return function (x) {
return fns.reduceRight(function (y, f) {
return f(y);
}, x);
};
}
function curry$1(fn) {
return function curried() {
var _this = this;
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return args.length >= fn.length ?
fn.apply(this, args)
: function () {
for (var _len3 = arguments.length, nextArgs = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
nextArgs[_key3] = arguments[_key3];
}
return curried.apply(_this, [].concat(args, nextArgs));
};
};
}
function isObject$1(value) {
return {}.toString.call(value).includes("Object");
}
function isEmpty(obj) {
return !Object.keys(obj).length;
}
function isFunction(value) {
return typeof value === "function";
}
function hasOwnProperty(object, property) {
return Object.prototype.hasOwnProperty.call(object, property);
}
function validateChanges(initial, changes) {
if (!isObject$1(changes)) errorHandler$1("changeType");
if (
Object.keys(changes).some(function (field) {
return !hasOwnProperty(initial, field);
})
)
errorHandler$1("changeField");
return changes;
}
function validateSelector(selector) {
if (!isFunction(selector)) errorHandler$1("selectorType");
}
function validateHandler(handler) {
if (!(isFunction(handler) || isObject$1(handler))) errorHandler$1("handlerType");
if (
isObject$1(handler) &&
Object.values(handler).some(function (_handler) {
return !isFunction(_handler);
})
)
errorHandler$1("handlersType");
}
function validateInitial(initial) {
if (!initial) errorHandler$1("initialIsRequired");
if (!isObject$1(initial)) errorHandler$1("initialType");
if (isEmpty(initial)) errorHandler$1("initialContent");
}
function throwError$1(errorMessages, type) {
throw new Error(errorMessages[type] || errorMessages["default"]);
}
var errorMessages$1 = {
initialIsRequired: "initial state is required",
initialType: "initial state should be an object",
initialContent: "initial state shouldn't be an empty object",
handlerType: "handler should be an object or a function",
handlersType: "all handlers should be a functions",
selectorType: "selector should be a function",
changeType: "provided value of changes should be an object",
changeField: 'it seams you want to change a field in the state which is not specified in the "initial" state',
default: "an unknown error accured in `state-local` package",
};
var errorHandler$1 = curry$1(throwError$1)(errorMessages$1);
var validators$1 = {
changes: validateChanges,
selector: validateSelector,
handler: validateHandler,
initial: validateInitial,
};
function create(initial) {
var handler = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
validators$1.initial(initial);
validators$1.handler(handler);
var state = {
current: initial,
};
var didUpdate = curry$1(didStateUpdate)(state, handler);
var update = curry$1(updateState)(state);
var validate = curry$1(validators$1.changes)(initial);
var getChanges = curry$1(extractChanges)(state);
function getState() {
var selector =
arguments.length > 0 && arguments[0] !== undefined ?
arguments[0]
: function (state) {
return state;
};
validators$1.selector(selector);
return selector(state.current);
}
function setState(causedChanges) {
compose$1(didUpdate, update, validate, getChanges)(causedChanges);
}
return [getState, setState];
}
function extractChanges(state, causedChanges) {
return isFunction(causedChanges) ? causedChanges(state.current) : causedChanges;
}
function updateState(state, changes) {
state.current = _objectSpread2(_objectSpread2({}, state.current), changes);
return changes;
}
function didStateUpdate(state, handler, changes) {
isFunction(handler) ?
handler(state.current)
: Object.keys(changes).forEach(function (field) {
var _handler$field;
return (_handler$field = handler[field]) === null || _handler$field === void 0 ? void 0 : _handler$field.call(handler, state.current[field]);
});
return changes;
}
var index = {
create: create,
};
var config$1 = {
paths: {
vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs",
},
};
function curry(fn) {
return function curried() {
var _this = this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return args.length >= fn.length ?
fn.apply(this, args)
: function () {
for (var _len2 = arguments.length, nextArgs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
nextArgs[_key2] = arguments[_key2];
}
return curried.apply(_this, [].concat(args, nextArgs));
};
};
}
function isObject(value) {
return {}.toString.call(value).includes("Object");
}
/**
* validates the configuration object and informs about deprecation
* @param {Object} config - the configuration object
* @return {Object} config - the validated configuration object
*/
function validateConfig(config) {
if (!config) errorHandler("configIsRequired");
if (!isObject(config)) errorHandler("configType");
if (config.urls) {
informAboutDeprecation();
return {
paths: {
vs: config.urls.monacoBase,
},
};
}
return config;
}
/**
* logs deprecation message
*/
function informAboutDeprecation() {
console.warn(errorMessages.deprecation);
}
function throwError(errorMessages, type) {
throw new Error(errorMessages[type] || errorMessages["default"]);
}
var errorMessages = {
configIsRequired: "the configuration object is required",
configType: "the configuration object should be an object",
default: "an unknown error accured in `@monaco-editor/loader` package",
deprecation:
"Deprecation warning!\n You are using deprecated way of configuration.\n\n Instead of using\n monaco.config({ urls: { monacoBase: '...' } })\n use\n monaco.config({ paths: { vs: '...' } })\n\n For more please check the link https://github.com/suren-atoyan/monaco-loader#config\n ",
};
var errorHandler = curry(throwError)(errorMessages);
var validators = {
config: validateConfig,
};
var compose = function compose() {
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) {
fns[_key] = arguments[_key];
}
return function (x) {
return fns.reduceRight(function (y, f) {
return f(y);
}, x);
};
};
function merge(target, source) {
Object.keys(source).forEach(function (key) {
if (source[key] instanceof Object) {
if (target[key]) {
Object.assign(source[key], merge(target[key], source[key]));
}
}
});
return _objectSpread2$1(_objectSpread2$1({}, target), source);
}
// The source (has been changed) is https://github.com/facebook/react/issues/5465#issuecomment-157888325
var CANCELATION_MESSAGE = {
type: "cancelation",
msg: "operation is manually canceled",
};
function makeCancelable(promise) {
var hasCanceled_ = false;
var wrappedPromise = new Promise(function (resolve, reject) {
promise.then(function (val) {
return hasCanceled_ ? reject(CANCELATION_MESSAGE) : resolve(val);
});
promise["catch"](reject);
});
return (
(wrappedPromise.cancel = function () {
return (hasCanceled_ = true);
}),
wrappedPromise
);
}
var _excluded = ["monaco"];
/** the local state of the module */
var _state$create = index.create({
config: config$1,
isInitialized: false,
resolve: null,
reject: null,
monaco: null,
}),
_state$create2 = _slicedToArray(_state$create, 2),
getState = _state$create2[0],
setState = _state$create2[1];
/**
* set the loader configuration
* @param {Object} config - the configuration object
*/
function config(globalConfig) {
var _validators$config = validators.config(globalConfig),
monaco = _validators$config.monaco,
config = _objectWithoutProperties(_validators$config, _excluded);
setState(function (state) {
return {
config: merge(state.config, config),
monaco: monaco,
};
});
}
/**
* handles the initialization of the monaco-editor
* @return {Promise} - returns an instance of monaco (with a cancelable promise)
*/
function init() {
var state = getState(function (_ref) {
var monaco = _ref.monaco,
isInitialized = _ref.isInitialized,
resolve = _ref.resolve;
return {
monaco: monaco,
isInitialized: isInitialized,
resolve: resolve,
};
});
if (!state.isInitialized) {
setState({
isInitialized: true,
});
if (state.monaco) {
state.resolve(state.monaco);
return makeCancelable(wrapperPromise);
}
if (window.monaco && window.monaco.editor) {
storeMonacoInstance(window.monaco);
state.resolve(window.monaco);
return makeCancelable(wrapperPromise);
}
compose(injectScripts, getMonacoLoaderScript)(configureLoader);
}
return makeCancelable(wrapperPromise);
}
/**
* injects provided scripts into the document.body
* @param {Object} script - an HTML script element
* @return {Object} - the injected HTML script element
*/
function injectScripts(script) {
return document.body.appendChild(script);
}
/**
* creates an HTML script element with/without provided src
* @param {string} [src] - the source path of the script
* @return {Object} - the created HTML script element
*/
function createScript(src) {
var script = document.createElement("script");
return (src && (script.src = src), script);
}
/**
* creates an HTML script element with the monaco loader src
* @return {Object} - the created HTML script element
*/
function getMonacoLoaderScript(configureLoader) {
var state = getState(function (_ref2) {
var config = _ref2.config,
reject = _ref2.reject;
return {
config: config,
reject: reject,
};
});
var loaderScript = createScript("".concat(state.config.paths.vs, "/loader.js"));
loaderScript.onload = function () {
return configureLoader();
};
loaderScript.onerror = state.reject;
return loaderScript;
}
/**
* configures the monaco loader
*/
function configureLoader() {
var state = getState(function (_ref3) {
var config = _ref3.config,
resolve = _ref3.resolve,
reject = _ref3.reject;
return {
config: config,
resolve: resolve,
reject: reject,
};
});
var require = window.require;
require.config(state.config);
require(["vs/editor/editor.main"], function (loaded) {
var monaco = loaded.m /* for 0.53 & 0.54 */ || loaded; /* for other versions */
storeMonacoInstance(monaco);
state.resolve(monaco);
}, function (error) {
state.reject(error);
});
}
/**
* store monaco instance in local state
*/
function storeMonacoInstance(monaco) {
if (!getState().monaco) {
setState({
monaco: monaco,
});
}
}
/**
* internal helper function
* extracts stored monaco instance
* @return {Object|null} - the monaco instance
*/
function __getMonacoInstance() {
return getState(function (_ref4) {
var monaco = _ref4.monaco;
return monaco;
});
}
var wrapperPromise = new Promise(function (resolve, reject) {
return setState({
resolve: resolve,
reject: reject,
});
});
var loader = {
config: config,
init: init,
__getMonacoInstance: __getMonacoInstance,
};
const { memo: Te } = await importShared("react");
const ke = await importShared("react");
const { useState: re, useRef: S, useCallback: oe, useEffect: ne } = ke;
const { memo: ye } = await importShared("react");
const K = await importShared("react");
var le = { wrapper: { display: "flex", position: "relative", textAlign: "initial" }, fullWidth: { width: "100%" }, hide: { display: "none" } },
v = le;
const me = await importShared("react");
var ae = { container: { display: "flex", height: "100%", width: "100%", justifyContent: "center", alignItems: "center" } },
Y = ae;
function Me({ children: e }) {
return me.createElement("div", { style: Y.container }, e);
}
var Z = Me;
var $ = Z;
function Ee({ width: e, height: r, isEditorReady: n, loading: t, _ref: a, className: m, wrapperProps: E }) {
return K.createElement(
"section",
{ style: { ...v.wrapper, width: e, height: r }, ...E },
!n && K.createElement($, null, t),
K.createElement("div", { ref: a, style: { ...v.fullWidth, ...(!n && v.hide) }, className: m }),
);
}
var ee = Ee;
var H = ye(ee);
const { useEffect: xe } = await importShared("react");
function Ce(e) {
xe(e, []);
}
var k = Ce;
const { useEffect: ge, useRef: Re } = await importShared("react");
function he(e, r, n = true) {
let t = Re(true);
ge(
t.current || !n ?
() => {
t.current = false;
}
: e,
r,
);
}
var l = he;
function D() {}
function h(e, r, n, t) {
return De(e, t) || be(e, r, n, t);
}
function De(e, r) {
return e.editor.getModel(te(e, r));
}
function be(e, r, n, t) {
return e.editor.createModel(r, n, t ? te(e, t) : void 0);
}
function te(e, r) {
return e.Uri.parse(r);
}
function Oe({
original: e,
modified: r,
language: n,
originalLanguage: t,
modifiedLanguage: a,
originalModelPath: m,
modifiedModelPath: E,
keepCurrentOriginalModel: g = false,
keepCurrentModifiedModel: N = false,
theme: x = "light",
loading: P = "Loading...",
options: y = {},
height: V = "100%",
width: z = "100%",
className: F,
wrapperProps: j = {},
beforeMount: A = D,
onMount: q = D,
}) {
let [M, O] = re(false),
[T, s] = re(true),
u = S(null),
c = S(null),
w = S(null),
d = S(q),
o = S(A),
b = S(false);
(k(() => {
let i = loader.init();
return (i.then((f) => (c.current = f) && s(false)).catch((f) => f?.type !== "cancelation" && console.error("Monaco initialization: error:", f)), () => (u.current ? I() : i.cancel()));
}),
l(
() => {
if (u.current && c.current) {
let i = u.current.getOriginalEditor(),
f = h(c.current, e || "", t || n || "text", m || "");
f !== i.getModel() && i.setModel(f);
}
},
[m],
M,
),
l(
() => {
if (u.current && c.current) {
let i = u.current.getModifiedEditor(),
f = h(c.current, r || "", a || n || "text", E || "");
f !== i.getModel() && i.setModel(f);
}
},
[E],
M,
),
l(
() => {
let i = u.current.getModifiedEditor();
i.getOption(c.current.editor.EditorOption.readOnly) ?
i.setValue(r || "")
: r !== i.getValue() && (i.executeEdits("", [{ range: i.getModel().getFullModelRange(), text: r || "", forceMoveMarkers: true }]), i.pushUndoStop());
},
[r],
M,
),
l(
() => {
u.current?.getModel()?.original.setValue(e || "");
},
[e],
M,
),
l(
() => {
let { original: i, modified: f } = u.current.getModel();
(c.current.editor.setModelLanguage(i, t || n || "text"), c.current.editor.setModelLanguage(f, a || n || "text"));
},
[n, t, a],
M,
),
l(
() => {
c.current?.editor.setTheme(x);
},
[x],
M,
),
l(
() => {
u.current?.updateOptions(y);
},
[y],
M,
));
let L = oe(() => {
if (!c.current) return;
o.current(c.current);
let i = h(c.current, e || "", t || n || "text", m || ""),
f = h(c.current, r || "", a || n || "text", E || "");
u.current?.setModel({ original: i, modified: f });
}, [n, r, a, e, t, m, E]),
U = oe(() => {
!b.current && w.current && ((u.current = c.current.editor.createDiffEditor(w.current, { automaticLayout: true, ...y })), L(), c.current?.editor.setTheme(x), O(true), (b.current = true));
}, [y, x, L]);
(ne(() => {
M && d.current(u.current, c.current);
}, [M]),
ne(() => {
!T && !M && U();
}, [T, M, U]));
function I() {
let i = u.current?.getModel();
(g || i?.original?.dispose(), N || i?.modified?.dispose(), u.current?.dispose());
}
return ke.createElement(H, { width: z, height: V, isEditorReady: M, loading: P, _ref: w, className: F, wrapperProps: j });
}
var ie = Oe;
var we = Te(ie);
const { useState: Ie } = await importShared("react");
function Pe() {
let [e, r] = Ie(loader.__getMonacoInstance());
return (
k(() => {
let n;
return (
e ||
((n = loader.init()),
n.then((t) => {
r(t);
})),
() => n?.cancel()
);
}),
e
);
}
var Le = Pe;
const { memo: ze } = await importShared("react");
const We = await importShared("react");
const { useState: ue, useEffect: W, useRef: C, useCallback: _e } = We;
const { useEffect: Ue, useRef: ve } = await importShared("react");
function He(e) {
let r = ve();
return (
Ue(() => {
r.current = e;
}, [e]),
r.current
);
}
var se = He;
var _ = new Map();
function Ve({
defaultValue: e,
defaultLanguage: r,
defaultPath: n,
value: t,
language: a,
path: m,
theme: E = "light",
line: g,
loading: N = "Loading...",
options: x = {},
overrideServices: P = {},
saveViewState: y = true,
keepCurrentModel: V = false,
width: z = "100%",
height: F = "100%",
className: j,
wrapperProps: A = {},
beforeMount: q = D,
onMount: M = D,
onChange: O,
onValidate: T = D,
}) {
let [s, u] = ue(false),
[c, w] = ue(true),
d = C(null),
o = C(null),
b = C(null),
L = C(M),
U = C(q),
I = C(),
i = C(t),
f = se(m),
Q = C(false),
B = C(false);
(k(() => {
let p = loader.init();
return (p.then((R) => (d.current = R) && w(false)).catch((R) => R?.type !== "cancelation" && console.error("Monaco initialization: error:", R)), () => (o.current ? pe() : p.cancel()));
}),
l(
() => {
let p = h(d.current, e || t || "", r || a || "", m || n || "");
p !== o.current?.getModel() && (y && _.set(f, o.current?.saveViewState()), o.current?.setModel(p), y && o.current?.restoreViewState(_.get(m)));
},
[m],
s,
),
l(
() => {
o.current?.updateOptions(x);
},
[x],
s,
),
l(
() => {
!o.current ||
t === void 0 ||
(o.current.getOption(d.current.editor.EditorOption.readOnly) ?
o.current.setValue(t)
: t !== o.current.getValue() &&
((B.current = true),
o.current.executeEdits("", [{ range: o.current.getModel().getFullModelRange(), text: t, forceMoveMarkers: true }]),
o.current.pushUndoStop(),
(B.current = false)));
},
[t],
s,
),
l(
() => {
let p = o.current?.getModel();
p && a && d.current?.editor.setModelLanguage(p, a);
},
[a],
s,
),
l(
() => {
g !== void 0 && o.current?.revealLine(g);
},
[g],
s,
),
l(
() => {
d.current?.editor.setTheme(E);
},
[E],
s,
));
let X = _e(() => {
if (!(!b.current || !d.current) && !Q.current) {
U.current(d.current);
let p = m || n,
R = h(d.current, t || e || "", r || a || "", p || "");
((o.current = d.current?.editor.create(b.current, { model: R, automaticLayout: true, ...x }, P)),
y && o.current.restoreViewState(_.get(p)),
d.current.editor.setTheme(E),
g !== void 0 && o.current.revealLine(g),
u(true),
(Q.current = true));
}
}, [e, r, n, t, a, m, x, P, y, E, g]);
(W(() => {
s && L.current(o.current, d.current);
}, [s]),
W(() => {
!c && !s && X();
}, [c, s, X]),
(i.current = t),
W(() => {
s &&
O &&
(I.current?.dispose(),
(I.current = o.current?.onDidChangeModelContent((p) => {
B.current || O(o.current.getValue(), p);
})));
}, [s, O]),
W(() => {
if (s) {
let p = d.current.editor.onDidChangeMarkers((R) => {
let G = o.current.getModel()?.uri;
if (G && R.find((J) => J.path === G.path)) {
let J = d.current.editor.getModelMarkers({ resource: G });
T?.(J);
}
});
return () => {
p?.dispose();
};
}
return () => {};
}, [s, T]));
function pe() {
(I.current?.dispose(), V ? y && _.set(m, o.current.saveViewState()) : o.current.getModel()?.dispose(), o.current.dispose());
}
return We.createElement(H, { width: z, height: F, isEditorReady: s, loading: N, _ref: b, className: j, wrapperProps: A });
}
var fe = Ve;
var de = ze(fe);
var Ft = de;
export { we as DiffEditor, de as Editor, Ft as default, loader, Le as useMonaco };

347
web/dist/assets/index-DQGM2Mpm.js vendored Normal file
View File

@ -0,0 +1,347 @@
var react = { exports: {} };
var react_production_min = {};
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReact_production_min;
function requireReact_production_min() {
if (hasRequiredReact_production_min) return react_production_min;
hasRequiredReact_production_min = 1;
var l = Symbol.for("react.element"),
n = Symbol.for("react.portal"),
p = Symbol.for("react.fragment"),
q = Symbol.for("react.strict_mode"),
r = Symbol.for("react.profiler"),
t = Symbol.for("react.provider"),
u = Symbol.for("react.context"),
v = Symbol.for("react.forward_ref"),
w = Symbol.for("react.suspense"),
x = Symbol.for("react.memo"),
y = Symbol.for("react.lazy"),
z = Symbol.iterator;
function A(a) {
if (null === a || "object" !== typeof a) return null;
a = (z && a[z]) || a["@@iterator"];
return "function" === typeof a ? a : null;
}
var B = {
isMounted: function () {
return false;
},
enqueueForceUpdate: function () {},
enqueueReplaceState: function () {},
enqueueSetState: function () {},
},
C = Object.assign,
D = {};
function E(a, b, e) {
this.props = a;
this.context = b;
this.refs = D;
this.updater = e || B;
}
E.prototype.isReactComponent = {};
E.prototype.setState = function (a, b) {
if ("object" !== typeof a && "function" !== typeof a && null != a)
throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");
this.updater.enqueueSetState(this, a, b, "setState");
};
E.prototype.forceUpdate = function (a) {
this.updater.enqueueForceUpdate(this, a, "forceUpdate");
};
function F() {}
F.prototype = E.prototype;
function G(a, b, e) {
this.props = a;
this.context = b;
this.refs = D;
this.updater = e || B;
}
var H = (G.prototype = new F());
H.constructor = G;
C(H, E.prototype);
H.isPureReactComponent = true;
var I = Array.isArray,
J = Object.prototype.hasOwnProperty,
K = { current: null },
L = { key: true, ref: true, __self: true, __source: true };
function M(a, b, e) {
var d,
c = {},
k = null,
h = null;
if (null != b) for (d in (void 0 !== b.ref && (h = b.ref), void 0 !== b.key && (k = "" + b.key), b)) J.call(b, d) && !L.hasOwnProperty(d) && (c[d] = b[d]);
var g = arguments.length - 2;
if (1 === g) c.children = e;
else if (1 < g) {
for (var f = Array(g), m = 0; m < g; m++) f[m] = arguments[m + 2];
c.children = f;
}
if (a && a.defaultProps) for (d in ((g = a.defaultProps), g)) void 0 === c[d] && (c[d] = g[d]);
return { $$typeof: l, type: a, key: k, ref: h, props: c, _owner: K.current };
}
function N(a, b) {
return { $$typeof: l, type: a.type, key: b, ref: a.ref, props: a.props, _owner: a._owner };
}
function O(a) {
return "object" === typeof a && null !== a && a.$$typeof === l;
}
function escape(a) {
var b = { "=": "=0", ":": "=2" };
return (
"$" +
a.replace(/[=:]/g, function (a) {
return b[a];
})
);
}
var P = /\/+/g;
function Q(a, b) {
return "object" === typeof a && null !== a && null != a.key ? escape("" + a.key) : b.toString(36);
}
function R(a, b, e, d, c) {
var k = typeof a;
if ("undefined" === k || "boolean" === k) a = null;
var h = false;
if (null === a) h = true;
else
switch (k) {
case "string":
case "number":
h = true;
break;
case "object":
switch (a.$$typeof) {
case l:
case n:
h = true;
}
}
if (h)
return (
(h = a),
(c = c(h)),
(a = "" === d ? "." + Q(h, 0) : d),
I(c) ?
((e = ""),
null != a && (e = a.replace(P, "$&/") + "/"),
R(c, b, e, "", function (a) {
return a;
}))
: null != c && (O(c) && (c = N(c, e + (!c.key || (h && h.key === c.key) ? "" : ("" + c.key).replace(P, "$&/") + "/") + a)), b.push(c)),
1
);
h = 0;
d = "" === d ? "." : d + ":";
if (I(a))
for (var g = 0; g < a.length; g++) {
k = a[g];
var f = d + Q(k, g);
h += R(k, b, e, f, c);
}
else if (((f = A(a)), "function" === typeof f)) for (a = f.call(a), g = 0; !(k = a.next()).done; ) ((k = k.value), (f = d + Q(k, g++)), (h += R(k, b, e, f, c)));
else if ("object" === k)
throw (
(b = String(a)),
Error(
"Objects are not valid as a React child (found: " +
("[object Object]" === b ? "object with keys {" + Object.keys(a).join(", ") + "}" : b) +
"). If you meant to render a collection of children, use an array instead.",
)
);
return h;
}
function S(a, b, e) {
if (null == a) return a;
var d = [],
c = 0;
R(a, d, "", "", function (a) {
return b.call(e, a, c++);
});
return d;
}
function T(a) {
if (-1 === a._status) {
var b = a._result;
b = b();
b.then(
function (b) {
if (0 === a._status || -1 === a._status) ((a._status = 1), (a._result = b));
},
function (b) {
if (0 === a._status || -1 === a._status) ((a._status = 2), (a._result = b));
},
);
-1 === a._status && ((a._status = 0), (a._result = b));
}
if (1 === a._status) return a._result.default;
throw a._result;
}
var U = { current: null },
V = { transition: null },
W = { ReactCurrentDispatcher: U, ReactCurrentBatchConfig: V, ReactCurrentOwner: K };
function X() {
throw Error("act(...) is not supported in production builds of React.");
}
react_production_min.Children = {
map: S,
forEach: function (a, b, e) {
S(
a,
function () {
b.apply(this, arguments);
},
e,
);
},
count: function (a) {
var b = 0;
S(a, function () {
b++;
});
return b;
},
toArray: function (a) {
return (
S(a, function (a) {
return a;
}) || []
);
},
only: function (a) {
if (!O(a)) throw Error("React.Children.only expected to receive a single React element child.");
return a;
},
};
react_production_min.Component = E;
react_production_min.Fragment = p;
react_production_min.Profiler = r;
react_production_min.PureComponent = G;
react_production_min.StrictMode = q;
react_production_min.Suspense = w;
react_production_min.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = W;
react_production_min.act = X;
react_production_min.cloneElement = function (a, b, e) {
if (null === a || void 0 === a) throw Error("React.cloneElement(...): The argument must be a React element, but you passed " + a + ".");
var d = C({}, a.props),
c = a.key,
k = a.ref,
h = a._owner;
if (null != b) {
void 0 !== b.ref && ((k = b.ref), (h = K.current));
void 0 !== b.key && (c = "" + b.key);
if (a.type && a.type.defaultProps) var g = a.type.defaultProps;
for (f in b) J.call(b, f) && !L.hasOwnProperty(f) && (d[f] = void 0 === b[f] && void 0 !== g ? g[f] : b[f]);
}
var f = arguments.length - 2;
if (1 === f) d.children = e;
else if (1 < f) {
g = Array(f);
for (var m = 0; m < f; m++) g[m] = arguments[m + 2];
d.children = g;
}
return { $$typeof: l, type: a.type, key: c, ref: k, props: d, _owner: h };
};
react_production_min.createContext = function (a) {
a = { $$typeof: u, _currentValue: a, _currentValue2: a, _threadCount: 0, Provider: null, Consumer: null, _defaultValue: null, _globalName: null };
a.Provider = { $$typeof: t, _context: a };
return (a.Consumer = a);
};
react_production_min.createElement = M;
react_production_min.createFactory = function (a) {
var b = M.bind(null, a);
b.type = a;
return b;
};
react_production_min.createRef = function () {
return { current: null };
};
react_production_min.forwardRef = function (a) {
return { $$typeof: v, render: a };
};
react_production_min.isValidElement = O;
react_production_min.lazy = function (a) {
return { $$typeof: y, _payload: { _status: -1, _result: a }, _init: T };
};
react_production_min.memo = function (a, b) {
return { $$typeof: x, type: a, compare: void 0 === b ? null : b };
};
react_production_min.startTransition = function (a) {
var b = V.transition;
V.transition = {};
try {
a();
} finally {
V.transition = b;
}
};
react_production_min.unstable_act = X;
react_production_min.useCallback = function (a, b) {
return U.current.useCallback(a, b);
};
react_production_min.useContext = function (a) {
return U.current.useContext(a);
};
react_production_min.useDebugValue = function () {};
react_production_min.useDeferredValue = function (a) {
return U.current.useDeferredValue(a);
};
react_production_min.useEffect = function (a, b) {
return U.current.useEffect(a, b);
};
react_production_min.useId = function () {
return U.current.useId();
};
react_production_min.useImperativeHandle = function (a, b, e) {
return U.current.useImperativeHandle(a, b, e);
};
react_production_min.useInsertionEffect = function (a, b) {
return U.current.useInsertionEffect(a, b);
};
react_production_min.useLayoutEffect = function (a, b) {
return U.current.useLayoutEffect(a, b);
};
react_production_min.useMemo = function (a, b) {
return U.current.useMemo(a, b);
};
react_production_min.useReducer = function (a, b, e) {
return U.current.useReducer(a, b, e);
};
react_production_min.useRef = function (a) {
return U.current.useRef(a);
};
react_production_min.useState = function (a) {
return U.current.useState(a);
};
react_production_min.useSyncExternalStore = function (a, b, e) {
return U.current.useSyncExternalStore(a, b, e);
};
react_production_min.useTransition = function () {
return U.current.useTransition();
};
react_production_min.version = "18.3.1";
return react_production_min;
}
var hasRequiredReact;
function requireReact() {
if (hasRequiredReact) return react.exports;
hasRequiredReact = 1;
{
react.exports = requireReact_production_min();
}
return react.exports;
}
export { requireReact as r };

7665
web/dist/assets/index-eoEhLOdg.js vendored Normal file

File diff suppressed because it is too large Load Diff

59
web/dist/assets/jsx-runtime-CvJTHeKY.js vendored Normal file
View File

@ -0,0 +1,59 @@
import { r as requireReact } from "./index-DQGM2Mpm.js";
var jsxRuntime = { exports: {} };
var reactJsxRuntime_production_min = {};
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactJsxRuntime_production_min;
function requireReactJsxRuntime_production_min() {
if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min;
hasRequiredReactJsxRuntime_production_min = 1;
var f = requireReact(),
k = Symbol.for("react.element"),
l = Symbol.for("react.fragment"),
m = Object.prototype.hasOwnProperty,
n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,
p = { key: true, ref: true, __self: true, __source: true };
function q(c, a, g) {
var b,
d = {},
e = null,
h = null;
void 0 !== g && (e = "" + g);
void 0 !== a.key && (e = "" + a.key);
void 0 !== a.ref && (h = a.ref);
for (b in a) m.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);
if (c && c.defaultProps) for (b in ((a = c.defaultProps), a)) void 0 === d[b] && (d[b] = a[b]);
return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current };
}
reactJsxRuntime_production_min.Fragment = l;
reactJsxRuntime_production_min.jsx = q;
reactJsxRuntime_production_min.jsxs = q;
return reactJsxRuntime_production_min;
}
var hasRequiredJsxRuntime;
function requireJsxRuntime() {
if (hasRequiredJsxRuntime) return jsxRuntime.exports;
hasRequiredJsxRuntime = 1;
{
jsxRuntime.exports = requireReactJsxRuntime_production_min();
}
return jsxRuntime.exports;
}
var jsxRuntimeExports = requireJsxRuntime();
export { jsxRuntimeExports as j };

File diff suppressed because one or more lines are too long

2890
web/dist/assets/module-CfopjtOo.js vendored Normal file

File diff suppressed because one or more lines are too long

78
web/dist/assets/native-DB2Z31Rx.js vendored Normal file

File diff suppressed because one or more lines are too long

84
web/dist/assets/remoteEntry.js vendored Normal file
View File

@ -0,0 +1,84 @@
const currentImports = {};
const exportSet = new Set(["Module", "__esModule", "default", "_export_sfc"]);
let moduleMap = {
"./editor": () => {
dynamicLoadingCss(["style-Bs2zy9jQ.css"], false, "./editor");
return __federation_import("./__federation_expose_Editor-5Sa5G2Fp.js").then((module) => (Object.keys(module).every((item) => exportSet.has(item)) ? () => module.default : () => module));
},
};
const seen = {};
const dynamicLoadingCss = (cssFilePaths, dontAppendStylesToHead, exposeItemName) => {
const metaUrl = import.meta.url;
if (typeof metaUrl === "undefined") {
console.warn('The remote style takes effect only when the build.target option in the vite.config.ts file is higher than that of "es2020".');
return;
}
const curUrl = metaUrl.substring(0, metaUrl.lastIndexOf("remoteEntry.js"));
const base = "/";
("assets");
cssFilePaths.forEach((cssPath) => {
let href = "";
const baseUrl = base || curUrl;
if (baseUrl) {
const trimmer = {
trailing: (path) => (path.endsWith("/") ? path.slice(0, -1) : path),
leading: (path) => (path.startsWith("/") ? path.slice(1) : path),
};
const isAbsoluteUrl = (url) => url.startsWith("http") || url.startsWith("//");
const cleanBaseUrl = trimmer.trailing(baseUrl);
const cleanCssPath = trimmer.leading(cssPath);
const cleanCurUrl = trimmer.trailing(curUrl);
if (isAbsoluteUrl(baseUrl)) {
href = [cleanBaseUrl, cleanCssPath].filter(Boolean).join("/");
} else {
if (cleanCurUrl.includes(cleanBaseUrl)) {
href = [cleanCurUrl, cleanCssPath].filter(Boolean).join("/");
} else {
href = [cleanCurUrl + cleanBaseUrl, cleanCssPath].filter(Boolean).join("/");
}
}
} else {
href = cssPath;
}
if (dontAppendStylesToHead) {
const key = "css__smartblock__" + exposeItemName;
window[key] = window[key] || [];
window[key].push(href);
return;
}
if (href in seen) return;
seen[href] = true;
const element = document.createElement("link");
element.rel = "stylesheet";
element.href = href;
document.head.appendChild(element);
});
};
async function __federation_import(name) {
currentImports[name] ??= import(name);
return currentImports[name];
}
const get = (module) => {
if (!moduleMap[module]) throw new Error("Can not find remote module " + module);
return moduleMap[module]();
};
const init = (shareScope) => {
globalThis.__federation_shared__ = globalThis.__federation_shared__ || {};
Object.entries(shareScope).forEach(([key, value]) => {
for (const [versionKey, versionValue] of Object.entries(value)) {
const scope = versionValue.scope || "default";
globalThis.__federation_shared__[scope] = globalThis.__federation_shared__[scope] || {};
const shared = globalThis.__federation_shared__[scope];
(shared[key] = shared[key] || {})[versionKey] = versionValue;
}
});
};
export { dynamicLoadingCss, get, init };

1028
web/dist/assets/style-Bs2zy9jQ.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
import { r as requireReact } from "./index-DQGM2Mpm.js";
import { X as requireShim } from "./index-Bs--Ol2m.js";
var withSelector = { exports: {} };
var withSelector_production = {};
/**
* @license React
* use-sync-external-store-shim/with-selector.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredWithSelector_production;
function requireWithSelector_production() {
if (hasRequiredWithSelector_production) return withSelector_production;
hasRequiredWithSelector_production = 1;
var React = requireReact(),
shim = requireShim();
function is(x, y) {
return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);
}
var objectIs = "function" === typeof Object.is ? Object.is : is,
useSyncExternalStore = shim.useSyncExternalStore,
useRef = React.useRef,
useEffect = React.useEffect,
useMemo = React.useMemo,
useDebugValue = React.useDebugValue;
withSelector_production.useSyncExternalStoreWithSelector = function (subscribe, getSnapshot, getServerSnapshot, selector, isEqual) {
var instRef = useRef(null);
if (null === instRef.current) {
var inst = { hasValue: false, value: null };
instRef.current = inst;
} else inst = instRef.current;
instRef = useMemo(
function () {
function memoizedSelector(nextSnapshot) {
if (!hasMemo) {
hasMemo = true;
memoizedSnapshot = nextSnapshot;
nextSnapshot = selector(nextSnapshot);
if (void 0 !== isEqual && inst.hasValue) {
var currentSelection = inst.value;
if (isEqual(currentSelection, nextSnapshot)) return (memoizedSelection = currentSelection);
}
return (memoizedSelection = nextSnapshot);
}
currentSelection = memoizedSelection;
if (objectIs(memoizedSnapshot, nextSnapshot)) return currentSelection;
var nextSelection = selector(nextSnapshot);
if (void 0 !== isEqual && isEqual(currentSelection, nextSelection)) return ((memoizedSnapshot = nextSnapshot), currentSelection);
memoizedSnapshot = nextSnapshot;
return (memoizedSelection = nextSelection);
}
var hasMemo = false,
memoizedSnapshot,
memoizedSelection,
maybeGetServerSnapshot = void 0 === getServerSnapshot ? null : getServerSnapshot;
return [
function () {
return memoizedSelector(getSnapshot());
},
null === maybeGetServerSnapshot ? void 0 : (
function () {
return memoizedSelector(maybeGetServerSnapshot());
}
),
];
},
[getSnapshot, getServerSnapshot, selector, isEqual],
);
var value = useSyncExternalStore(subscribe, instRef[0], instRef[1]);
useEffect(
function () {
inst.hasValue = true;
inst.value = value;
},
[value],
);
useDebugValue(value);
return value;
};
return withSelector_production;
}
var hasRequiredWithSelector;
function requireWithSelector() {
if (hasRequiredWithSelector) return withSelector.exports;
hasRequiredWithSelector = 1;
{
withSelector.exports = requireWithSelector_production();
}
return withSelector.exports;
}
var withSelectorExports = requireWithSelector();
export { withSelectorExports as w };

10
web/dist/index.html vendored Normal file
View File

@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>SmartBlock Plugin</title>
<link rel="stylesheet" crossorigin href="/assets/style-Bs2zy9jQ.css" />
</head>
<body>
<!-- This is a placeholder for Module Federation remote entry -->
</body>
</html>

408
web/editor.tsx Normal file
View File

@ -0,0 +1,408 @@
/**
* Smart Block Editor
*
* AI-powered block editor that generates content structure and HTML templates
* from natural language prompts.
*/
import React, { useState, useCallback, useMemo } from "react";
import {
// Types
type BlockEditorProps,
type Generation,
type SmartBlockMeta,
// Components
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
ScrollArea,
Button,
Input,
Textarea,
Label,
Badge,
Monaco,
Loader2,
Sparkles,
Check,
Code,
History,
Wand2,
Send,
Edit3,
// Hooks
useAIGenerate,
useAIAssist,
} from "@block-ninja/ui";
function SmartBlockEditor({ content, onChange }: BlockEditorProps) {
const [activeTab, setActiveTab] = useState("generate");
const [prompt, setPrompt] = useState("");
const [assistPrompt, setAssistPrompt] = useState("");
const [lastExplanation, setLastExplanation] = useState("");
const { generate, isLoading: isGenerating } = useAIGenerate();
const { assist, isLoading: isAssisting } = useAIAssist();
// Extract meta and current generation
const meta = useMemo<SmartBlockMeta>(() => {
return (content._meta as SmartBlockMeta) ?? { currentGenerationId: "", generations: [] };
}, [content._meta]);
const currentGeneration = useMemo(() => meta.generations?.find((g: Generation) => g.id === meta.currentGenerationId), [meta]);
const currentTemplate = currentGeneration?.template || "";
// Get content fields (excluding _meta)
const contentFields = useMemo(() => {
const fields: Record<string, unknown> = {};
for (const [key, value] of Object.entries(content)) {
if (!key.startsWith("_")) {
fields[key] = value;
}
}
return fields;
}, [content]);
// Handle generate
const handleGenerate = useCallback(async () => {
if (!prompt.trim()) return;
try {
const result = await generate(prompt, currentTemplate);
const newGeneration: Generation = {
id: result.generationId,
prompt: prompt,
schema: result.schema,
template: result.template,
createdAt: new Date().toISOString(),
};
const updatedMeta: SmartBlockMeta = {
currentGenerationId: result.generationId,
generations: [...(meta.generations || []), newGeneration],
};
// Clear old content fields and add new schema defaults
const newContent: Record<string, unknown> = { _meta: updatedMeta };
if (result.schema?.properties) {
for (const key of Object.keys(result.schema.properties as Record<string, unknown>)) {
newContent[key] = "";
}
}
onChange(newContent);
setLastExplanation(result.explanation);
setPrompt("");
setActiveTab("content");
} catch (error) {
console.error("Generation failed:", error);
}
}, [prompt, currentTemplate, meta, generate, onChange]);
// Handle assist
const handleAssist = useCallback(async () => {
if (!assistPrompt.trim() || !currentTemplate) return;
try {
const result = await assist(assistPrompt, currentTemplate, currentGeneration?.schema ? JSON.stringify(currentGeneration.schema) : undefined);
// Update the current generation's template
const updatedGenerations = (meta.generations || []).map((g: Generation) => (g.id === meta.currentGenerationId ? { ...g, template: result.template } : g));
onChange({
...content,
_meta: { ...meta, generations: updatedGenerations },
});
setLastExplanation(result.explanation);
setAssistPrompt("");
} catch (error) {
console.error("Assist failed:", error);
}
}, [assistPrompt, currentTemplate, currentGeneration, meta, content, assist, onChange]);
// Handle template change (direct edit)
const handleTemplateChange = useCallback(
(newTemplate: string | undefined) => {
if (!newTemplate || !currentGeneration) return;
const updatedGenerations = (meta.generations || []).map((g: Generation) => (g.id === meta.currentGenerationId ? { ...g, template: newTemplate } : g));
onChange({
...content,
_meta: { ...meta, generations: updatedGenerations },
});
},
[currentGeneration, meta, content, onChange],
);
// Handle switching generation
const handleSwitchGeneration = useCallback(
(generationId: string) => {
const generation = meta.generations?.find((g: Generation) => g.id === generationId);
if (!generation) return;
// Clear current content and set defaults from new schema
const newContent: Record<string, unknown> = {
_meta: { ...meta, currentGenerationId: generationId },
};
if (generation.schema?.properties) {
for (const key of Object.keys(generation.schema.properties as Record<string, unknown>)) {
newContent[key] = content[key] ?? "";
}
}
onChange(newContent);
setActiveTab("content");
},
[meta, content, onChange],
);
// Handle content field change
const handleFieldChange = useCallback(
(fieldName: string, value: unknown) => {
onChange({ ...content, [fieldName]: value });
},
[content, onChange],
);
// Render content fields based on schema
const renderContentFields = () => {
const schema = currentGeneration?.schema as Record<string, unknown> | undefined;
if (!schema?.properties) {
return (
<div className="text-center py-8 text-muted-foreground">
<p>Generate content structure first</p>
</div>
);
}
const properties = Object.entries(schema.properties as Record<string, unknown>);
const requiredFields = new Set((schema.required as string[]) || []);
return (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">Content Fields</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{properties.map(([name, prop]) => {
const propObj = prop as Record<string, unknown>;
const isRequired = requiredFields.has(name);
const editor = (propObj["x-editor"] as string) || "text";
let inputType = "text";
if (editor === "number") {
inputType = "number";
} else if (editor === "url") {
inputType = "url";
}
return (
<div key={name} className="space-y-2">
<Label htmlFor={name}>
{(propObj.title as string) || (propObj.description as string) || name}
{isRequired && <span className="text-destructive ml-1">*</span>}
</Label>
{editor === "textarea" ?
<Textarea
id={name}
value={(contentFields[name] as string) || ""}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => handleFieldChange(name, e.target.value)}
placeholder={propObj.description as string}
rows={4}
/>
: <Input
id={name}
type={inputType}
value={(contentFields[name] as string) || ""}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleFieldChange(name, e.target.value)}
placeholder={propObj.description as string}
/>
}
</div>
);
})}
</CardContent>
</Card>
);
};
return (
<div className="space-y-4">
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="generate" className="gap-2">
<Wand2 className="h-4 w-4" />
Generate
</TabsTrigger>
<TabsTrigger value="content" className="gap-2" disabled={!currentGeneration}>
<Edit3 className="h-4 w-4" />
Content
</TabsTrigger>
<TabsTrigger value="html" className="gap-2" disabled={!currentGeneration}>
<Code className="h-4 w-4" />
HTML
</TabsTrigger>
<TabsTrigger value="history" className="gap-2" disabled={(meta.generations?.length || 0) === 0}>
<History className="h-4 w-4" />
History
</TabsTrigger>
</TabsList>
{/* Generate Tab */}
<TabsContent value="generate" className="space-y-4">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Sparkles className="h-5 w-5 text-primary" />
Generate Content Structure
</CardTitle>
<CardDescription>Describe the content you want to create. AI will generate both the form fields and HTML template.</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Textarea
placeholder="E.g., Create a testimonial card with author photo, quote, name, title, and 5-star rating..."
value={prompt}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setPrompt(e.target.value)}
className="min-h-[100px]"
/>
<div className="flex justify-between items-center">
<p className="text-sm text-muted-foreground">{currentGeneration ? "Generating will create a new version" : "Start by describing your content"}</p>
<Button action="create" entity="smart-block-generation" onClick={handleGenerate} disabled={isGenerating || !prompt.trim()}>
{isGenerating ?
<>
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
Generating...
</>
: <>
<Wand2 className="h-4 w-4 mr-2" />
Generate
</>
}
</Button>
</div>
</CardContent>
</Card>
{lastExplanation && (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">AI Response</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">{lastExplanation}</p>
</CardContent>
</Card>
)}
{!currentGeneration && (
<div className="text-center py-8 text-muted-foreground">
<Wand2 className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p>No content structure generated yet</p>
<p className="text-sm">Enter a prompt above to get started</p>
</div>
)}
</TabsContent>
{/* Content Tab */}
<TabsContent value="content">{renderContentFields()}</TabsContent>
{/* HTML Tab */}
<TabsContent value="html" className="space-y-4">
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm flex items-center justify-between">
HTML Template
<Badge variant="outline">{Object.keys(((currentGeneration?.schema as Record<string, unknown>)?.properties as Record<string, unknown>) || {}).length} fields</Badge>
</CardTitle>
</CardHeader>
<CardContent>
<Monaco value={currentTemplate} onChange={handleTemplateChange} language="html" height="300px" theme="vs-dark" />
<p className="text-xs text-muted-foreground mt-2">Use {"{{fieldName}}"} placeholders to insert content values</p>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm flex items-center gap-2">
<Sparkles className="h-4 w-4" />
AI Assistant
</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
<div className="flex gap-2">
<Input
placeholder="E.g., Make the quote italic and add a shadow..."
value={assistPrompt}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAssistPrompt(e.target.value)}
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleAssist();
}
}}
/>
<Button action="edit" entity="smart-block-template" onClick={handleAssist} disabled={isAssisting || !assistPrompt.trim()} size="icon">
{isAssisting ?
<Loader2 className="h-4 w-4 animate-spin" />
: <Send className="h-4 w-4" />}
</Button>
</div>
</CardContent>
</Card>
</TabsContent>
{/* History Tab */}
<TabsContent value="history">
<Card>
<CardHeader>
<CardTitle className="text-sm">Generation History</CardTitle>
<CardDescription>Switch between previous generations or regenerate from a prompt</CardDescription>
</CardHeader>
<CardContent>
<ScrollArea className="h-[300px]">
<div className="space-y-2">
{[...(meta.generations || [])].reverse().map((gen: Generation) => (
<Button
key={gen.id}
type="button"
variant="ghost"
action="select"
entity="smart-block-generation"
className={`w-full h-auto justify-start p-3 rounded-lg border text-left font-normal whitespace-normal transition-colors hover:text-foreground [&_svg]:size-3 ${
gen.id === meta.currentGenerationId ? "bg-primary/10 border-primary hover:bg-primary/10" : "hover:bg-muted"
}`}
onClick={() => handleSwitchGeneration(gen.id)}>
<div className="w-full flex items-start justify-between gap-2">
<div className="flex-1 min-w-0">
<p className="text-sm font-medium truncate">{gen.prompt}</p>
<p className="text-xs text-muted-foreground">{new Date(gen.createdAt).toLocaleString()}</p>
</div>
{gen.id === meta.currentGenerationId && (
<Badge variant="default" className="shrink-0">
<Check className="h-3 w-3 mr-1" />
Active
</Badge>
)}
</div>
</Button>
))}
</div>
</ScrollArea>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
);
}
// Export for plugin loader
export default SmartBlockEditor;

3
web/entry.ts Normal file
View File

@ -0,0 +1,3 @@
// Placeholder entry for Module Federation remote
// The actual editor is exposed via "./editor" in the federation config
export {};

3
web/eslint.config.js Normal file
View File

@ -0,0 +1,3 @@
import config from "../../../cms/web/eslint.config.js";
export default config;

10
web/index.html Normal file
View File

@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>SmartBlock Plugin</title>
</head>
<body>
<!-- This is a placeholder for Module Federation remote entry -->
<script type="module" src="./entry.ts"></script>
</body>
</html>

22
web/package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "smartblock-editor",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"build": "vite build",
"dev": "vite build --watch"
},
"dependencies": {
"@block-ninja/ui": "^0.1.0"
},
"devDependencies": {
"@originjs/vite-plugin-federation": "^1.4.1",
"@types/react": "^18.3.28",
"@vitejs/plugin-react": "^4.7.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"typescript": "^5.9.3",
"vite": "^6.4.1"
}
}

4767
web/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

13
web/prettier.config.cjs Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
arrowParens: "always",
bracketSameLine: true,
bracketSpacing: true,
experimentalTernaries: true,
printWidth: 200,
quoteProps: "consistent",
semi: true,
singleQuote: false,
tabWidth: 4,
trailingComma: "all",
useTabs: true,
};

14
web/tsconfig.json Normal file
View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"noEmit": true
},
"include": ["*.tsx", "*.ts"]
}

26
web/vite.config.ts Normal file
View File

@ -0,0 +1,26 @@
import federation from "@originjs/vite-plugin-federation";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
react(),
federation({
name: "smartblock",
filename: "remoteEntry.js",
// Expose the editor component
exposes: {
"./editor": "./editor.tsx",
},
// Use shared dependencies from host
shared: ["react", "react-dom", "@block-ninja/ui"],
}),
],
build: {
modulePreload: false,
target: "esnext",
minify: false,
cssCodeSplit: false,
outDir: "dist",
},
});