cyrus-studio 1.0.5 → 1.0.7

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.
Files changed (38) hide show
  1. package/README.md +107 -65
  2. package/dist/index.cjs +121 -0
  3. package/dist/index.d.ts +52 -0
  4. package/dist/index.js +41 -0
  5. package/dist/index.mjs +91 -0
  6. package/dist/protocol.d.ts +35 -0
  7. package/dist/protocol.js +31 -0
  8. package/dist/src/index.d.ts +1176 -0
  9. package/dist/src/index.js +41 -0
  10. package/package.json +34 -50
  11. package/dist/assets/AuthDialog-B03ebZW3.js +0 -1
  12. package/dist/assets/CanvaSaveModal---DLe6sk.js +0 -1
  13. package/dist/assets/CodingWorkspace-Bp3Xn7dV.js +0 -6
  14. package/dist/assets/CreateAppDialog-aeo418v9.js +0 -1236
  15. package/dist/assets/CreateProjectDialog-osNHeVX4.js +0 -1
  16. package/dist/assets/HistoryTimelineDialog-DPbIRdCf.js +0 -1
  17. package/dist/assets/ImportModal-69EavD-n.js +0 -83
  18. package/dist/assets/KeyboardShortcutsDialog-CG2dNBJ-.js +0 -1
  19. package/dist/assets/LiveRuntimeWorkspace-Bz13Ad0r.js +0 -1
  20. package/dist/assets/ShareExportDialog-DMSzEbpj.js +0 -1
  21. package/dist/assets/UserProfileDialog-BcUs54wd.js +0 -1
  22. package/dist/assets/WelcomeOnboardingModal-CYrLzJ0Y.js +0 -1
  23. package/dist/assets/index-COkFr0Rf.css +0 -2
  24. package/dist/assets/index-DvPq7fF5.js +0 -793
  25. package/dist/assets/reactTsGenerator-a0llqmE2.js +0 -1330
  26. package/dist/assets/rolldown-runtime-W7wSyTde.js +0 -1
  27. package/dist/assets/themes-Cn_1tMt7.js +0 -1
  28. package/dist/assets/typechecker-BbGTunMD.js +0 -2
  29. package/dist/assets/vendor-firebase-DNMzpOGz.js +0 -32
  30. package/dist/assets/vendor-icons-M_KBtuE-.js +0 -13
  31. package/dist/assets/vendor-jszip-Cv4gVObP.js +0 -2
  32. package/dist/assets/vendor-react-BTEcvIpc.js +0 -227
  33. package/dist/assets/vendor-utils-3OgY5Lif.js +0 -1
  34. package/dist/favicon.svg +0 -1
  35. package/dist/icons.svg +0 -24
  36. package/dist/index.html +0 -50
  37. package/dist/login-bg.png +0 -0
  38. package/dist/test-sdk-sandbox.html +0 -125
package/README.md CHANGED
@@ -1,113 +1,155 @@
1
- # Cyrus Studio — AI Visual Product Builder & Multi-Framework Code Generator
1
+ # cyrus-studio
2
2
 
3
3
  <p align="center">
4
4
  <a href="https://cyrustudio.vercel.app"><img src="https://img.shields.io/badge/Live_App-cyrustudio.vercel.app-6366f1?style=for-the-badge&logo=vercel&logoColor=white" alt="Live Demo" /></a>
5
5
  <a href="https://www.npmjs.com/package/cyrus-studio"><img src="https://img.shields.io/npm/v/cyrus-studio?style=for-the-badge&color=blue" alt="NPM Version" /></a>
6
6
  <a href="https://www.npmjs.com/package/cyrus-studio"><img src="https://img.shields.io/npm/dm/cyrus-studio?style=for-the-badge&color=green" alt="NPM Downloads" /></a>
7
- <a href="https://github.com/cyrus-studio/cyrus"><img src="https://img.shields.io/badge/License-MIT-amber?style=for-the-badge" alt="License" /></a>
7
+ <img src="https://img.shields.io/badge/License-MIT-amber?style=for-the-badge" alt="License" />
8
8
  </p>
9
9
 
