@superblocksteam/cli 2.0.3-next.131 → 2.0.3-next.132

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/README.md CHANGED
@@ -14,7 +14,7 @@ $ npm install -g @superblocksteam/cli
14
14
  $ superblocks COMMAND
15
15
  running command...
16
16
  $ superblocks (--version)
17
- @superblocksteam/cli/2.0.3-next.131 linux-x64 node-v20.19.0
17
+ @superblocksteam/cli/2.0.3-next.132 linux-x64 node-v20.19.0
18
18
  $ superblocks --help [COMMAND]
19
19
  USAGE
20
20
  $ superblocks COMMAND
@@ -44,7 +44,7 @@ var content5 = '### Layout and Sizing in Superblocks\n\nAll layouts in Superbloc
44
44
 
45
45
  // ../../../vite-plugin-file-sync/dist/ai-service/prompts/generated/subprompts/superblocks-page.js
46
46
  init_cjs_shims();
47
- var content6 = '### How to use SbPage\n\n- A page is a file that exports a default React component through the `registerPage` function.\n- Each page consists of two files: `index.tsx` (the page component) and `scope.ts` (the entity definitions).\n- For `export default registerPage(Page1, Page1Scope)`, Page1 is a function that returns a React component, and Page1Scope is the scope containing all entities.\n- A project consists of one or more pages. Each page is a directory in the `pages` directory.\n- Pages are registered in the `routes.json` file.\n\n### Page Structure\n\nEach page should have the following structure:\n\n**scope.ts** - Contains all entity definitions (variables, APIs, etc.):\n\n```ts\nimport {\n createSbScope,\n SbVariable,\n SbVariablePersistence,\n SbApi,\n SbText,\n} from "@superblocksteam/library";\n\nexport const Page1Scope = createSbScope<{\n // Component bindings can be typed here if needed\n Text1: typeof SbText;\n}>(\n {\n // Define your entities here\n myVariable: SbVariable({\n defaultValue: "initial value",\n persistence: SbVariablePersistence.TEMPORARY,\n }),\n myApi: SbApi({}),\n },\n {\n name: "Page1",\n },\n);\n\nexport const Page1 = Page1Scope.entities;\n```\n\n**index.tsx** - Contains the page component:\n\n```tsx\nimport { SbPage, SbText, registerPage } from "@superblocksteam/library";\nimport { Page1, Page1Scope } from "./scope";\n\nfunction Page() {\n // Destructure entities from the scope for easy access\n const { Text1, myVariable, myApi } = Page1;\n\n return (\n <SbPage>\n {/* Use entities in your components */}\n <SbText bind={Text1} text={sbComputed(() => myVariable.value)} />\n </SbPage>\n );\n}\n\nexport default registerPage(Page, Page1Scope);\n```\n\n## How component bindings work\n\nIf you need to reference a component directly to get access to some of its state, you must use a binding inside the page scope.\n\nComponent bindings allow you to:\n\n- Access component properties and state from other components and APIs\n- Create reactive relationships between components\n- Reference component values in `sbComputed` expressions\n\n### Setting up component bindings\n\n1. **Define the binding type in your scope**: Add the component type to the scope\'s type definitions:\n\n```ts\nexport const Page1Scope = createSbScope<{\n // Component bindings are typed here\n Text1: typeof SbText;\n UserInput: typeof SbInput;\n}>(\n {\n // Your other entities (variables, APIs, etc.)\n myVariable: SbVariable({ defaultValue: "Hello" }),\n },\n {\n name: "Page1",\n },\n);\n```\n\n2. **Bind the component in your JSX**: Use the `bind` prop to connect the component to the binding:\n\n```tsx\nfunction Page() {\n const { Text1, UserInput, myVariable } = Page1;\n\n return (\n <SbPage>\n <SbInput bind={UserInput} placeholder="Enter your name" />\n <SbText\n bind={Text1}\n text={sbComputed(() => `Hello, ${UserInput.value}!`)}\n />\n </SbPage>\n );\n}\n```\n\n3. **Access component state**: Use `sbComputed` to access properties of bound components:\n\n```tsx\n// Access input value\ntext={sbComputed(() => UserInput.value)}\n\n// Access text content\ntext={sbComputed(() => Text1.text)}\n\n// Combine with other state\ntext={sbComputed(() => `${myVariable.value}: ${UserInput.value}`)}\n```\n\n### Common binding use cases\n\n- **Form inputs**: Access user input values to display or validate\n- **Dynamic content**: Reference one component\'s state to update another\n- **Conditional rendering**: Use component state to control visibility or styling\n\n### Page load events\n\nYou can fire callbacks when a page is loaded using the onLoad property of the Page component. You must use the SbEventFlow API for any actions you want to run inside onLoad.\n\nExample:\n\n```tsx\nimport {\n registerPage,\n SbEventFlow,\n SbPage,\n sbComputed,\n} from "@superblocksteam/library";\nimport { Page1, Page1Scope } from "./scope";\n\nconst Page1Component = () => {\n return (\n <SbPage\n onLoad={SbEventFlow.runJS(() => {\n console.log("Page loaded");\n })}\n >\n ...\n </SbPage>\n );\n};\n\nexport default registerPage(Page1Component, Page1Scope);\n```\n\nA very common and helpful usage of this callback is to run APIs using SbEventFlow to fetch data when the page loads. Example:\n\n```tsx\nimport {\n SbPage,\n registerPage,\n SbEventFlow,\n sbComputed,\n} from "@superblocksteam/library";\nimport { Page1, Page1Scope } from "./scope";\n\nconst Page1Component = () => {\n return <SbPage onLoad={SbEventFlow.runApis(["getOrders"])}>...</SbPage>;\n};\n\nexport default registerPage(Page1Component, Page1Scope);\n```\n\nIn this example, the `getOrders` API would be defined in the scope.ts file like this:\n\n```ts\n// In scope.ts\n\n// We register the api in createSbScope by creating a key\n// with the same name as the file, and SbApi({}) with an empty object\n// Note: We DO NOT import the api file. It\'s not necessary.\nexport const Page1Scope = createSbScope(\n () => ({\n getOrders: SbApi({}),\n }),\n {\n name: "Page1",\n },\n);\n```\n';
47
+ var content6 = '### How to use SbPage\n\n- A page is a file that exports a default React component through the `registerPage` function.\n- Each page consists of two files: `index.tsx` (the page component) and `scope.ts` (the entity definitions).\n- For `export default registerPage(Page1, Page1Scope)`, Page1 is a function that returns a React component, and Page1Scope is the scope containing all entities.\n- A project consists of one or more pages. Each page is a directory in the `pages` directory.\n- Pages are registered in the `routes.json` file.\n\n### Page Structure\n\nEach page should have the following structure:\n\n**scope.ts** - Contains all entity definitions (variables, APIs, etc.):\n\n```ts\nimport {\n createSbScope,\n SbVariable,\n SbVariablePersistence,\n SbApi,\n SbText,\n} from "@superblocksteam/library";\n\nexport const Page1Scope = createSbScope<{\n Text1: any;\n}>(\n {\n // Define your entities here\n myVariable: SbVariable({\n defaultValue: "initial value",\n persistence: SbVariablePersistence.TEMPORARY,\n }),\n myApi: SbApi({}),\n },\n {\n name: "Page1",\n },\n);\n\nexport const Page1 = Page1Scope.entities;\n```\n\n**index.tsx** - Contains the page component:\n\n```tsx\nimport { SbPage, SbText, registerPage } from "@superblocksteam/library";\nimport { Page1, Page1Scope } from "./scope";\n\nfunction Page() {\n // Destructure entities from the scope for easy access\n const { Text1, myVariable, myApi } = Page1;\n\n return (\n <SbPage>\n {/* Use entities in your components */}\n <SbText bind={Text1} text={sbComputed(() => myVariable.value)} />\n </SbPage>\n );\n}\n\nexport default registerPage(Page, Page1Scope);\n```\n\n## How component bindings work\n\nIf you need to reference a component directly to get access to some of its state, you must use a binding inside the page scope.\n\nComponent bindings allow you to:\n\n- Access component properties and state from other components and APIs\n- Create reactive relationships between components\n- Reference component values in `sbComputed` expressions\n\n### Setting up component bindings\n\n1. **Define the binding type in your scope**: Add the component type to the scope\'s type definitions:\n\n```ts\nexport const Page1Scope = createSbScope<{\n Text1: any;\n UserInput: any;\n}>(\n {\n // Your other entities (variables, APIs, etc.)\n myVariable: SbVariable({ defaultValue: "Hello" }),\n },\n {\n name: "Page1",\n },\n);\n```\n\n2. **Bind the component in your JSX**: Use the `bind` prop to connect the component to the binding:\n\n```tsx\nfunction Page() {\n const { Text1, UserInput, myVariable } = Page1;\n\n return (\n <SbPage>\n <SbInput bind={UserInput} placeholder="Enter your name" />\n <SbText\n bind={Text1}\n text={sbComputed(() => `Hello, ${UserInput.value}!`)}\n />\n </SbPage>\n );\n}\n```\n\n3. **Access component state**: Use `sbComputed` to access properties of bound components:\n\n```tsx\n// Access input value\ntext={sbComputed(() => UserInput.value)}\n\n// Access text content\ntext={sbComputed(() => Text1.text)}\n\n// Combine with other state\ntext={sbComputed(() => `${myVariable.value}: ${UserInput.value}`)}\n```\n\n### Common binding use cases\n\n- **Form inputs**: Access user input values to display or validate\n- **Dynamic content**: Reference one component\'s state to update another\n- **Conditional rendering**: Use component state to control visibility or styling\n\n### Page load events\n\nYou can fire callbacks when a page is loaded using the onLoad property of the Page component. You must use the SbEventFlow API for any actions you want to run inside onLoad.\n\nExample:\n\n```tsx\nimport {\n registerPage,\n SbEventFlow,\n SbPage,\n sbComputed,\n} from "@superblocksteam/library";\nimport { Page1, Page1Scope } from "./scope";\n\nconst Page1Component = () => {\n return (\n <SbPage\n onLoad={SbEventFlow.runJS(() => {\n console.log("Page loaded");\n })}\n >\n ...\n </SbPage>\n );\n};\n\nexport default registerPage(Page1Component, Page1Scope);\n```\n\nA very common and helpful usage of this callback is to run APIs using SbEventFlow to fetch data when the page loads. Example:\n\n```tsx\nimport {\n SbPage,\n registerPage,\n SbEventFlow,\n sbComputed,\n} from "@superblocksteam/library";\nimport { Page1, Page1Scope } from "./scope";\n\nconst Page1Component = () => {\n return <SbPage onLoad={SbEventFlow.runApis(["getOrders"])}>...</SbPage>;\n};\n\nexport default registerPage(Page1Component, Page1Scope);\n```\n\nIn this example, the `getOrders` API would be defined in the scope.ts file like this:\n\n```ts\n// In scope.ts\n\n// We register the api in createSbScope by creating a key\n// with the same name as the file, and SbApi({}) with an empty object\n// Note: We DO NOT import the api file. It\'s not necessary.\nexport const Page1Scope = createSbScope(\n () => ({\n getOrders: SbApi({}),\n }),\n {\n name: "Page1",\n },\n);\n```\n';
48
48
 
