@kohryan/moodui 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +53 -2
- package/dist/index.d.mts +26 -3
- package/dist/index.d.ts +26 -3
- package/dist/index.js +229 -34
- package/dist/index.mjs +227 -34
- package/dist/ui/assets/index-C2YFmKz4.js +64 -0
- package/dist/ui/index.html +1 -1
- package/package.json +2 -1
- package/src/ui/main.tsx +831 -7
- package/dist/ui/assets/index-CMk6XQXy.js +0 -58
package/dist/cli.mjs
CHANGED
|
@@ -586,13 +586,19 @@ function validateNode(input, path3) {
|
|
|
586
586
|
else if (!isString(props.src)) errors.push(`${path3}.props.src must be a string`);
|
|
587
587
|
break;
|
|
588
588
|
}
|
|
589
|
+
case "icon": {
|
|
590
|
+
const props = input.props;
|
|
591
|
+
if (!isObject(props)) errors.push(`${path3}.props must be an object`);
|
|
592
|
+
else if (!isString(props.name)) errors.push(`${path3}.props.name must be a string`);
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
589
595
|
case "spacer": {
|
|
590
596
|
const props = input.props;
|
|
591
597
|
if (props != null && !isObject(props)) errors.push(`${path3}.props must be an object if provided`);
|
|
592
598
|
break;
|
|
593
599
|
}
|
|
594
600
|
default:
|
|
595
|
-
errors.push(`${path3}.type must be one of: box | text | button | input | image | spacer`);
|
|
601
|
+
errors.push(`${path3}.type must be one of: box | text | button | input | image | icon | spacer`);
|
|
596
602
|
}
|
|
597
603
|
if (errors.length > 0) return { ok: false, errors };
|
|
598
604
|
return { ok: true, value: input };
|
|
@@ -661,14 +667,20 @@ function buildSystemPrompt() {
|
|
|
661
667
|
"- button: { type:'button', props: { label: string, variant?, actionId?, disabled?, ...common } }",
|
|
662
668
|
"- input: { type:'input', props?: { name?, placeholder?, defaultValue?, ...common } }",
|
|
663
669
|
"- image: { type:'image', props: { src: string, alt?, fit?, ...common } }",
|
|
670
|
+
"- icon: { type:'icon', props: { name, size?, color?, strokeWidth?, ...common } }",
|
|
664
671
|
"- spacer: { type:'spacer', props?: { size? } }",
|
|
665
672
|
"",
|
|
666
673
|
"common props:",
|
|
667
674
|
"- id, testId, className, style(object), padding, margin, background, borderRadius, width, height",
|
|
668
675
|
"",
|
|
676
|
+
"Reusable icon names:",
|
|
677
|
+
"- sparkles, search, settings, user, mail, heart, home, plus, check, arrow-right, calendar, bell, star, chart-bar, message-circle, shield",
|
|
678
|
+
"",
|
|
669
679
|
"Rules:",
|
|
670
680
|
"- root wajib ada",
|
|
671
681
|
"- minimal pakai box sebagai container utama",
|
|
682
|
+
"- gunakan icon node bila UI membutuhkan icon navigasi, status, statistik, CTA, atau dekorasi ringan",
|
|
683
|
+
"- utamakan layout modern: card, section, spacing konsisten, hierarchy yang jelas",
|
|
672
684
|
"- semua string pakai double quotes (JSON standard)",
|
|
673
685
|
"- jangan pakai function / JS expression apa pun"
|
|
674
686
|
].join("\n");
|
|
@@ -712,9 +724,11 @@ function parseFirstJsonObject(text) {
|
|
|
712
724
|
function renderReact(specInput, options = {}) {
|
|
713
725
|
const spec = assertMoodUISpec(specInput);
|
|
714
726
|
const componentName = options.componentName ?? "MoodUIScreen";
|
|
727
|
+
const hasIcons = containsIconNode(spec.root);
|
|
715
728
|
const jsx = renderNode(spec.root, { indent: 2, onActionProp: "onAction" });
|
|
716
729
|
return [
|
|
717
730
|
'import * as React from "react";',
|
|
731
|
+
hasIcons ? 'import { MoodUIIcon } from "@kohryan/moodui";' : "",
|
|
718
732
|
"",
|
|
719
733
|
"export type MoodUIScreenActionHandler = (actionId: string) => void;",
|
|
720
734
|
"",
|
|
@@ -759,6 +773,8 @@ function renderNode(node, ctx) {
|
|
|
759
773
|
return renderSelfClosingElement("input", node, ctx);
|
|
760
774
|
case "image":
|
|
761
775
|
return renderSelfClosingElement("img", node, ctx);
|
|
776
|
+
case "icon":
|
|
777
|
+
return renderSelfClosingElement("MoodUIIcon", node, ctx);
|
|
762
778
|
case "spacer": {
|
|
763
779
|
const size = node.props?.size ?? 8;
|
|
764
780
|
const style = { width: normalizeCssValue(size), height: normalizeCssValue(size) };
|
|
@@ -805,6 +821,12 @@ function buildProps(tag, node, extraProps) {
|
|
|
805
821
|
if (props.alt) out.push(`alt=${serializeJsxAttrValue(props.alt)}`);
|
|
806
822
|
if (props.fit) mergedStyle.objectFit = props.fit;
|
|
807
823
|
}
|
|
824
|
+
if (tag === "MoodUIIcon") {
|
|
825
|
+
if (props.name) out.push(`name=${serializeJsxAttrValue(props.name)}`);
|
|
826
|
+
if (props.size != null) out.push(`size={${serializeJsValue(props.size)}}`);
|
|
827
|
+
if (props.color) out.push(`color=${serializeJsxAttrValue(props.color)}`);
|
|
828
|
+
if (props.strokeWidth != null) out.push(`strokeWidth={${serializeJsValue(props.strokeWidth)}}`);
|
|
829
|
+
}
|
|
808
830
|
if (Object.keys(mergedStyle).length > 0) {
|
|
809
831
|
out.push(`style={(${serializeJsValue(mergedStyle)} as React.CSSProperties)}`);
|
|
810
832
|
}
|
|
@@ -934,6 +956,11 @@ function safeObjectKey(key) {
|
|
|
934
956
|
function escapeText(value) {
|
|
935
957
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
936
958
|
}
|
|
959
|
+
function containsIconNode(node) {
|
|
960
|
+
if (node.type === "icon") return true;
|
|
961
|
+
if (node.type !== "box") return false;
|
|
962
|
+
return (node.children ?? []).some((child) => containsIconNode(child));
|
|
963
|
+
}
|
|
937
964
|
function indent(spaces) {
|
|
938
965
|
return " ".repeat(spaces);
|
|
939
966
|
}
|
|
@@ -1164,8 +1191,32 @@ async function runUI() {
|
|
|
1164
1191
|
uiDir = path2.join(__dirname2, "..", "dist", "ui");
|
|
1165
1192
|
}
|
|
1166
1193
|
const PORT = 3e3;
|
|
1167
|
-
|
|
1194
|
+
const workingDir = process.cwd();
|
|
1195
|
+
console.log(`Starting MoodUI UI at http://localhost:${PORT}`);
|
|
1196
|
+
console.log(`Working directory: ${workingDir}`);
|
|
1168
1197
|
const server = http.createServer(async (req, res) => {
|
|
1198
|
+
if (req.method === "POST" && req.url === "/api/save-file") {
|
|
1199
|
+
try {
|
|
1200
|
+
let body = "";
|
|
1201
|
+
for await (const chunk of req) {
|
|
1202
|
+
body += chunk.toString();
|
|
1203
|
+
}
|
|
1204
|
+
const { path: filePath2, code } = JSON.parse(body);
|
|
1205
|
+
const resolvedOut = path2.resolve(workingDir, filePath2);
|
|
1206
|
+
await fs6.mkdir(path2.dirname(resolvedOut), { recursive: true });
|
|
1207
|
+
await fs6.writeFile(resolvedOut, code, "utf8");
|
|
1208
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1209
|
+
res.end(JSON.stringify({ success: true, path: resolvedOut }));
|
|
1210
|
+
console.log(`OK: wrote ${resolvedOut}`);
|
|
1211
|
+
return;
|
|
1212
|
+
} catch (err) {
|
|
1213
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1214
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1215
|
+
res.end(JSON.stringify({ success: false, error: errorMsg }));
|
|
1216
|
+
console.error(`Error saving file: ${errorMsg}`);
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1169
1220
|
let filePath = path2.join(uiDir, req.url === "/" ? "index.html" : req.url);
|
|
1170
1221
|
const extname = String(path2.extname(filePath)).toLowerCase();
|
|
1171
1222
|
const mimeTypes = {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
2
3
|
|
|
3
4
|
type MoodUISpecVersion = 1;
|
|
4
5
|
type MoodUISpec = {
|
|
@@ -37,7 +38,7 @@ type MoodUISpace = number | string | {
|
|
|
37
38
|
};
|
|
38
39
|
type MoodUIColor = string;
|
|
39
40
|
type MoodUIRadius = number | string;
|
|
40
|
-
type MoodUINode = MoodUIBoxNode | MoodUITextNode | MoodUIButtonNode | MoodUIInputNode | MoodUIImageNode | MoodUISpacerNode;
|
|
41
|
+
type MoodUINode = MoodUIBoxNode | MoodUITextNode | MoodUIButtonNode | MoodUIInputNode | MoodUIImageNode | MoodUIIconNode | MoodUISpacerNode;
|
|
41
42
|
type MoodUIBoxNode = {
|
|
42
43
|
type: "box";
|
|
43
44
|
props?: MoodUICommonProps & {
|
|
@@ -85,6 +86,16 @@ type MoodUIImageNode = {
|
|
|
85
86
|
fit?: "cover" | "contain" | "fill" | "none" | "scale-down";
|
|
86
87
|
};
|
|
87
88
|
};
|
|
89
|
+
type MoodUIIconName = "arrow-right" | "bell" | "calendar" | "chart-bar" | "check" | "heart" | "home" | "mail" | "message-circle" | "plus" | "search" | "settings" | "shield" | "sparkles" | "star" | "user";
|
|
90
|
+
type MoodUIIconNode = {
|
|
91
|
+
type: "icon";
|
|
92
|
+
props: MoodUICommonProps & {
|
|
93
|
+
name: MoodUIIconName;
|
|
94
|
+
size?: number | string;
|
|
95
|
+
color?: MoodUIColor;
|
|
96
|
+
strokeWidth?: number;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
88
99
|
type MoodUISpacerNode = {
|
|
89
100
|
type: "spacer";
|
|
90
101
|
props?: {
|
|
@@ -103,7 +114,7 @@ type Err = {
|
|
|
103
114
|
type ValidationResult<T> = Ok<T> | Err;
|
|
104
115
|
declare function validateMoodUISpec(input: unknown): ValidationResult<MoodUISpec>;
|
|
105
116
|
declare function assertMoodUISpec(input: unknown): MoodUISpec;
|
|
106
|
-
type _InternalNodeTypes = MoodUITextNode | MoodUIButtonNode | MoodUIInputNode | MoodUIImageNode | MoodUISpacerNode;
|
|
117
|
+
type _InternalNodeTypes = MoodUITextNode | MoodUIButtonNode | MoodUIIconNode | MoodUIInputNode | MoodUIImageNode | MoodUISpacerNode;
|
|
107
118
|
|
|
108
119
|
type RenderReactOptions = {
|
|
109
120
|
componentName?: string;
|
|
@@ -115,6 +126,18 @@ type _RenderReactExports = {
|
|
|
115
126
|
renderReactJSX: typeof renderReactJSX;
|
|
116
127
|
};
|
|
117
128
|
|
|
129
|
+
type MoodUIIconProps = {
|
|
130
|
+
name: MoodUIIconName;
|
|
131
|
+
size?: number | string;
|
|
132
|
+
color?: string;
|
|
133
|
+
strokeWidth?: number;
|
|
134
|
+
className?: string;
|
|
135
|
+
style?: React.CSSProperties;
|
|
136
|
+
title?: string;
|
|
137
|
+
};
|
|
138
|
+
declare const MOODUI_ICON_NAMES: MoodUIIconName[];
|
|
139
|
+
declare function MoodUIIcon(props: MoodUIIconProps): react_jsx_runtime.JSX.Element;
|
|
140
|
+
|
|
118
141
|
type MoodUIRuntimeProps = {
|
|
119
142
|
spec: MoodUISpec;
|
|
120
143
|
onAction?: (actionId: string) => void;
|
|
@@ -205,4 +228,4 @@ type GenerateReactFromPromptResult = {
|
|
|
205
228
|
};
|
|
206
229
|
declare function generateReactFromPrompt(options: GenerateReactFromPromptOptions): Promise<GenerateReactFromPromptResult>;
|
|
207
230
|
|
|
208
|
-
export { type GeminiClientOptions, type GenerateReactFromPromptOptions, type GenerateReactFromPromptResult, type GenerateSpecOptions, type GenerateSpecResult, type LLMChatClient, type LLMChatRequest, type LLMMessage, type MoodUIBoxNode, type MoodUIButtonNode, type MoodUIColor, type MoodUICommonProps, type MoodUIImageNode, type MoodUIInputNode, type MoodUINode, MoodUIPromptPlayground, type MoodUIPromptPlaygroundProps, type MoodUIRadius, MoodUIRuntime, type MoodUIRuntimeProps, type MoodUISpace, type MoodUISpacerNode, type MoodUISpec, type MoodUISpecVersion, type MoodUITextNode, type MoodUITheme, type OllamaClientOptions, type OpenAICompatibleClientOptions, type RenderReactOptions, type ValidationResult, type _InternalNodeTypes, type _RenderReactExports, assertMoodUISpec, createGeminiClient, createOllamaClient, createOpenAICompatibleClient, generateMoodUISpec, generateReactFromPrompt, renderReact, renderReactJSX, validateMoodUISpec };
|
|
231
|
+
export { type GeminiClientOptions, type GenerateReactFromPromptOptions, type GenerateReactFromPromptResult, type GenerateSpecOptions, type GenerateSpecResult, type LLMChatClient, type LLMChatRequest, type LLMMessage, MOODUI_ICON_NAMES, type MoodUIBoxNode, type MoodUIButtonNode, type MoodUIColor, type MoodUICommonProps, MoodUIIcon, type MoodUIIconName, type MoodUIIconNode, type MoodUIIconProps, type MoodUIImageNode, type MoodUIInputNode, type MoodUINode, MoodUIPromptPlayground, type MoodUIPromptPlaygroundProps, type MoodUIRadius, MoodUIRuntime, type MoodUIRuntimeProps, type MoodUISpace, type MoodUISpacerNode, type MoodUISpec, type MoodUISpecVersion, type MoodUITextNode, type MoodUITheme, type OllamaClientOptions, type OpenAICompatibleClientOptions, type RenderReactOptions, type ValidationResult, type _InternalNodeTypes, type _RenderReactExports, assertMoodUISpec, createGeminiClient, createOllamaClient, createOpenAICompatibleClient, generateMoodUISpec, generateReactFromPrompt, renderReact, renderReactJSX, validateMoodUISpec };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
2
3
|
|
|
3
4
|
type MoodUISpecVersion = 1;
|
|
4
5
|
type MoodUISpec = {
|
|
@@ -37,7 +38,7 @@ type MoodUISpace = number | string | {
|
|
|
37
38
|
};
|
|
38
39
|
type MoodUIColor = string;
|
|
39
40
|
type MoodUIRadius = number | string;
|
|
40
|
-
type MoodUINode = MoodUIBoxNode | MoodUITextNode | MoodUIButtonNode | MoodUIInputNode | MoodUIImageNode | MoodUISpacerNode;
|
|
41
|
+
type MoodUINode = MoodUIBoxNode | MoodUITextNode | MoodUIButtonNode | MoodUIInputNode | MoodUIImageNode | MoodUIIconNode | MoodUISpacerNode;
|
|
41
42
|
type MoodUIBoxNode = {
|
|
42
43
|
type: "box";
|
|
43
44
|
props?: MoodUICommonProps & {
|
|
@@ -85,6 +86,16 @@ type MoodUIImageNode = {
|
|
|
85
86
|
fit?: "cover" | "contain" | "fill" | "none" | "scale-down";
|
|
86
87
|
};
|
|
87
88
|
};
|
|
89
|
+
type MoodUIIconName = "arrow-right" | "bell" | "calendar" | "chart-bar" | "check" | "heart" | "home" | "mail" | "message-circle" | "plus" | "search" | "settings" | "shield" | "sparkles" | "star" | "user";
|
|
90
|
+
type MoodUIIconNode = {
|
|
91
|
+
type: "icon";
|
|
92
|
+
props: MoodUICommonProps & {
|
|
93
|
+
name: MoodUIIconName;
|
|
94
|
+
size?: number | string;
|
|
95
|
+
color?: MoodUIColor;
|
|
96
|
+
strokeWidth?: number;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
88
99
|
type MoodUISpacerNode = {
|
|
89
100
|
type: "spacer";
|
|
90
101
|
props?: {
|
|
@@ -103,7 +114,7 @@ type Err = {
|
|
|
103
114
|
type ValidationResult<T> = Ok<T> | Err;
|
|
104
115
|
declare function validateMoodUISpec(input: unknown): ValidationResult<MoodUISpec>;
|
|
105
116
|
declare function assertMoodUISpec(input: unknown): MoodUISpec;
|
|
106
|
-
type _InternalNodeTypes = MoodUITextNode | MoodUIButtonNode | MoodUIInputNode | MoodUIImageNode | MoodUISpacerNode;
|
|
117
|
+
type _InternalNodeTypes = MoodUITextNode | MoodUIButtonNode | MoodUIIconNode | MoodUIInputNode | MoodUIImageNode | MoodUISpacerNode;
|
|
107
118
|
|
|
108
119
|
type RenderReactOptions = {
|
|
109
120
|
componentName?: string;
|
|
@@ -115,6 +126,18 @@ type _RenderReactExports = {
|
|
|
115
126
|
renderReactJSX: typeof renderReactJSX;
|
|
116
127
|
};
|
|
117
128
|
|
|
129
|
+
type MoodUIIconProps = {
|
|
130
|
+
name: MoodUIIconName;
|
|
131
|
+
size?: number | string;
|
|
132
|
+
color?: string;
|
|
133
|
+
strokeWidth?: number;
|
|
134
|
+
className?: string;
|
|
135
|
+
style?: React.CSSProperties;
|
|
136
|
+
title?: string;
|
|
137
|
+
};
|
|
138
|
+
declare const MOODUI_ICON_NAMES: MoodUIIconName[];
|
|
139
|
+
declare function MoodUIIcon(props: MoodUIIconProps): react_jsx_runtime.JSX.Element;
|
|
140
|
+
|
|
118
141
|
type MoodUIRuntimeProps = {
|
|
119
142
|
spec: MoodUISpec;
|
|
120
143
|
onAction?: (actionId: string) => void;
|
|
@@ -205,4 +228,4 @@ type GenerateReactFromPromptResult = {
|
|
|
205
228
|
};
|
|
206
229
|
declare function generateReactFromPrompt(options: GenerateReactFromPromptOptions): Promise<GenerateReactFromPromptResult>;
|
|
207
230
|
|
|
208
|
-
export { type GeminiClientOptions, type GenerateReactFromPromptOptions, type GenerateReactFromPromptResult, type GenerateSpecOptions, type GenerateSpecResult, type LLMChatClient, type LLMChatRequest, type LLMMessage, type MoodUIBoxNode, type MoodUIButtonNode, type MoodUIColor, type MoodUICommonProps, type MoodUIImageNode, type MoodUIInputNode, type MoodUINode, MoodUIPromptPlayground, type MoodUIPromptPlaygroundProps, type MoodUIRadius, MoodUIRuntime, type MoodUIRuntimeProps, type MoodUISpace, type MoodUISpacerNode, type MoodUISpec, type MoodUISpecVersion, type MoodUITextNode, type MoodUITheme, type OllamaClientOptions, type OpenAICompatibleClientOptions, type RenderReactOptions, type ValidationResult, type _InternalNodeTypes, type _RenderReactExports, assertMoodUISpec, createGeminiClient, createOllamaClient, createOpenAICompatibleClient, generateMoodUISpec, generateReactFromPrompt, renderReact, renderReactJSX, validateMoodUISpec };
|
|
231
|
+
export { type GeminiClientOptions, type GenerateReactFromPromptOptions, type GenerateReactFromPromptResult, type GenerateSpecOptions, type GenerateSpecResult, type LLMChatClient, type LLMChatRequest, type LLMMessage, MOODUI_ICON_NAMES, type MoodUIBoxNode, type MoodUIButtonNode, type MoodUIColor, type MoodUICommonProps, MoodUIIcon, type MoodUIIconName, type MoodUIIconNode, type MoodUIIconProps, type MoodUIImageNode, type MoodUIInputNode, type MoodUINode, MoodUIPromptPlayground, type MoodUIPromptPlaygroundProps, type MoodUIRadius, MoodUIRuntime, type MoodUIRuntimeProps, type MoodUISpace, type MoodUISpacerNode, type MoodUISpec, type MoodUISpecVersion, type MoodUITextNode, type MoodUITheme, type OllamaClientOptions, type OpenAICompatibleClientOptions, type RenderReactOptions, type ValidationResult, type _InternalNodeTypes, type _RenderReactExports, assertMoodUISpec, createGeminiClient, createOllamaClient, createOpenAICompatibleClient, generateMoodUISpec, generateReactFromPrompt, renderReact, renderReactJSX, validateMoodUISpec };
|