10
+ > **AI Visual Canvas Studio** — Embed Cyrus in any React app, generate full UIs from prompts, and export clean code for React, Flutter, SwiftUI & HTML.
11
+
12
+ 🚀 **Try the Live App**: **[https://cyrustudio.vercel.app](https://cyrustudio.vercel.app)**
13
+
10
14
  ---
11
15
 
12
- **Cyrus Studio** is a modern visual product builder that lets designers and developers create production-ready user interfaces on a visual canvas, preview them in real time, and export clean, maintainable source code for **React / Next.js**, **Flutter**, **SwiftUI**, and **HTML / Tailwind CSS**.
16
+ ## Install
13
17
 
14
- 🚀 **Try the Live Cloud App**: **[https://cyrustudio.vercel.app](https://cyrustudio.vercel.app)**
18
+ ```bash
19
+ npm install cyrus-studio
20
+ ```
21
+
22
+ > **Peer dependencies required**: `react >= 18` and `react-dom >= 18`
15
23
 
16
24
  ---
17
25
 
18
- ## ✨ Features
26
+ ## Usage
19
27
 
20
- - 🎨 **Visual Canvas & Drag-and-Drop Editor**: Build complex UIs with intuitive positioning, flex layouts, smart alignment guides, multi-selection, and layered hierarchy.
21
- - ⚡ **Multi-Framework Code Generation**:
22
- - ⚛️ **React / Next.js** (Tailwind CSS, TypeScript)
23
- - 📱 **Flutter / Dart** (Widget tree with theme binding)
24
- - 🍎 **SwiftUI** (Native declarative iOS/macOS views)
25
- - 🌐 **Clean HTML5 / CSS** (Semantic layout with Tailwind)
26
- - 🤖 **AI Design & Code Copilot**: Integrate OpenAI, Google Gemini, Anthropic Claude, or local Ollama models to generate layouts, copy, and components via natural language.
27
- - 👥 **Real-Time Multiplayer Collaboration**: Multiplayer room with live cursor tracking, conflict resolution, and commenting.
28
- - 📐 **Responsive Viewport Controls**: Instant switching between Desktop (1440px), Tablet (768px), and Mobile (390px) viewports with auto-scaling.
29
- - 🌗 **Design Systems & Theme Engine**: Global color palettes, typography tokens, component instances, and dynamic light/dark mode preview.
30
- - 📦 **One-Click Export**: Download structured ZIP bundles, export individual page source files, or save high-resolution PNG/PDF artboards.
28
+ ### 1. Embed the Cyrus Canvas in your React App
31
29
 
32
- ---
30
+ ```tsx
31
+ import { CyrusCanvasIframe } from 'cyrus-studio';
33
32
 
34
- ## 🚀 Quick Start
33
+ export default function App() {
34
+ return (
35
+ <CyrusCanvasIframe
36
+ hostUrl="https://cyrustudio.vercel.app"
37
+ projectId="my-project"
38
+ theme="dark"
39
+ style={{ height: '800px' }}
40
+ onStateChange={(state) => console.log('Canvas state:', state)}
41
+ />
42
+ );
43
+ }
44
+ ```
35
45
 
36
- ### Option 1: Use the Live Web App (No Setup Required)
37
- Open **[https://cyrustudio.vercel.app](https://cyrustudio.vercel.app)** in your browser and start designing immediately.
46
+ ### 2. Generate an Embed URL Programmatically
38
47
 
39
- ### Option 2: Run Locally via Git / NPM
48
+ ```ts
49
+ import { createCyrusEmbedUrl } from 'cyrus-studio';
40
50
 
41
- ```bash
42
- # 1. Clone the repository
43
- git clone https://github.com/cyrus-studio/cyrus.git
44
- cd cyrus
51
+ const url = createCyrusEmbedUrl({
52
+ hostUrl: 'https://cyrustudio.vercel.app',
53
+ projectId: 'landing-v2',
54
+ theme: 'light',
55
+ readOnly: true,
56
+ });
57
+
58
+ console.log(url);
59
+ // https://cyrustudio.vercel.app/#/embed/landing-v2?theme=light&readOnly=true
60
+ ```
61
+
62
+ ### 3. Use the Cyrus IR (Intermediate Representation) Protocol
63
+
64
+ The Cyrus IR is a universal JSON schema for AI-generated UIs. Use it to pass designs between your AI pipeline and the Cyrus canvas.
65
+
66
+ ```ts
67
+ import {
68
+ validateAndRepairCyrusIR,
69
+ getCyrusLLMSystemPrompt,
70
+ type CyrusIRDocument,
71
+ } from 'cyrus-studio';
45
72
 
46
- # 2. Install dependencies
47
- npm install
73
+ // Get the system prompt to paste into your LLM
74
+ const systemPrompt = getCyrusLLMSystemPrompt();
48
75
 
49
- # 3. Start development server
50
- npm run dev
76
+ // Validate and repair raw LLM output
77
+ const rawAiOutput = `{ "version": "1.0", "projectTitle": "My App", "pages": [...] }`;
78
+ const doc: CyrusIRDocument = validateAndRepairCyrusIR(rawAiOutput);
79
+
80
+ console.log(doc.pages[0].elements);
51
81
  ```
52
82
 
53
- Once started, open **`http://localhost:5173`** in your browser.
83
+ ---
84
+
85
+ ## API Reference
86
+
87
+ ### `CyrusCanvasIframe`
54
88
 
55
- - **Vite Web App**: Port `5173`
56
- - **Backend API Gateway**: Port `8787` (`/api` proxy)
89
+ React component that embeds the Cyrus Visual Canvas as an iframe.
90
+
91
+ | Prop | Type | Default | Description |
92
+ |---|---|---|---|
93
+ | `hostUrl` | `string` | `http://localhost:5173` | URL of the Cyrus app |
94
+ | `projectId` | `string` | `"demo"` | Project ID to load in canvas |
95
+ | `theme` | `"light" \| "dark"` | `"light"` | Canvas color theme |
96
+ | `onStateChange` | `(state: any) => void` | — | Callback fired when canvas state changes |
97
+ | `style` | `CSSProperties` | — | Inline styles for the iframe |
98
+ | `className` | `string` | — | CSS class for the iframe |
99
+
100
+ All standard `<iframe>` HTML attributes are also supported.
57
101
 
58
102
  ---
59
103
 
60
- ## ⚙️ Configuration & Environment Variables
104
+ ### `createCyrusEmbedUrl(options)`
61
105
 
62
- Copy `.env.example` to `.env` to configure optional integrations:
106
+ Returns an embeddable URL string.
63
107
 
64
- ```bash
65
- cp .env.example .env
108
+ ```ts
109
+ createCyrusEmbedUrl({
110
+ projectId?: string; // Project to load
111
+ hostUrl?: string; // Cyrus host (default: localhost:5173)
112
+ theme?: 'light' | 'dark';
113
+ readOnly?: boolean;
114
+ })
66
115
  ```
67
116
 
68
- | Variable | Description |
69
- | :--- | :--- |
70
- | `VITE_STRIPE_PUBLISHABLE_KEY` | Stripe Publishable Key for billing checkout. |
71
- | `STRIPE_SECRET_KEY` | Stripe Secret Key for serverless subscription sessions. |
72
- | `GEMINI_API_KEY` | Google Gemini API key for AI generation. |
73
- | `OPENAI_API_KEY` | OpenAI API key for GPT-4o design assistant. |
74
- | `ANTHROPIC_API_KEY` | Anthropic Claude API key. |
75
- | `JWT_SECRET` | Secret string for production session token signing. |
117
+ ---
76
118
 
77
- > **Note**: Cyrus Studio runs fully locally even without external API keys. Features requiring a specific provider will prompt for the key when used.
119
+ ### `validateAndRepairCyrusIR(rawInput)`
78
120
 
79
- ---
121
+ Parses, validates, and repairs raw AI output (string or object) into a valid `CyrusIRDocument`. Safe to call with any LLM response — never throws.
80
122
 
81
- ## 🛠️ Development & Testing Scripts
123
+ ---
82
124
 
83
- ```bash
84
- # Start local development server (Client + Server concurrently)
85
- npm run dev
125
+ ### `getCyrusLLMSystemPrompt()`
86
126
 
87
- # Run TypeScript type checking
88
- npm run typecheck
127
+ Returns the recommended system prompt string to paste into Claude, GPT-4, Gemini, or any other LLM so it outputs valid Cyrus IR JSON.
89
128
 
90
- # Run test suite with Vitest (90+ unit and integration tests)
91
- npm run test
129
+ ---
92
130
 
93
- # Build production bundle
94
- npm run build
131
+ ## TypeScript Types
95
132
 
96
- # Preview production build locally
97
- npm run preview
133
+ ```ts
134
+ import type {
135
+ CyrusIRDocument,
136
+ CyrusIRPage,
137
+ CyrusIRElement,
138
+ CyrusIframeProps,
139
+ } from 'cyrus-studio';
98
140
  ```
99
141
 
100
142
  ---
101
143
 
102
- ## 📦 Tech Stack
144
+ ## CommonJS Support
103
145
 
104
- - **Frontend**: React 19, TypeScript, Tailwind CSS v4, Zustand, Lucide Icons, JSZip
105
- - **Build System**: Vite, PostCSS, Oxlint
106
- - **Backend & APIs**: Node.js, Vercel Serverless Functions, Firebase Auth / Cloud Firestore
107
- - **Testing**: Vitest, React Testing Library
146
+ ```js
147
+ // Works with require() too
148
+ const { CyrusCanvasIframe, createCyrusEmbedUrl } = require('cyrus-studio');
149
+ ```
108
150
 
109
151
  ---
110
152
 
111
- ## 📄 License
153
+ ## License
112
154
 
113
- This project is licensed under the **[MIT License](LICENSE)**.
155
+ MIT © [Cyrus Studio Team](https://cyrustudio.vercel.app)
package/dist/index.cjs ADDED
@@ -0,0 +1,121 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // packages/cyrus-sdk/src/index.ts
30
+ var index_exports = {};
31
+ __export(index_exports, {
32
+ CyrusCanvasIframe: () => CyrusCanvasIframe,
33
+ createCyrusEmbedUrl: () => createCyrusEmbedUrl,
34
+ getCyrusLLMSystemPrompt: () => getCyrusLLMSystemPrompt,
35
+ validateAndRepairCyrusIR: () => validateAndRepairCyrusIR
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+ var import_react = __toESM(require("react"), 1);
39
+
40
+ // packages/cyrus-sdk/src/protocol.ts
41
+ function validateAndRepairCyrusIR(rawInput) {
42
+ let doc = rawInput;
43
+ if (typeof rawInput === "string") {
44
+ try {
45
+ const cleaned = rawInput.replace(/```json\s*/gi, "").replace(/```\s*$/gi, "").trim();
46
+ doc = JSON.parse(cleaned);
47
+ } catch {
48
+ return { version: "1.0", projectTitle: "AI Application", pages: [{ name: "Page 1", elements: [] }] };
49
+ }
50
+ }
51
+ if (!doc || typeof doc !== "object") {
52
+ return { version: "1.0", projectTitle: "AI Application", pages: [{ name: "Page 1", elements: [] }] };
53
+ }
54
+ const pages = Array.isArray(doc.pages) ? doc.pages : [{ name: "Home", elements: doc.elements || [] }];
55
+ return {
56
+ version: "1.0",
57
+ projectTitle: String(doc.projectTitle || doc.name || "AI Application"),
58
+ type: doc.type || "webapp",
59
+ theme: doc.theme || "light",
60
+ palette: doc.palette || { primary: "#0071e3", secondary: "#7d2ae8", background: "#ffffff", surface: "#f5f5f7", text: "#1d1d1f" },
61
+ pages: pages.map((p, idx) => ({
62
+ name: p.name || `Page ${idx + 1}`,
63
+ slug: p.slug || `page-${idx + 1}`,
64
+ elements: Array.isArray(p.elements) ? p.elements : []
65
+ }))
66
+ };
67
+ }
68
+ function getCyrusLLMSystemPrompt() {
69
+ return `Output your designs strictly in the Cyrus IR JSON format:
70
+ {
71
+ "version": "1.0",
72
+ "projectTitle": "Title",
73
+ "type": "webapp",
74
+ "theme": "light",
75
+ "pages": [
76
+ { "name": "Home", "elements": [{ "type": "navbar" }, { "type": "heading" }, { "type": "button" }] }
77
+ ]
78
+ }`;
79
+ }
80
+
81
+ // packages/cyrus-sdk/src/index.ts
82
+ function createCyrusEmbedUrl(options = {}) {
83
+ const host = options.hostUrl || "http://localhost:5173";
84
+ const pid = options.projectId || "demo";
85
+ const theme = options.theme || "light";
86
+ return `${host}/#/embed/${pid}?theme=${theme}&readOnly=${Boolean(options.readOnly)}`;
87
+ }
88
+ function CyrusCanvasIframe({
89
+ projectId = "demo",
90
+ hostUrl = "http://localhost:5173",
91
+ theme = "light",
92
+ onStateChange,
93
+ style,
94
+ className,
95
+ ...rest
96
+ }) {
97
+ const embedUrl = createCyrusEmbedUrl({ projectId, hostUrl, theme });
98
+ import_react.default.useEffect(() => {
99
+ if (!onStateChange || typeof window === "undefined") return;
100
+ const handleMessage = (event) => {
101
+ if (event.data && event.data.type === "CYRUS_STATE_CHANGED") {
102
+ onStateChange(event.data.payload);
103
+ }
104
+ };
105
+ window.addEventListener("message", handleMessage);
106
+ return () => window.removeEventListener("message", handleMessage);
107
+ }, [onStateChange]);
108
+ return import_react.default.createElement("iframe", {
109
+ src: embedUrl,
110
+ className: className || "w-full h-full border-0 rounded-2xl shadow-lg",
111
+ style: {
112
+ width: "100%",
113
+ height: "600px",
114
+ border: "none",
115
+ borderRadius: "16px",
116
+ ...style
117
+ },
118
+ title: "Cyrus Visual Canvas Studio",
119
+ ...rest
120
+ });
121
+ }
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+
3
+ export interface CyrusIRElement {
4
+ id?: string;
5
+ type: string;
6
+ name?: string;
7
+ x?: number;
8
+ y?: number;
9
+ width?: number;
10
+ height?: number;
11
+ styles?: Record<string, any>;
12
+ content?: Record<string, any>;
13
+ children?: CyrusIRElement[];
14
+ }
15
+
16
+ export interface CyrusIRPage {
17
+ id?: string;
18
+ name?: string;
19
+ slug?: string;
20
+ backgroundColor?: string;
21
+ elements: CyrusIRElement[];
22
+ }
23
+
24
+ export interface CyrusIRDocument {
25
+ version: '1.0';
26
+ projectTitle: string;
27
+ type?: 'webapp' | 'website' | 'dashboard' | 'landing' | 'mobile';
28
+ theme?: 'light' | 'dark' | 'system';
29
+ palette?: {
30
+ primary?: string;
31
+ secondary?: string;
32
+ background?: string;
33
+ surface?: string;
34
+ text?: string;
35
+ };
36
+ pages: CyrusIRPage[];
37
+ }
38
+
39
+ export declare function validateAndRepairCyrusIR(rawInput: any): CyrusIRDocument;
40
+ export declare function irToProject(ir: CyrusIRDocument): any;
41
+ export declare function projectToCyrusIR(project: any): CyrusIRDocument;
42
+ export declare function getCyrusLLMSystemPrompt(): string;
43
+ export declare function createCyrusEmbedUrl(options: { projectId?: string; hostUrl?: string; theme?: 'light' | 'dark'; readOnly?: boolean; }): string;
44
+
45
+ export interface CyrusIframeProps extends React.IframeHTMLAttributes<HTMLIFrameElement> {
46
+ projectId?: string;
47
+ hostUrl?: string;
48
+ theme?: 'light' | 'dark';
49
+ onStateChange?: (state: any) => void;
50
+ }
51
+
52
+ export declare function CyrusCanvasIframe(props: CyrusIframeProps): React.ReactElement;
package/dist/index.js ADDED
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ export * from './protocol.js';
3
+ /**
4
+ * Generates an embeddable Cyrus studio URL from a project ID or custom host.
5
+ */
6
+ export function createCyrusEmbedUrl(options = {}) {
7
+ const host = options.hostUrl || 'http://localhost:5173';
8
+ const pid = options.projectId || 'demo';
9
+ const theme = options.theme || 'light';
10
+ return `${host}/#/embed/${pid}?theme=${theme}&readOnly=${Boolean(options.readOnly)}`;
11
+ }
12
+ /**
13
+ * React Component to embed the Cyrus Visual Canvas Studio in any web app.
14
+ */
15
+ export function CyrusCanvasIframe({ projectId = 'demo', hostUrl = 'http://localhost:5173', theme = 'light', onStateChange, style, className, ...rest }) {
16
+ const embedUrl = createCyrusEmbedUrl({ projectId, hostUrl, theme });
17
+ React.useEffect(() => {
18
+ if (!onStateChange || typeof window === 'undefined')
19
+ return;
20
+ const handleMessage = (event) => {
21
+ if (event.data && event.data.type === 'CYRUS_STATE_CHANGED') {
22
+ onStateChange(event.data.payload);
23
+ }
24
+ };
25
+ window.addEventListener('message', handleMessage);
26
+ return () => window.removeEventListener('message', handleMessage);
27
+ }, [onStateChange]);
28
+ return React.createElement('iframe', {
29
+ src: embedUrl,
30
+ className: className || 'w-full h-full border-0 rounded-2xl shadow-lg',
31
+ style: {
32
+ width: '100%',
33
+ height: '600px',
34
+ border: 'none',
35
+ borderRadius: '16px',
36
+ ...style,
37
+ },
38
+ title: 'Cyrus Visual Canvas Studio',
39
+ ...rest,
40
+ });
41
+ }
package/dist/index.mjs ADDED
@@ -0,0 +1,91 @@
1
+ // packages/cyrus-sdk/src/index.ts
2
+ import React from "react";
3
+
4
+ // packages/cyrus-sdk/src/protocol.ts
5
+ function validateAndRepairCyrusIR(rawInput) {
6
+ let doc = rawInput;
7
+ if (typeof rawInput === "string") {
8
+ try {
9
+ const cleaned = rawInput.replace(/```json\s*/gi, "").replace(/```\s*$/gi, "").trim();
10
+ doc = JSON.parse(cleaned);
11
+ } catch {
12
+ return { version: "1.0", projectTitle: "AI Application", pages: [{ name: "Page 1", elements: [] }] };
13
+ }
14
+ }
15
+ if (!doc || typeof doc !== "object") {
16
+ return { version: "1.0", projectTitle: "AI Application", pages: [{ name: "Page 1", elements: [] }] };
17
+ }
18
+ const pages = Array.isArray(doc.pages) ? doc.pages : [{ name: "Home", elements: doc.elements || [] }];
19
+ return {
20
+ version: "1.0",
21
+ projectTitle: String(doc.projectTitle || doc.name || "AI Application"),
22
+ type: doc.type || "webapp",
23
+ theme: doc.theme || "light",
24
+ palette: doc.palette || { primary: "#0071e3", secondary: "#7d2ae8", background: "#ffffff", surface: "#f5f5f7", text: "#1d1d1f" },
25
+ pages: pages.map((p, idx) => ({
26
+ name: p.name || `Page ${idx + 1}`,
27
+ slug: p.slug || `page-${idx + 1}`,
28
+ elements: Array.isArray(p.elements) ? p.elements : []
29
+ }))
30
+ };
31
+ }
32
+ function getCyrusLLMSystemPrompt() {
33
+ return `Output your designs strictly in the Cyrus IR JSON format:
34
+ {
35
+ "version": "1.0",
36
+ "projectTitle": "Title",
37
+ "type": "webapp",
38
+ "theme": "light",
39
+ "pages": [
40
+ { "name": "Home", "elements": [{ "type": "navbar" }, { "type": "heading" }, { "type": "button" }] }
41
+ ]
42
+ }`;
43
+ }
44
+
45
+ // packages/cyrus-sdk/src/index.ts
46
+ function createCyrusEmbedUrl(options = {}) {
47
+ const host = options.hostUrl || "http://localhost:5173";
48
+ const pid = options.projectId || "demo";
49
+ const theme = options.theme || "light";
50
+ return `${host}/#/embed/${pid}?theme=${theme}&readOnly=${Boolean(options.readOnly)}`;
51
+ }
52
+ function CyrusCanvasIframe({
53
+ projectId = "demo",
54
+ hostUrl = "http://localhost:5173",
55
+ theme = "light",
56
+ onStateChange,
57
+ style,
58
+ className,
59
+ ...rest
60
+ }) {
61
+ const embedUrl = createCyrusEmbedUrl({ projectId, hostUrl, theme });
62
+ React.useEffect(() => {
63
+ if (!onStateChange || typeof window === "undefined") return;
64
+ const handleMessage = (event) => {
65
+ if (event.data && event.data.type === "CYRUS_STATE_CHANGED") {
66
+ onStateChange(event.data.payload);
67
+ }
68
+ };
69
+ window.addEventListener("message", handleMessage);
70
+ return () => window.removeEventListener("message", handleMessage);
71
+ }, [onStateChange]);
72
+ return React.createElement("iframe", {
73
+ src: embedUrl,
74
+ className: className || "w-full h-full border-0 rounded-2xl shadow-lg",
75
+ style: {
76
+ width: "100%",
77
+ height: "600px",
78
+ border: "none",
79
+ borderRadius: "16px",
80
+ ...style
81
+ },
82
+ title: "Cyrus Visual Canvas Studio",
83
+ ...rest
84
+ });
85
+ }
86
+ export {
87
+ CyrusCanvasIframe,
88
+ createCyrusEmbedUrl,
89
+ getCyrusLLMSystemPrompt,
90
+ validateAndRepairCyrusIR
91
+ };
@@ -0,0 +1,35 @@
1
+ export interface CyrusIRElement {
2
+ id?: string;
3
+ type: string;
4
+ name?: string;
5
+ x?: number;
6
+ y?: number;
7
+ width?: number;
8
+ height?: number;
9
+ styles?: Record<string, any>;
10
+ content?: Record<string, any>;
11
+ children?: CyrusIRElement[];
12
+ }
13
+ export interface CyrusIRPage {
14
+ id?: string;
15
+ name?: string;
16
+ slug?: string;
17
+ backgroundColor?: string;
18
+ elements: CyrusIRElement[];
19
+ }
20
+ export interface CyrusIRDocument {
21
+ version: '1.0';
22
+ projectTitle: string;
23
+ type?: 'webapp' | 'website' | 'dashboard' | 'landing' | 'mobile';
24
+ theme?: 'light' | 'dark' | 'system';
25
+ palette?: {
26
+ primary?: string;
27
+ secondary?: string;
28
+ background?: string;
29
+ surface?: string;
30
+ text?: string;
31
+ };
32
+ pages: CyrusIRPage[];
33
+ }
34
+ export declare function validateAndRepairCyrusIR(rawInput: any): CyrusIRDocument;
35
+ export declare function getCyrusLLMSystemPrompt(): string;
@@ -0,0 +1,31 @@
1
+ export function validateAndRepairCyrusIR(rawInput) {
2
+ let doc = rawInput;
3
+ if (typeof rawInput === 'string') {
4
+ try {
5
+ const cleaned = rawInput.replace(/```json\s*/gi, '').replace(/```\s*$/gi, '').trim();
6
+ doc = JSON.parse(cleaned);
7
+ }
8
+ catch {
9
+ return { version: '1.0', projectTitle: 'AI Application', pages: [{ name: 'Page 1', elements: [] }] };
10
+ }
11
+ }
12
+ if (!doc || typeof doc !== 'object') {
13
+ return { version: '1.0', projectTitle: 'AI Application', pages: [{ name: 'Page 1', elements: [] }] };
14
+ }
15
+ const pages = Array.isArray(doc.pages) ? doc.pages : [{ name: 'Home', elements: doc.elements || [] }];
16
+ return {
17
+ version: '1.0',
18
+ projectTitle: String(doc.projectTitle || doc.name || 'AI Application'),
19
+ type: doc.type || 'webapp',
20
+ theme: doc.theme || 'light',
21
+ palette: doc.palette || { primary: '#0071e3', secondary: '#7d2ae8', background: '#ffffff', surface: '#f5f5f7', text: '#1d1d1f' },
22
+ pages: pages.map((p, idx) => ({
23
+ name: p.name || `Page ${idx + 1}`,
24
+ slug: p.slug || `page-${idx + 1}`,
25
+ elements: Array.isArray(p.elements) ? p.elements : [],
26
+ })),
27
+ };
28
+ }
29
+ export function getCyrusLLMSystemPrompt() {
30
+ return `Output your designs strictly in the Cyrus IR JSON format:\n{\n "version": "1.0",\n "projectTitle": "Title",\n "type": "webapp",\n "theme": "light",\n "pages": [\n { "name": "Home", "elements": [{ "type": "navbar" }, { "type": "heading" }, { "type": "button" }] }\n ]\n}`;
31
+ }