49
49
  // ../../../vite-plugin-file-sync/dist/ai-service/prompts/generated/subprompts/superblocks-routes.js
50
50
  init_cjs_shims();
@@ -52,7 +52,7 @@ var content7 = '- The `routes.json` file maps URLs to pages.\n Example routes.j
52
52
 
53
53
  // ../../../vite-plugin-file-sync/dist/ai-service/prompts/generated/subprompts/superblocks-state.js
54
54
  init_cjs_shims();
55
- var content8 = '**Superblocks State System**\n\nThe Superblocks state system is built around scopes that contain entities (variables, APIs, components). Here\'s how it works:\n\n### 1. Scopes\n\nScopes are defined in separate `scope.ts` files for each page and contain all the page\'s entities.\n\n- **Page scopes** are visible only within the current page\n- **App scopes** can be shared across pages (though you should primarily use page scopes)\n- Entities are accessed by importing and destructuring from the scope\n\n> **Parent \u2192 child rule**: Child scopes can read & write parent entities, but parents cannot reach into children.\n\n### 2. Scope Structure\n\nEach page has its own scope file that defines all entities:\n\n```ts\n// pages/Page1/scope.ts\nimport {\n createSbScope,\n SbVariable,\n SbVariablePersistence,\n SbApi,\n SbTable,\n sbComputed,\n} from "@superblocksteam/library";\n\nexport const Page1Scope = createSbScope<{\n // Component type definitions can go here if needed\n OrdersTable: typeof SbTable;\n}>(\n ({ entities: { counter } }) => ({\n // Static state variable with a simple default value\n counter: SbVariable({\n defaultValue: 0,\n persistence: SbVariablePersistence.TEMPORARY,\n }),\n // State variable with computed default value from other entities\n doubledCounter: SbVariable({\n defaultValue: sbComputed(() => counter.value * 2),\n persistence: SbVariablePersistence.TEMPORARY,\n }),\n myApi: SbApi({}),\n }),\n {\n name: "Page1",\n },\n);\n\nexport const Page1 = Page1Scope.entities;\n```\n\nThen in your page component, you import and destructure the entities:\n\n```tsx\n// pages/Page1/index.tsx\nimport { SbPage, SbText, SbButton, sbComputed } from "@superblocksteam/library";\nimport { Page1, Page1Scope } from "./scope";\n\nfunction Page() {\n // Destructure entities for easy access\n const { counter, doubledCounter, myApi, OrdersTable } = Page1;\n\n return (\n <SbPage>\n <SbText text={sbComputed(() => `Count: ${counter.value}`)} />\n <SbButton onClick={SbEventFlow.setStateVar("counter", 5)} />\n </SbPage>\n );\n}\n\nexport default registerPage(Page, Page1Scope);\n```\n\n_You should NOT create any extra scopes other than page scopes in most cases._\n\n### 3. Computed Default Values\n\nState variables can have their `defaultValue` computed from other entities in the same scope at initialization time.\n\n**Static default value:**\n\n```ts\nstaticVar: SbVariable({\n defaultValue: "Hello World",\n});\n```\n\n**Computed default value:**\n\n```ts\n({ entities: { Input1, staticVar } }) => ({\n computedVar: SbVariable({\n defaultValue: sbComputed(() => `${staticVar.value} - ${Input1.value}`),\n }),\n});\n```\n\nWrap the entire scope object with the entities function and destructure needed entities from the `entities` parameter. This only sets the initial value - it doesn\'t create ongoing updates.\n\n### 4. Entity Types\n\n- **Component state**\n\n - Declared by defining the component type in the scope and using the `bind` prop\n - Accessed by destructuring the component entity and using `sbComputed(() => ComponentName.prop)`\n - Writable if the prop is marked writable in the schema\n\n- **State variable**\n\n - Declared with `SbVariable({ defaultValue })` or within `(({ entities }) => ({ ... }))` wrapper for computed defaults\n - `defaultValue` sets the initial value when the application loads\n - For static variables: Only JSON parsable data structures, no functions\n - For computed variables: Use `sbComputed` within defaultValue to reference other entities\n - Do not call `defaultValue` like a function. Set values with `SbEventFlow.setStateVar()` or read with `sbComputed(() => varName.value)`\n - Accessed with `sbComputed(() => varName.value)` \u2014 **always include `.value`**\n - Writable through SbEventFlow\n\n- **API**\n - Declared with `SbApi({})` in the scope file.\n - Accessed with `sbComputed(() => apiName.response)` or `sbComputed(() => apiName.error)`\n - Read\u2011only\n\n### 5. Reading State with sbComputed\n\n**CRITICAL**: There are two different patterns for accessing data in `sbComputed` depending on what you\'re accessing:\n\n#### 5.1. Scope Entities (Direct Access)\n\nFor entities defined in your scope file (variables, APIs, components), access them **directly** after destructuring:\n\n```tsx\nconst { UserSearchInput, myVariable, myApi } = Page1;\n\n// \u2705 CORRECT - Direct access for scope entities\n<SbText text={sbComputed(() => `Search: ${UserSearchInput.value}`)} />\n<SbText text={sbComputed(() => myVariable.value)} />\n<SbTable tableData={sbComputed(() => myApi.response)} />\n```\n\n#### 4.2. Global State (Import Access)\n\nFor global state like globals, theme, environment, and embedded data, import the globals directly from the library:\n\n```tsx\n// \u2705 CORRECT - Import globals directly\n<SbText\n text={sbComputed(() => `Welcome ${Global.user.name}!`)}\n textStyle={{\n textColor: {\n default: sbComputed(() => Theme.colors.neutral900)\n }\n }}\n/>\n<SbContainer backgroundColor={sbComputed(() => Theme.colors.primary)} />\n```\n\n#### 5.3. Mixed Access\n\nWhen you need both scope entities and global state in the same `sbComputed`:\n\n```tsx\nconst { orders } = Page1;\n\n// \u2705 CORRECT - Combine direct entity access with imported globals\n<SbText\n text={sbComputed(() => `${orders.length} orders for ${Global.user.name}`)}\n/>;\n```\n\n### 6. Writing State\n\nPreferred approach:\n\n```tsx\nSbEventFlow.setStateVar("pageSize", 50);\n```\n\nFallback for complex operations:\n\n```tsx\nSbEventFlow.runJS(() => {\n // Direct assignment works within SbEventFlow.runJS\n pageSize.value = 50;\n});\n```\n\n### 7. CRITICAL Rules\n\n1. **Use component state directly**; NEVER create a state variable for a value that is available directly from a component property.\n2. Use component bindings by defining component types in your scope and using the `bind` prop.\n3. Keep logic declarative \u2014 derive UI from state using `sbComputed` instead of mutating props at runtime.\n4. Always destructure entities from your scope for clean access patterns.\n5. **Use the correct sbComputedz pattern**: Direct access for scope entities, import globals (Global, Theme, Embed, Env) for global access.\n6. Always complete implement all features, don\'t stub out or skip anything.\n\n**\u2705 Example**\n\n```tsx\n// In your scope.ts file\nexport const Page1Scope = createSbScope<{\n FirstName: typeof SbInput;\n LastName: typeof SbInput;\n}>(\n ({ entities: { FirstName, LastName } }) => ({\n fullName: SbVariable({\n defaultValue: sbComputed(() =>\n `${FirstName.value} ${LastName.value}`.trim(),\n ),\n }),\n }),\n {\n name: "Page1",\n },\n);\n```\n\n```tsx\nconst { FirstName, LastName, fullName } = Page1;\n\n<SbInput bind={FirstName} placeholder="First name" />\n<SbInput bind={LastName} placeholder="Last name" />\n<SbText text={sbComputed(() => `Welcome ${fullName.value}!`)} />\n```\n\n```tsx\n{\n /* The user types in the search input, and the value is displayed */\n}\n// In your scope.ts file\nexport const Page1Scope = createSbScope<{\n searchTerm: typeof SbInput;\n}>(\n {},\n {\n name: "Page1",\n },\n);\n```\n\n```tsx\nconst { searchTerm } = Page1;\n\n<SbInput bind={searchTerm} />\n<SbText text={sbComputed(() => searchTerm.value)} />\n```\n\n```tsx\n{\n /* Switch to change into admin mode */\n}\n// In your scope.ts file\nexport const Page1Scope = createSbScope<{\n isAdmin: typeof SbSwitch;\n}>(\n {},\n {\n name: "Page1",\n },\n);\n```\n\n```tsx\nconst { isAdmin } = Page1;\n\n<SbSwitch bind={isAdmin} label="Use Admin Mode" defaultChecked={false} />;\n{\n /* Only enable the delete button when switched into Admin mode */\n}\n<SbButton\n disabled={sbComputed(() => !isAdmin.isChecked)}\n label="Delete profile"\n/>;\n```\n\n\u2705 ALWAYS USE DIRECT COMPONENT STATE ACCESS WITH sbComputed:\n\n```tsx\n// GOOD - DO THIS\n// In your scope.ts file\nexport const Page1Scope = createSbScope<{\n UserNameInput: typeof SbTextInput;\n}>(\n {},\n {\n name: "Page1",\n },\n);\n```\n\n```tsx\nconst { UserNameInput } = Page1;\n\n<SbTextInput bind={UserNameInput} />\n<SbText text={sbComputed(() => `Hello, ${UserNameInput.value}!`)} />\n```\n\n\u274C NEVER CREATE REDUNDANT STATE VARIABLES:\n\n```tsx\n// WRONG - DON\'T DO THIS\n<SbTextInput bind={UserNameInput} onChange={SbEventFlow.setStateVar(\'userNameVar\', \'myValue\')} />\n<SbText text={sbComputed(() => `Hello, ${userNameVar.value}!`)} />\n...\n// And then in scope.ts:\n{\n userNameVar: SbVariable({ defaultValue: "" }),\n}\n```\n\nTo reference a property of a component using `sbComputed`, the component must have a binding defined in your scope.\n\nThat\'s why you should ALWAYS define component bindings in your scope for the following component types when you need to access their state:\n\n- SbInput\n- SbDropdown\n- SbDatePicker\n- SbCheckbox\n- SbSwitch\n- SbForm\n- SbRadio\n- SbRichText\n- SbFilePicker\n- SbCodeEditor\n- SbChat\n\n\u274C DO NOT USE STATE VARIABLES AS FUNCTIONS:\n\nState variables are not functions. You cannot call them or store functions in defaultValue.\n\n```tsx\n// WRONG - DON\'T DO THIS\nfunction Page1() {\n const { customerNameFilter, filterOrders } = Page1;\n\n return (\n <SbPage width={Dim.fill()}>\n <SbInput\n bind={customerNameFilter}\n label="Customer Name"\n placeholder="Filter by customer name"\n width={Dim.fill()}\n onTextChanged={SbEventFlow.runJS(() => {\n // This is wrong - you cannot call filterOrders as a function\n filterOrders();\n })}\n />\n </SbPage>\n );\n}\n\n// And in scope.ts (WRONG):\n{\n filterOrders: SbVariable({\n defaultValue: () => {\n // This doesn\'t work - state variables are not functions\n const customerNameFilter = customerNameFilter.value.toLowerCase();\n // ... more logic\n },\n });\n}\n```\n\nInstead do the following:\n\n\u2705 COMPUTE VALUES WITH sbComputed AND SET STATE WITH SbEventFlow:\n\n```tsx\n// CORRECT - DO THIS\nfunction Page1() {\n const { customerNameFilter, filteredOrders, orders } = Page1;\n\n return (\n <SbPage width={Dim.fill()}>\n <SbInput\n bind={customerNameFilter}\n label="Customer Name"\n placeholder="Filter by customer name"\n width={Dim.fill()}\n onTextChanged={SbEventFlow.runJS(() => {\n // Repeat the filtering logic inline - do NOT create helper functions\n const filtered = orders.value.filter((order) => {\n return (\n !customerNameFilter.value ||\n order.customerName\n .toLowerCase()\n .includes(customerNameFilter.value.toLowerCase())\n );\n });\n\n // Set the state variable with the computed result\n filteredOrders.value = filtered;\n })}\n />\n {/* If you need the same filtering logic elsewhere, repeat it inline */}\n <SbButton\n label="Apply Filter"\n onClick={SbEventFlow.runJS(() => {\n // Repeat the same filtering logic here - code repetition is preferred\n const filtered = orders.value.filter((order) => {\n return (\n !customerNameFilter.value ||\n order.customerName\n .toLowerCase()\n .includes(customerNameFilter.value.toLowerCase())\n );\n });\n\n filteredOrders.value = filtered;\n })}\n />\n <SbTable tableData={sbComputed(() => filteredOrders.value)} />\n </SbPage>\n );\n}\n\nexport default registerPage(Page1, Page1Scope);\n```\n\nAnd in your scope.ts file:\n\n```ts\nexport const Page1Scope = createSbScope<{\n customerNameFilter: typeof SbInput;\n}>(\n {\n orders: SbVariable({\n defaultValue: [\n {\n id: "ORD-1001",\n customerName: "John Smith",\n orderType: "Grocery Delivery",\n },\n {\n id: "ORD-1002",\n customerName: "Emily Johnson",\n orderType: "Express Delivery",\n },\n ],\n persistence: SbVariablePersistence.TEMPORARY,\n }),\n filteredOrders: SbVariable({\n defaultValue: [],\n persistence: SbVariablePersistence.TEMPORARY,\n }),\n },\n {\n name: "Page1",\n },\n);\n```\n';
55
+ var content8 = '**Superblocks State System**\n\nThe Superblocks state system is built around scopes that contain entities (variables, APIs, components). Here\'s how it works:\n\n### 1. Scopes\n\nScopes are defined in separate `scope.ts` files for each page and contain all the page\'s entities.\n\n- **Page scopes** are visible only within the current page\n- **App scopes** can be shared across pages (though you should primarily use page scopes)\n- Entities are accessed by importing and destructuring from the scope\n\n> **Parent \u2192 child rule**: Child scopes can read & write parent entities, but parents cannot reach into children.\n\n### 2. Scope Structure\n\nEach page has its own scope file that defines all entities:\n\n```ts\n// pages/Page1/scope.ts\nimport {\n createSbScope,\n SbVariable,\n SbVariablePersistence,\n SbApi,\n sbComputed,\n} from "@superblocksteam/library";\n\nexport const Page1Scope = createSbScope<{\n OrdersTable: any;\n}>(\n ({ entities: { counter } }) => ({\n // Static state variable with a simple default value\n counter: SbVariable({\n defaultValue: 0,\n persistence: SbVariablePersistence.TEMPORARY,\n }),\n // State variable with computed default value from other entities\n doubledCounter: SbVariable({\n defaultValue: sbComputed(() => counter.value * 2),\n persistence: SbVariablePersistence.TEMPORARY,\n }),\n myApi: SbApi({}),\n }),\n {\n name: "Page1",\n },\n);\n\nexport const Page1 = Page1Scope.entities;\n```\n\nThen in your page component, you import and destructure the entities:\n\n```tsx\n// pages/Page1/index.tsx\nimport { SbPage, SbText, SbButton, sbComputed } from "@superblocksteam/library";\nimport { Page1, Page1Scope } from "./scope";\n\nfunction Page() {\n // Destructure entities for easy access\n const { counter, doubledCounter, myApi, OrdersTable } = Page1;\n\n return (\n <SbPage>\n <SbText text={sbComputed(() => `Count: ${counter.value}`)} />\n <SbButton onClick={SbEventFlow.setStateVar("counter", 5)} />\n </SbPage>\n );\n}\n\nexport default registerPage(Page, Page1Scope);\n```\n\n_You should NOT create any extra scopes other than page scopes in most cases._\n\n### 3. Computed Default Values\n\nState variables can have their `defaultValue` computed from other entities in the same scope at initialization time.\n\n**Static default value:**\n\n```ts\nstaticVar: SbVariable({\n defaultValue: "Hello World",\n});\n```\n\n**Computed default value:**\n\n```ts\n({ entities: { Input1, staticVar } }) => ({\n computedVar: SbVariable({\n defaultValue: sbComputed(() => `${staticVar.value} - ${Input1.value}`),\n }),\n});\n```\n\nWrap the entire scope object with the entities function and destructure needed entities from the `entities` parameter. This only sets the initial value - it doesn\'t create ongoing updates.\n\n### 4. Entity Types\n\n- **Component state**\n\n - Declared by defining the component type in the scope and using the `bind` prop\n - Accessed by destructuring the component entity and using `sbComputed(() => ComponentName.prop)`\n - Writable if the prop is marked writable in the schema\n\n- **State variable**\n\n - Declared with `SbVariable({ defaultValue })` or within `(({ entities }) => ({ ... }))` wrapper for computed defaults\n - `defaultValue` sets the initial value when the application loads\n - For static variables: Only JSON parsable data structures, no functions\n - For computed variables: Use `sbComputed` within defaultValue to reference other entities\n - Do not call `defaultValue` like a function. Set values with `SbEventFlow.setStateVar()` or read with `sbComputed(() => varName.value)`\n - Accessed with `sbComputed(() => varName.value)` \u2014 **always include `.value`**\n - Writable through SbEventFlow\n\n- **API**\n - Declared with `SbApi({})` in the scope file.\n - Accessed with `sbComputed(() => apiName.response)` or `sbComputed(() => apiName.error)`\n - Read\u2011only\n\n### 5. Reading State with sbComputed\n\n**CRITICAL**: There are two different patterns for accessing data in `sbComputed` depending on what you\'re accessing:\n\n#### 5.1. Scope Entities (Direct Access)\n\nFor entities defined in your scope file (variables, APIs, components), access them **directly** after destructuring:\n\n```tsx\nconst { UserSearchInput, myVariable, myApi } = Page1;\n\n// \u2705 CORRECT - Direct access for scope entities\n<SbText text={sbComputed(() => `Search: ${UserSearchInput.value}`)} />\n<SbText text={sbComputed(() => myVariable.value)} />\n<SbTable tableData={sbComputed(() => myApi.response)} />\n```\n\n#### 4.2. Global State (Import Access)\n\nFor global state like globals, theme, environment, and embedded data, import the globals directly from the library:\n\n```tsx\n// \u2705 CORRECT - Import globals directly\n<SbText\n text={sbComputed(() => `Welcome ${Global.user.name}!`)}\n textStyle={{\n textColor: {\n default: sbComputed(() => Theme.colors.neutral900)\n }\n }}\n/>\n<SbContainer backgroundColor={sbComputed(() => Theme.colors.primary)} />\n```\n\n#### 5.3. Mixed Access\n\nWhen you need both scope entities and global state in the same `sbComputed`:\n\n```tsx\nconst { orders } = Page1;\n\n// \u2705 CORRECT - Combine direct entity access with imported globals\n<SbText\n text={sbComputed(() => `${orders.length} orders for ${Global.user.name}`)}\n/>;\n```\n\n### 6. Writing State\n\nPreferred approach:\n\n```tsx\nSbEventFlow.setStateVar("pageSize", 50);\n```\n\nFallback for complex operations:\n\n```tsx\nSbEventFlow.runJS(() => {\n // Direct assignment works within SbEventFlow.runJS\n pageSize.value = 50;\n});\n```\n\n### 7. CRITICAL Rules\n\n1. **Use component state directly**; NEVER create a state variable for a value that is available directly from a component property.\n2. Use component bindings by defining component types in your scope and using the `bind` prop.\n3. Keep logic declarative \u2014 derive UI from state using `sbComputed` instead of mutating props at runtime.\n4. Always destructure entities from your scope for clean access patterns.\n5. **Use the correct sbComputedz pattern**: Direct access for scope entities, import globals (Global, Theme, Embed, Env) for global access.\n6. Always complete implement all features, don\'t stub out or skip anything.\n\n**\u2705 Example**\n\n```tsx\n// In your scope.ts file\nexport const Page1Scope = createSbScope<{\n ApplicantSelector: any;\n}>(\n {\n applicants: SbVariable({ defaultValue: [] }),\n },\n {\n name: "Page1",\n },\n);\n```\n\n```tsx\n// In your page component after destructuring entities\nconst { ApplicantSelector, applicants } = Page1;\n\n<SbDropdown bind={ApplicantSelector} options={/* ... */} />\n<SbTable tableData={sbComputed(() => applicants[ApplicantSelector.selectedOptionValue])} />\n```\n\n```tsx\n// In your scope.ts file\nexport const Page1Scope = createSbScope<{\n Country: any;\n}>(\n {\n applicants: SbVariable({ defaultValue: [] }),\n },\n FirstName: typeof SbInput;\n LastName: typeof SbInput;\n}>(\n ({ entities: { FirstName, LastName } }) => ({\n fullName: SbVariable({\n defaultValue: sbComputed(() =>\n `${FirstName.value} ${LastName.value}`.trim(),\n ),\n }),\n }),\n {\n name: "Page1",\n },\n);\n```\n\n```tsx\nconst { FirstName, LastName, fullName } = Page1;\n\n<SbInput bind={FirstName} placeholder="First name" />\n<SbInput bind={LastName} placeholder="Last name" />\n<SbText text={sbComputed(() => `Welcome ${fullName.value}!`)} />\n```\n\n```tsx\n{\n /* The user types in the search input, and the value is displayed */\n}\n// In your scope.ts file\nexport const Page1Scope = createSbScope<{\n searchTerm: any;\n}>(\n {},\n {\n name: "Page1",\n },\n);\n```\n\n```tsx\nconst { searchTerm } = Page1;\n\n<SbInput bind={searchTerm} />\n<SbText text={sbComputed(() => searchTerm.value)} />\n```\n\n```tsx\n{\n /* Switch to change into admin mode */\n}\n// In your scope.ts file\nexport const Page1Scope = createSbScope<{\n isAdmin: any;\n}>(\n {},\n {\n name: "Page1",\n },\n);\n```\n\n```tsx\nconst { isAdmin } = Page1;\n\n<SbSwitch bind={isAdmin} label="Use Admin Mode" defaultChecked={false} />;\n{\n /* Only enable the delete button when switched into Admin mode */\n}\n<SbButton\n disabled={sbComputed(() => !isAdmin.isChecked)}\n label="Delete profile"\n/>;\n```\n\n\u2705 ALWAYS USE DIRECT COMPONENT STATE ACCESS WITH sbComputed:\n\n```tsx\n// GOOD - DO THIS\n// In your scope.ts file\nexport const Page1Scope = createSbScope<{\n UserNameInput: any;\n}>(\n {},\n {\n name: "Page1",\n },\n);\n```\n\n```tsx\nconst { UserNameInput } = Page1;\n\n<SbTextInput bind={UserNameInput} />\n<SbText text={sbComputed(() => `Hello, ${UserNameInput.value}!`)} />\n```\n\n\u274C NEVER CREATE REDUNDANT STATE VARIABLES:\n\n```tsx\n// WRONG - DON\'T DO THIS\n<SbTextInput bind={UserNameInput} onChange={SbEventFlow.setStateVar(\'userNameVar\', \'myValue\')} />\n<SbText text={sbComputed(() => `Hello, ${userNameVar.value}!`)} />\n...\n// And then in scope.ts:\n{\n userNameVar: SbVariable({ defaultValue: "" }),\n}\n```\n\nTo reference a property of a component using `sbComputed`, the component must have a binding defined in your scope.\n\nThat\'s why you should ALWAYS define component bindings in your scope for the following component types when you need to access their state:\n\n- SbInput\n- SbDropdown\n- SbDatePicker\n- SbCheckbox\n- SbSwitch\n- SbForm\n- SbRadio\n- SbRichText\n- SbFilePicker\n- SbCodeEditor\n- SbChat\n\n\u274C DO NOT USE STATE VARIABLES AS FUNCTIONS:\n\nState variables are not functions. You cannot call them or store functions in defaultValue.\n\n```tsx\n// WRONG - DON\'T DO THIS\nfunction Page1() {\n const { customerNameFilter, filterOrders } = Page1;\n\n return (\n <SbPage width={Dim.fill()}>\n <SbInput\n bind={customerNameFilter}\n label="Customer Name"\n placeholder="Filter by customer name"\n width={Dim.fill()}\n onTextChanged={SbEventFlow.runJS(() => {\n // This is wrong - you cannot call filterOrders as a function\n filterOrders();\n })}\n />\n </SbPage>\n );\n}\n\n// And in scope.ts (WRONG):\n{\n filterOrders: SbVariable({\n defaultValue: () => {\n // This doesn\'t work - state variables are not functions\n const customerNameFilter = customerNameFilter.value.toLowerCase();\n // ... more logic\n },\n });\n}\n```\n\nInstead do the following:\n\n\u2705 COMPUTE VALUES WITH sbComputed AND SET STATE WITH SbEventFlow:\n\n```tsx\n// CORRECT - DO THIS\nfunction Page1() {\n const { customerNameFilter, filteredOrders, orders } = Page1;\n\n return (\n <SbPage width={Dim.fill()}>\n <SbInput\n bind={customerNameFilter}\n label="Customer Name"\n placeholder="Filter by customer name"\n width={Dim.fill()}\n onTextChanged={SbEventFlow.runJS(() => {\n // Repeat the filtering logic inline - do NOT create helper functions\n const filtered = orders.value.filter((order) => {\n return (\n !customerNameFilter.value ||\n order.customerName\n .toLowerCase()\n .includes(customerNameFilter.value.toLowerCase())\n );\n });\n\n // Set the state variable with the computed result\n filteredOrders.value = filtered;\n })}\n />\n {/* If you need the same filtering logic elsewhere, repeat it inline */}\n <SbButton\n label="Apply Filter"\n onClick={SbEventFlow.runJS(() => {\n // Repeat the same filtering logic here - code repetition is preferred\n const filtered = orders.value.filter((order) => {\n return (\n !customerNameFilter.value ||\n order.customerName\n .toLowerCase()\n .includes(customerNameFilter.value.toLowerCase())\n );\n });\n\n filteredOrders.value = filtered;\n })}\n />\n <SbTable tableData={sbComputed(() => filteredOrders.value)} />\n </SbPage>\n );\n}\n\nexport default registerPage(Page1, Page1Scope);\n```\n\nAnd in your scope.ts file:\n\n```ts\nexport const Page1Scope = createSbScope<{\n customerNameFilter: any;\n}>(\n {\n orders: SbVariable({\n defaultValue: [\n {\n id: "ORD-1001",\n customerName: "John Smith",\n orderType: "Grocery Delivery",\n },\n {\n id: "ORD-1002",\n customerName: "Emily Johnson",\n orderType: "Express Delivery",\n },\n ],\n persistence: SbVariablePersistence.TEMPORARY,\n }),\n filteredOrders: SbVariable({\n defaultValue: [],\n persistence: SbVariablePersistence.TEMPORARY,\n }),\n },\n {\n name: "Page1",\n },\n);\n```\n';
56
56
 
57
57
  // ../../../vite-plugin-file-sync/dist/ai-service/prompts/generated/subprompts/superblocks-theming.js
58
58
  init_cjs_shims();
package/dist/index.js CHANGED
@@ -329161,7 +329161,7 @@ var import_dd_trace = __toESM(require_dd_trace2(), 1);
329161
329161
  // ../sdk/package.json
329162
329162
  var package_default = {
329163
329163
  name: "@superblocksteam/sdk",
329164
- version: "2.0.3-next.131",
329164
+ version: "2.0.3-next.132",
329165
329165
  type: "module",
329166
329166
  description: "Superblocks JS SDK",
329167
329167
  homepage: "https://www.superblocks.com",
@@ -329191,8 +329191,8 @@ var package_default = {
329191
329191
  "@rollup/wasm-node": "^4.35.0",
329192
329192
  "@superblocksteam/bucketeer-sdk": "0.4.1",
329193
329193
  "@superblocksteam/shared": "^0.9081.0",
329194
- "@superblocksteam/util": "2.0.3-next.131",
329195
- "@superblocksteam/vite-plugin-file-sync": "2.0.3-next.131",
329194
+ "@superblocksteam/util": "2.0.3-next.132",
329195
+ "@superblocksteam/vite-plugin-file-sync": "2.0.3-next.132",
329196
329196
  "@vitejs/plugin-react": "^4.3.4",
329197
329197
  axios: "^1.4.0",
329198
329198
  chokidar: "^4.0.3",
@@ -373714,7 +373714,7 @@ async function startVite({ app, httpServer: httpServer2, root: root2, mode, fsOp
373714
373714
  };
373715
373715
  const isCustomBuildEnabled2 = await isCustomComponentsEnabled();
373716
373716
  const customFolder = path21.join(root2, "custom");
373717
- const cdnUrl = "https://assets-cdn.superblocks.com/library/2.0.3-next.131";
373717
+ const cdnUrl = "https://assets-cdn.superblocks.com/library/2.0.3-next.132";
373718
373718
  const env3 = loadEnv(mode, root2, "");
373719
373719
  const hmrPort = await getFreePort();
373720
373720
  const viteServer = await createServer({
@@ -380384,7 +380384,7 @@ init_cjs_shims();
380384
380384
  init_cjs_shims();
380385
380385
  var generated = {};
380386
380386
  try {
380387
- generated = await import("./generated-NMKLFPL6.js");
380387
+ generated = await import("./generated-OUL5LHHB.js");
380388
380388
  } catch (_error) {
380389
380389
  console.warn("Generated markdown modules not found. Run `pnpm generate:markdown` first.");
380390
380390
  }
@@ -509,5 +509,5 @@
509
509
  "strict": true
510
510
  }
511
511
  },
512
- "version": "2.0.3-next.131"
512
+ "version": "2.0.3-next.132"
513
513
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superblocksteam/cli",
3
- "version": "2.0.3-next.131",
3
+ "version": "2.0.3-next.132",
4
4
  "type": "module",
5
5
  "description": "Official Superblocks CLI",
6
6
  "homepage": "https://www.superblocks.com",
@@ -42,9 +42,9 @@
42
42
  "devDependencies": {
43
43
  "@eslint/js": "^9.16.0",
44
44
  "@oclif/test": "^4.1.11",
45
- "@superblocksteam/sdk": "2.0.3-next.131",
45
+ "@superblocksteam/sdk": "2.0.3-next.132",
46
46
  "@superblocksteam/shared": "^0.9081.0",
47
- "@superblocksteam/util": "2.0.3-next.131",
47
+ "@superblocksteam/util": "2.0.3-next.132",
48
48
  "@types/babel__core": "^7.20.0",
49
49
  "@types/chai": "^4",
50
50
  "@types/fs-extra": "^11.0.1",