From 86d111864ede6771632c457555195e766c28665a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jurij=20Juki=C4=87?= Date: Wed, 29 Oct 2025 10:15:06 +0100 Subject: [PATCH 01/11] wip --- src/main.rs | 30 +- .../rust/ui/hyperapp-skeleton/README.md | 56 +- .../example-apps/file-explorer/Cargo.toml | 0 .../api/file-explorer-sys-v0.wit | 0 .../file-explorer/api/file-explorer.wit | 0 .../api/types-file-explorer-sys-v0.wit | 0 .../file-explorer/explorer/Cargo.toml | 0 .../file-explorer/explorer/src/icon | 0 .../file-explorer/explorer/src/lib.rs | 0 .../example-apps/file-explorer/metadata.json | 0 .../file-explorer/pkg/manifest.json | 0 .../file-explorer/ui/.eslintrc.cjs | 0 .../example-apps/file-explorer/ui/.gitignore | 0 .../example-apps/file-explorer/ui/README.md | 0 .../example-apps/file-explorer/ui/index.html | 0 .../file-explorer/ui/package-lock.json | 0 .../file-explorer/ui/package.json | 0 .../example-apps/file-explorer/ui/src/App.css | 0 .../example-apps/file-explorer/ui/src/App.tsx | 0 .../file-explorer/ui/src/assets/react.svg | 0 .../file-explorer/ui/src/assets/vite.svg | 0 .../components/ContextMenu/ContextMenu.css | 0 .../components/ContextMenu/ContextMenu.tsx | 0 .../components/FileExplorer/Breadcrumb.css | 0 .../components/FileExplorer/Breadcrumb.tsx | 0 .../components/FileExplorer/FileExplorer.css | 0 .../components/FileExplorer/FileExplorer.tsx | 0 .../src/components/FileExplorer/FileItem.css | 0 .../src/components/FileExplorer/FileItem.tsx | 0 .../src/components/FileExplorer/FileList.css | 0 .../src/components/FileExplorer/FileList.tsx | 0 .../src/components/FileExplorer/Toolbar.css | 0 .../src/components/FileExplorer/Toolbar.tsx | 0 .../components/ShareDialog/ShareDialog.css | 0 .../components/ShareDialog/ShareDialog.tsx | 0 .../ui/src/components/Upload/UploadZone.css | 0 .../ui/src/components/Upload/UploadZone.tsx | 0 .../ui/src/contexts/ThemeContext.tsx | 0 .../file-explorer/ui/src/index.css | 0 .../file-explorer/ui/src/lib/api.ts | 0 .../file-explorer/ui/src/main.tsx | 0 .../ui/src/store/fileExplorer.ts | 0 .../file-explorer/ui/src/types/api.ts | 0 .../file-explorer/ui/src/types/global.ts | 0 .../file-explorer/ui/src/vite-env.d.ts | 0 .../file-explorer/ui/tsconfig.json | 0 .../file-explorer/ui/tsconfig.node.json | 0 .../file-explorer/ui/vite.config.ts | 0 .../example-apps/id/Cargo.toml | 0 .../{resources => }/example-apps/id/README.md | 0 .../example-apps/id/id/Cargo.toml | 0 .../example-apps/id/id/src/icon | 0 .../example-apps/id/id/src/lib.rs | 0 .../example-apps/id/metadata.json | 0 .../example-apps/id/pkg/manifest.json | 0 .../example-apps/id/ui/.eslintrc.cjs | 0 .../example-apps/id/ui/.gitignore | 0 .../example-apps/id/ui/README.md | 0 .../example-apps/id/ui/index.html | 0 .../example-apps/id/ui/package-lock.json | 0 .../example-apps/id/ui/package.json | 0 .../example-apps/id/ui/public/assets/vite.svg | 0 .../example-apps/id/ui/src/App.css | 0 .../example-apps/id/ui/src/App.tsx | 0 .../example-apps/id/ui/src/assets/react.svg | 0 .../example-apps/id/ui/src/assets/vite.svg | 0 .../example-apps/id/ui/src/index.css | 0 .../example-apps/id/ui/src/main.tsx | 0 .../example-apps/id/ui/src/store/id.ts | 0 .../example-apps/id/ui/src/types/Id.ts | 0 .../example-apps/id/ui/src/types/global.ts | 0 .../example-apps/id/ui/src/vite-env.d.ts | 0 .../example-apps/id/ui/tsconfig.json | 0 .../example-apps/id/ui/tsconfig.node.json | 0 .../example-apps/id/ui/vite.config.ts | 0 .../example-apps/sign/Cargo.toml | 0 .../example-apps/sign/README.md | 0 .../example-apps/sign/metadata.json | 0 .../example-apps/sign/pkg/manifest.json | 0 .../example-apps/sign/sign/Cargo.toml | 0 .../example-apps/sign/sign/src/icon | 0 .../example-apps/sign/sign/src/lib.rs | 0 .../ui/hyperapp-skeleton/resources/README.md | 84 - .../guides/03-WIT-TYPES-DATA-MODELING.md | 734 -------- .../resources/guides/05-UI-FRONTEND-GUIDE.md | 1553 ----------------- .../resources/guides/10-SQLITE-API-GUIDE.md | 693 -------- .../11-REALTIME-COMMUNICATION-PATTERNS.md | 744 -------- .../resources/guides/README.md | 182 -- 88 files changed, 50 insertions(+), 4026 deletions(-) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/Cargo.toml (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/api/file-explorer-sys-v0.wit (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/api/file-explorer.wit (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/api/types-file-explorer-sys-v0.wit (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/explorer/Cargo.toml (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/explorer/src/icon (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/explorer/src/lib.rs (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/metadata.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/pkg/manifest.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/.eslintrc.cjs (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/.gitignore (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/README.md (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/index.html (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/package-lock.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/package.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/App.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/App.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/assets/react.svg (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/assets/vite.svg (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/ContextMenu/ContextMenu.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/ContextMenu/ContextMenu.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/FileExplorer/Breadcrumb.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/FileExplorer/Breadcrumb.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/FileExplorer/FileExplorer.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/FileExplorer/FileExplorer.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/FileExplorer/FileItem.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/FileExplorer/FileItem.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/FileExplorer/FileList.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/FileExplorer/FileList.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/FileExplorer/Toolbar.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/FileExplorer/Toolbar.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/ShareDialog/ShareDialog.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/ShareDialog/ShareDialog.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/Upload/UploadZone.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/components/Upload/UploadZone.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/contexts/ThemeContext.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/index.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/lib/api.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/main.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/store/fileExplorer.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/types/api.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/types/global.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/src/vite-env.d.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/tsconfig.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/tsconfig.node.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/file-explorer/ui/vite.config.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/Cargo.toml (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/README.md (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/id/Cargo.toml (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/id/src/icon (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/id/src/lib.rs (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/metadata.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/pkg/manifest.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/.eslintrc.cjs (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/.gitignore (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/README.md (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/index.html (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/package-lock.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/package.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/public/assets/vite.svg (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/src/App.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/src/App.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/src/assets/react.svg (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/src/assets/vite.svg (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/src/index.css (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/src/main.tsx (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/src/store/id.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/src/types/Id.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/src/types/global.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/src/vite-env.d.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/tsconfig.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/tsconfig.node.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/id/ui/vite.config.ts (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/sign/Cargo.toml (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/sign/README.md (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/sign/metadata.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/sign/pkg/manifest.json (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/sign/sign/Cargo.toml (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/sign/sign/src/icon (100%) rename src/new/templates/rust/ui/hyperapp-skeleton/{resources => }/example-apps/sign/sign/src/lib.rs (100%) delete mode 100644 src/new/templates/rust/ui/hyperapp-skeleton/resources/README.md delete mode 100644 src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/03-WIT-TYPES-DATA-MODELING.md delete mode 100644 src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/05-UI-FRONTEND-GUIDE.md delete mode 100644 src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/10-SQLITE-API-GUIDE.md delete mode 100644 src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/11-REALTIME-COMMUNICATION-PATTERNS.md delete mode 100644 src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/README.md diff --git a/src/main.rs b/src/main.rs index 0e385aaf..dee4d41b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -559,6 +559,8 @@ async fn execute( #[instrument(level = "trace", skip_all)] async fn make_app(current_dir: &std::ffi::OsString) -> Result { + let is_offline = std::env::var("KIT_OFFLINE").is_ok(); + Ok(command!() .name("kit") .version(env!("CARGO_PKG_VERSION")) @@ -589,11 +591,15 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .default_value("latest") .value_parser(PossibleValuesParser::new({ let mut possible_values = vec!["latest".to_string()]; - let mut remote_values = boot_fake_node::find_releases_with_asset_if_online( - None, - None, - &boot_fake_node::get_platform_runtime_name(true)? - ).await.unwrap_or_default(); + let mut remote_values = if is_offline { + Vec::new() + } else { + boot_fake_node::find_releases_with_asset_if_online( + None, + None, + &boot_fake_node::get_platform_runtime_name(true)? + ).await.unwrap_or_default() + }; remote_values.truncate(MAX_REMOTE_VALUES); //if remote_values.len() == 0 { // possible_values = vec![]; @@ -688,11 +694,15 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .default_value("latest") .value_parser(PossibleValuesParser::new({ let mut possible_values = vec!["latest".to_string()]; - let mut remote_values = boot_fake_node::find_releases_with_asset_if_online( - None, - None, - &boot_fake_node::get_platform_runtime_name(false)? - ).await?; + let mut remote_values = if is_offline { + Vec::new() + } else { + boot_fake_node::find_releases_with_asset_if_online( + None, + None, + &boot_fake_node::get_platform_runtime_name(false)? + ).await? + }; remote_values.truncate(MAX_REMOTE_VALUES); //if remote_values.len() == 0 { // possible_values = vec![]; diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/README.md b/src/new/templates/rust/ui/hyperapp-skeleton/README.md index 6cb2270a..0956b7d1 100644 --- a/src/new/templates/rust/ui/hyperapp-skeleton/README.md +++ b/src/new/templates/rust/ui/hyperapp-skeleton/README.md @@ -98,6 +98,18 @@ async fn my_endpoint(&self) -> String { } ``` +#### Remote Requests +All remote requests must use `.expects_response(30)` +```rust +let req = Request::to(("friend.os", "some-hyperapp", "some-hyperapp", "publisher.os")) + .expects_response(30) + .blob(LazyLoadBlob { + mime: None, + bytes: message, + }) + .body(body); +``` + #### Frontend API Calls Parameters must be sent as tuples for multi-parameter methods: ```typescript @@ -108,6 +120,14 @@ Parameters must be sent as tuples for multi-parameter methods: { "MethodName": [param1, param2] } ``` +#### Frontend keys in snake_case +All keys in TypeScript need to stay in snake_case (`node_id`), camelCase (`nodeId`) will break the app! +```typescript +export interface StatusSnapshot { + node_id: string; + } +``` + #### The /our.js Script MUST be included in index.html: ```html @@ -120,8 +140,8 @@ Your app's state is automatically persisted based on the `save_config` option: - `OnDiff`: Save when state changes (strongly recommended) - `Never`: No automatic saves - `EveryMessage`: Save after each message (safest; slowest) -- `EveeyNMessage(u64)`: Save every N messages received -- `EveeyNSeconds(u64)`: Save every N seconds +- `EveryNMessage(u64)`: Save every N messages received +- `EveryNSeconds(u64)`: Save every N seconds ## Customization Guide @@ -167,10 +187,6 @@ There is also a `request_networking` field that must be true to send messages ov 2. Update store in `ui/src/store/skeleton.ts` 3. Modify UI in `ui/src/App.tsx` -### 5. Rename as appropriate - -Change names throughout from `hyperapp-skeleton` (and variants) as appropriate if user describes app name. - ## Common Issues and Solutions ### "Failed to deserialize HTTP request" @@ -187,30 +203,26 @@ Change names throughout from `hyperapp-skeleton` (and variants) as appropriate i - Add #[derive(PartialEq)] to structs ### Import Errors -- Don't add `hyperware_process_lib` to Cargo.toml -- Use imports from `hyperprocess_macro` - -## Testing Your App +- Import the most important structs and functions from `hyperware_process_lib`, e.g. `Request`, `LazyLoadBlob`, `ProcessId` -1. Deploy app to a Hyperware node (after building, if requested): - ```bash - kit start-packages - ``` -2. Your app will be automatically installed and available at `http://localhost:8080` -3. Check the Hyperware homepage for your app icon +### manifest.json missing +- Run `kit b --hyperapp` to generate it ## Instructions ### Create an implementation plan Carefully read the prompt; look carefully at `instructions.md` (if it exists) and in the resources/ directory. -In particular, note the example applications `resources/example-apps/sign/`, `resources/example-apps/id/`, and `resources/example-apps/file-explorer`. Note that `file-explorer` example contains an `api`, which is generated by the compiler, and not human or LLM written. +In particular, note the example applications `resources/example-apps/sign/`, `resources/example-apps/id/`, and `resources/example-apps/file-explorer`. Note that `file-explorer` example contains an `api` folder, which is generated by the compiler, and not human or LLM written. `sign` and `id` demonstrate local messaging. `file-explorer` demonstrates VFS interactions. +<<<<<<< Updated upstream Expand the prompt and/or `instructions.md` into a detailed implementation plan. The implementor will be starting from this existing template that exists at `skeleton-app/` and `ui/`. +======= +>>>>>>> Stashed changes Note in particular that bindings for the UI will be generated when the app is built with `kit build --hyperapp`. As such, first design and implement the backend; the interface will be generated from the backend; finally design and implement the frontend to consume the interface. Subsequent changes to the interface must follow this pattern as well: start in backend, generate interface, finish in frontend @@ -219,15 +231,9 @@ Do NOT create the API. The API is machine generated. You create types that end up in the API by defining and using them in functions in the Rust backend "hyperapp" -Do NOT write code: just create a detailed `IMPLEMENTATION_PLAN.md` that will be used by the implementor. -The implementor will have access to `resources/` but will be working from `IMPLEMENTATION_PLAN.md`, so include all relevant context in the PLAN. -You can refer the implementor to `resources/` but do not assume the implementor has read them unless you refer them there. - ### Implement the plan -Look carefully at `IMPLEMENTATION_PLAN.md` and in the `resources/` directory, if relevant. -In particular, note the example applications `resources/example-apps/sign/`, `resources/example-apps/id/`, and `resources/example-apps/file-explorer`. -Use them if useful. +Look carefully at `IMPLEMENTATION_PLAN.md` and in the `example-apps/` directory, if relevant. Work from the existing template that exists at `skeleton-app/` and `ui/`. @@ -245,5 +251,3 @@ Notice that this all happens within those functions: just take the rust types as If you create a GUI for the app you MUST use target/ui/caller-utils.ts for HTTP requests to the backend. Do NOT edit this file: it is machine generated. Do NOT do `fetch` or other HTTP requests manually to the backend: use the functions in this machine generated interface. - -Implement the application described in the `IMPLEMENTATION_PLAN.md`. diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/Cargo.toml similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/Cargo.toml rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/Cargo.toml diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/api/file-explorer-sys-v0.wit b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/api/file-explorer-sys-v0.wit similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/api/file-explorer-sys-v0.wit rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/api/file-explorer-sys-v0.wit diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/api/file-explorer.wit b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/api/file-explorer.wit similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/api/file-explorer.wit rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/api/file-explorer.wit diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/api/types-file-explorer-sys-v0.wit b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/api/types-file-explorer-sys-v0.wit similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/api/types-file-explorer-sys-v0.wit rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/api/types-file-explorer-sys-v0.wit diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/explorer/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/Cargo.toml similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/explorer/Cargo.toml rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/Cargo.toml diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/explorer/src/icon b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/src/icon similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/explorer/src/icon rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/src/icon diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/explorer/src/lib.rs b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/src/lib.rs similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/explorer/src/lib.rs rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/explorer/src/lib.rs diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/metadata.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/metadata.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/metadata.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/metadata.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/pkg/manifest.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/pkg/manifest.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/pkg/manifest.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/pkg/manifest.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/.eslintrc.cjs b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/.eslintrc.cjs similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/.eslintrc.cjs rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/.eslintrc.cjs diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/.gitignore b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/.gitignore similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/.gitignore rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/.gitignore diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/README.md b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/README.md similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/README.md rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/README.md diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/index.html b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/index.html similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/index.html rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/index.html diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/package-lock.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/package-lock.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/package-lock.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/package-lock.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/package.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/package.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/package.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/package.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/App.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/App.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/App.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/App.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/App.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/App.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/App.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/App.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/assets/react.svg b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/assets/react.svg similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/assets/react.svg rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/assets/react.svg diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/assets/vite.svg b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/assets/vite.svg similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/assets/vite.svg rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/assets/vite.svg diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/ContextMenu/ContextMenu.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/ContextMenu/ContextMenu.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/ContextMenu/ContextMenu.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/ContextMenu/ContextMenu.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/ContextMenu/ContextMenu.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/ContextMenu/ContextMenu.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/ContextMenu/ContextMenu.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/ContextMenu/ContextMenu.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/Breadcrumb.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/Breadcrumb.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/Breadcrumb.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/Breadcrumb.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/Breadcrumb.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/Breadcrumb.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/Breadcrumb.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/Breadcrumb.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileExplorer.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileExplorer.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileExplorer.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileExplorer.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileExplorer.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileExplorer.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileExplorer.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileExplorer.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileItem.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileItem.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileItem.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileItem.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileItem.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileItem.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileItem.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileItem.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileList.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileList.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileList.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileList.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileList.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileList.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/FileList.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/FileList.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/Toolbar.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/Toolbar.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/Toolbar.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/Toolbar.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/Toolbar.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/Toolbar.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/FileExplorer/Toolbar.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/FileExplorer/Toolbar.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/ShareDialog/ShareDialog.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/ShareDialog/ShareDialog.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/ShareDialog/ShareDialog.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/ShareDialog/ShareDialog.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/ShareDialog/ShareDialog.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/ShareDialog/ShareDialog.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/ShareDialog/ShareDialog.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/ShareDialog/ShareDialog.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/Upload/UploadZone.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/Upload/UploadZone.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/Upload/UploadZone.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/Upload/UploadZone.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/Upload/UploadZone.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/Upload/UploadZone.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/components/Upload/UploadZone.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/components/Upload/UploadZone.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/contexts/ThemeContext.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/contexts/ThemeContext.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/contexts/ThemeContext.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/contexts/ThemeContext.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/index.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/index.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/index.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/index.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/lib/api.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/lib/api.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/lib/api.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/lib/api.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/main.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/main.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/main.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/main.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/store/fileExplorer.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/store/fileExplorer.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/store/fileExplorer.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/store/fileExplorer.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/types/api.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/types/api.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/types/api.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/types/api.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/types/global.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/types/global.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/types/global.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/types/global.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/vite-env.d.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/vite-env.d.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/src/vite-env.d.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/src/vite-env.d.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/tsconfig.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/tsconfig.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/tsconfig.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/tsconfig.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/tsconfig.node.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/tsconfig.node.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/tsconfig.node.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/tsconfig.node.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/vite.config.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/vite.config.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/file-explorer/ui/vite.config.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/file-explorer/ui/vite.config.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/Cargo.toml similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/Cargo.toml rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/Cargo.toml diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/README.md b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/README.md similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/README.md rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/README.md diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/id/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/Cargo.toml similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/id/Cargo.toml rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/Cargo.toml diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/id/src/icon b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/src/icon similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/id/src/icon rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/src/icon diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/id/src/lib.rs b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/src/lib.rs similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/id/src/lib.rs rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/id/src/lib.rs diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/metadata.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/metadata.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/metadata.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/metadata.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/pkg/manifest.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/pkg/manifest.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/pkg/manifest.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/pkg/manifest.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/.eslintrc.cjs b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/.eslintrc.cjs similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/.eslintrc.cjs rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/.eslintrc.cjs diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/.gitignore b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/.gitignore similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/.gitignore rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/.gitignore diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/README.md b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/README.md similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/README.md rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/README.md diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/index.html b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/index.html similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/index.html rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/index.html diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/package-lock.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/package-lock.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/package-lock.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/package-lock.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/package.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/package.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/package.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/package.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/public/assets/vite.svg b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/public/assets/vite.svg similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/public/assets/vite.svg rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/public/assets/vite.svg diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/App.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/App.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/App.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/App.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/App.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/App.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/App.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/App.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/assets/react.svg b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/assets/react.svg similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/assets/react.svg rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/assets/react.svg diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/assets/vite.svg b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/assets/vite.svg similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/assets/vite.svg rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/assets/vite.svg diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/index.css b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/index.css similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/index.css rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/index.css diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/main.tsx b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/main.tsx similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/main.tsx rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/main.tsx diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/store/id.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/store/id.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/store/id.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/store/id.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/types/Id.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/types/Id.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/types/Id.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/types/Id.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/types/global.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/types/global.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/types/global.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/types/global.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/vite-env.d.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/vite-env.d.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/src/vite-env.d.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/src/vite-env.d.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/tsconfig.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/tsconfig.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/tsconfig.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/tsconfig.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/tsconfig.node.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/tsconfig.node.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/tsconfig.node.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/tsconfig.node.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/vite.config.ts b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/vite.config.ts similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/id/ui/vite.config.ts rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/id/ui/vite.config.ts diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/Cargo.toml similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/Cargo.toml rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/Cargo.toml diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/README.md b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/README.md similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/README.md rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/README.md diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/metadata.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/metadata.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/metadata.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/metadata.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/pkg/manifest.json b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/pkg/manifest.json similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/pkg/manifest.json rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/pkg/manifest.json diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/sign/Cargo.toml b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/Cargo.toml similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/sign/Cargo.toml rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/Cargo.toml diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/sign/src/icon b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/src/icon similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/sign/src/icon rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/src/icon diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/sign/src/lib.rs b/src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/src/lib.rs similarity index 100% rename from src/new/templates/rust/ui/hyperapp-skeleton/resources/example-apps/sign/sign/src/lib.rs rename to src/new/templates/rust/ui/hyperapp-skeleton/example-apps/sign/sign/src/lib.rs diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/README.md b/src/new/templates/rust/ui/hyperapp-skeleton/resources/README.md deleted file mode 100644 index da38ba19..00000000 --- a/src/new/templates/rust/ui/hyperapp-skeleton/resources/README.md +++ /dev/null @@ -1,84 +0,0 @@ -# 📚 Hyperware Skeleton App Resources - -This directory contains all the resources needed to transform the skeleton app into any type of Hyperware application. - -## 📖 Development Guides - -The [`guides/`](./guides/) directory contains comprehensive documentation for building Hyperware apps: - -- **[Quick Reference](./guides/00-QUICK-REFERENCE.md)** - Essential rules and syntax -- **[Common Patterns](./guides/01-COMMON-PATTERNS.md)** - Ready-to-use code recipes -- **[Troubleshooting](./guides/02-TROUBLESHOOTING.md)** - Fix common errors -- **[WIT Types Guide](./guides/03-WIT-TYPES-DATA-MODELING.md)** - Data modeling constraints -- **[P2P Patterns](./guides/04-P2P-PATTERNS.md)** - Node-to-node communication -- **[Frontend Guide](./guides/05-UI-FRONTEND-GUIDE.md)** - React/TypeScript development -- **[Testing Guide](./guides/06-TESTING-DEBUGGING.md)** - Debug and test strategies -- **[Complete Examples](./guides/07-COMPLETE-EXAMPLES.md)** - Full working apps -- **[Manifest & Deployment](./guides/08-MANIFEST-AND-DEPLOYMENT.md)** - Understanding manifest.json -- **[Capabilities Guide](./guides/09-CAPABILITIES-GUIDE.md)** - System permissions reference - -See the [Guides README](./guides/README.md) for detailed navigation help. - -## 💡 Example App Ideas - -The [`example-apps/TODO.md`](./example-apps/TODO.md) file contains 12+ app ideas ranging from basic to advanced: - -- Todo lists and notepads -- P2P chat and file sharing -- Collaborative tools -- Games and marketplaces -- System utilities - -Each idea includes implementation notes and key concepts to demonstrate. - -## đŸŽ¯ How to Use These Resources - -### Starting a New App -1. Copy the skeleton app -2. Read the Quick Reference guide -3. Find a similar example in Complete Examples -4. Use Common Patterns for specific features - -### When You're Stuck -1. Check Troubleshooting for your error -2. Verify all requirements in Quick Reference -3. Look for working patterns in Complete Examples -4. Test with simpler code first - -### For Specific Features -- **State Management** → Common Patterns section 1 -- **P2P Communication** → P2P Patterns guide -- **File Handling** → Common Patterns section 4 -- **UI Development** → Frontend Guide - -## 🔑 Key Principles - -1. **Start Simple** - Get basic functionality working first -2. **Test Incrementally** - Don't write everything before testing -3. **Follow Patterns** - Use proven patterns from the guides -4. **Handle Errors** - Always provide user feedback -5. **Design for P2P** - Remember there's no central server - -## 📝 Quick Reminders - -### Must-Have Requirements -- `` in your HTML -- Tuple format `[p1, p2]` for multi-parameter calls -- `.expects_response(30)` on remote requests - -### Common Fixes -- **Build errors** → Usually missing requirements above -- **Type errors** → Use JSON strings for complex types -- **P2P failures** → Check node names and ProcessId format -- **UI issues** → Verify /our.js is included -- **manifest.json missing** → Run `kit b --hyperapp` to generate it -- **Capability errors** → Check Capabilities Guide for required permissions - -## 🚀 Next Steps - -1. Review the skeleton app's heavily commented `lib.rs` -2. Pick an example from Complete Examples to study -3. Start modifying the skeleton incrementally -4. Test with multiple nodes for P2P features - -Remember: The skeleton app is designed to compile and run immediately. Build on that working foundation! \ No newline at end of file diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/03-WIT-TYPES-DATA-MODELING.md b/src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/03-WIT-TYPES-DATA-MODELING.md deleted file mode 100644 index 2d3aa391..00000000 --- a/src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/03-WIT-TYPES-DATA-MODELING.md +++ /dev/null @@ -1,734 +0,0 @@ -# 📊 WIT Types & Data Modeling Guide - -## Understanding WIT (WebAssembly Interface Types) - -WIT is the type system that bridges your Rust code with the frontend. The hyperprocess macro automatically generates WIT files from your Rust types, but it has strict requirements. - -## Type Compatibility Matrix - -| Rust Type | WIT Type | Supported | Notes | -|-----------|----------|-----------|-------| -| `bool` | `bool` | ✅ | | -| `u8`, `u16`, `u32`, `u64` | `u8`, `u16`, `u32`, `u64` | ✅ | | -| `i8`, `i16`, `i32`, `i64` | `s8`, `s16`, `s32`, `s64` | ✅ | | -| `f32`, `f64` | `float32`, `float64` | ✅ | | -| `String` | `string` | ✅ | | -| `Vec` | `list` | ✅ | T must be supported | -| `Option` | `option` | ✅ | T must be supported | -| `(T1, T2, ...)` | `tuple` | ✅ | All T must be supported | -| `HashMap` | - | ❌ | Use `Vec<(K, V)>` | -| `HashSet` | - | ❌ | Use `Vec` | -| `[T; N]` | - | ❌ | Use `Vec` | -| `&str` | - | ❌ | Use `String` | -| `&[T]` | - | ❌ | Use `Vec` | -| Complex enums | - | âš ī¸ | Only simple variants | -| Trait objects | - | ❌ | Not supported | - -## Data Modeling Strategies - -### 1. Simple Types - Direct Mapping - -```rust -// ✅ These types map directly to WIT -#[derive(Serialize, Deserialize, PartialEq)] -pub struct User { - pub id: String, - pub name: String, - pub age: u32, - pub active: bool, - pub balance: f64, -} - -#[derive(Serialize, Deserialize, PartialEq)] -pub struct Response { - pub users: Vec, - pub total: u64, - pub page: Option, -} - -// Use in endpoint -#[http] -async fn get_users(&self, _request_body: String) -> Response { - Response { - users: self.users.clone(), - total: self.users.len() as u64, - page: Some(0), - } -} -``` - -### 2. Complex Types - JSON String Pattern - -```rust -// Internal complex type (not exposed via WIT) -#[derive(Serialize, Deserialize)] -struct ComplexGameState { - board: HashMap, - history: Vec, - timers: HashMap, - metadata: serde_json::Value, -} - -// ✅ Return as JSON string -#[http] -async fn get_game_state(&self, _request_body: String) -> String { - serde_json::to_string(&self.game_state).unwrap() -} - -// ✅ Accept as JSON string -#[http] -async fn update_game_state(&mut self, request_body: String) -> Result { - let state: ComplexGameState = serde_json::from_str(&request_body) - .map_err(|e| format!("Invalid game state: {}", e))?; - - self.game_state = state; - Ok("Updated".to_string()) -} -``` - -### 3. Enum Handling - -```rust -// ❌ WRONG - Complex enum variants not supported by WIT directly -pub enum GameEvent { - PlayerJoined { player_id: String, timestamp: u64 }, - MoveMade { from: Position, to: Position }, - GameEnded { winner: Option, reason: EndReason }, -} - -// ✅ PATTERN 1: Simple enum + data struct (WIT-compatible) -#[derive(Serialize, Deserialize, PartialEq)] -pub enum EventType { - PlayerJoined, - MoveMade, - GameEnded, -} - -#[derive(Serialize, Deserialize, PartialEq)] -pub struct GameEvent { - pub event_type: EventType, - pub player_id: Option, - pub from_position: Option, - pub to_position: Option, - pub winner: Option, - pub timestamp: u64, -} - -// ✅ PATTERN 2: Complex enums with mixed variants (JSON-only) -#[derive(Serialize, Deserialize)] -pub enum WsMessage { - // Simple variants work fine - Heartbeat, - Disconnect, - - // Complex variants with nested serde attributes - #[serde(rename_all = "camelCase")] - JoinRoom { - room_id: String, - auth_token: Option, - user_settings: UserSettings, - }, - - // Single data variants - Chat(String), - UpdateStatus(Status), -} - -// ✅ PATTERN 3: Tagged unions via JSON -#[derive(Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum GameEvent { - PlayerJoined { player_id: String, timestamp: u64 }, - MoveMade { from: Position, to: Position }, - GameEnded { winner: Option }, -} - -// Return as JSON string -#[http] -async fn get_events(&self, _request_body: String) -> String { - serde_json::to_string(&self.events).unwrap() -} -``` - -### 4. HashMap Replacement Patterns - -```rust -// ❌ WRONG - HashMap not supported -pub struct GameData { - pub players: HashMap, - pub scores: HashMap, -} - -// ✅ PATTERN 1: Use Vec of tuples -#[derive(Serialize, Deserialize, PartialEq)] -pub struct GameData { - pub players: Vec<(String, Player)>, - pub scores: Vec<(String, u32)>, -} - -// ✅ PATTERN 2: Separate key-value struct -#[derive(Serialize, Deserialize, PartialEq)] -pub struct PlayerEntry { - pub id: String, - pub player: Player, -} - -#[derive(Serialize, Deserialize, PartialEq)] -pub struct ScoreEntry { - pub player_id: String, - pub score: u32, -} - -#[derive(Serialize, Deserialize, PartialEq)] -pub struct GameData { - pub players: Vec, - pub scores: Vec, -} - -// ✅ PATTERN 3: Internal HashMap, external Vec -#[derive(Default, Serialize, Deserialize)] -pub struct AppState { - // Internal representation (not exposed) - players_map: HashMap, -} - -// Exposed via endpoints -#[http] -async fn get_players(&self, _request_body: String) -> Vec { - self.players_map.values().cloned().collect() -} - -#[http] -async fn get_player(&self, request_body: String) -> Result { - let id: String = serde_json::from_str(&request_body)?; - self.players_map.get(&id) - .cloned() - .ok_or_else(|| "Player not found".to_string()) -} -``` - -### 5. Nested Type Visibility - -```rust -// ❌ PROBLEM: WIT generator can't find NestedData -pub struct Response { - pub data: NestedData, -} - -pub struct NestedData { - pub items: Vec, -} - -pub struct Item { - pub id: String, -} - -// ✅ FIX 1: Ensure all types are referenced in endpoints -#[http] -async fn get_response(&self, _request_body: String) -> Response { ... } - -#[http] -async fn get_nested_data(&self, _request_body: String) -> NestedData { ... } - -#[http] -async fn get_item(&self, _request_body: String) -> Item { ... } - -// ✅ FIX 2: Flatten the structure -#[derive(Serialize, Deserialize, PartialEq)] -pub struct Response { - pub items: Vec, - pub metadata: ResponseMetadata, -} -``` - -## Design Patterns for Data Modeling - -### 1. Command Pattern for Complex Operations - -```rust -// Instead of complex parameters, use command objects -#[derive(Deserialize)] -pub struct CreateGameCommand { - pub name: String, - pub max_players: u8, - pub settings: GameSettings, -} - -#[derive(Deserialize)] -pub struct GameSettings { - pub time_limit: Option, - pub allow_spectators: bool, - pub game_mode: String, -} - -// ✅ Modern approach - Direct type deserialization -#[http(method = "POST")] -async fn create_game(&mut self, command: CreateGameCommand) -> Result { - // Process command directly - let game_id = self.create_game_from_command(command)?; - - Ok(GameInfo { - id: game_id, - status: GameStatus::Waiting, - }) -} - -// ✅ Legacy approach - Manual JSON parsing -#[http] -async fn create_game_legacy(&mut self, request_body: String) -> Result { - let command: CreateGameCommand = serde_json::from_str(&request_body)?; - - // Process command - let game_id = self.create_game_from_command(command)?; - - Ok(serde_json::json!({ "game_id": game_id }).to_string()) -} -``` - -### 2. View Pattern for Complex Queries - -```rust -// Internal complex state -struct Game { - id: String, - players: HashMap, - board: BoardState, - history: Vec, - // ... many more fields -} - -// Simplified view for API -#[derive(Serialize, Deserialize, PartialEq)] -pub struct GameView { - pub id: String, - pub player_count: u8, - pub current_turn: String, - pub status: GameStatus, -} - -#[derive(Serialize, Deserialize, PartialEq)] -pub struct GameDetailView { - pub id: String, - pub players: Vec, - pub board_state: String, // Serialized board - pub last_move: Option, -} - -// Expose views, not internal state -#[http] -async fn list_games(&self, _request_body: String) -> Vec { - self.games.values() - .map(|game| game.to_view()) - .collect() -} - -#[http] -async fn get_game_detail(&self, request_body: String) -> Result { - let id: String = serde_json::from_str(&request_body)?; - self.games.get(&id) - .map(|game| game.to_detail_view()) - .ok_or_else(|| "Game not found".to_string()) -} -``` - -### 3. Event Sourcing Pattern - -```rust -// Events as simple data -#[derive(Serialize, Deserialize, PartialEq)] -pub struct Event { - pub id: String, - pub timestamp: String, - pub event_type: String, - pub data: String, // JSON encoded event data -} - -// Store events, rebuild state -#[derive(Default, Serialize, Deserialize)] -pub struct AppState { - events: Vec, - // Cached current state (rebuilt from events) - #[serde(skip)] - current_state: Option, -} - -impl AppState { - fn rebuild_state(&mut self) { - let mut state = ComputedState::default(); - for event in &self.events { - state.apply_event(event); - } - self.current_state = Some(state); - } -} - -#[http] -async fn add_event(&mut self, request_body: String) -> Result { - let event: Event = serde_json::from_str(&request_body)?; - self.events.push(event); - self.rebuild_state(); - Ok("Event added".to_string()) -} -``` - -## Real-World Patterns from P2P Apps - -### Timestamp Handling (from samchat) - -```rust -// ❌ WRONG - chrono types not WIT-compatible -use chrono::{DateTime, Utc}; -pub struct Message { - pub timestamp: DateTime, -} - -// ✅ CORRECT - RFC3339 strings (sorts lexicographically!) -pub struct ChatMessage { - pub timestamp: String, // RFC3339 string for WIT compatibility -} - -// Usage -let current_time_str = Utc::now().to_rfc3339(); - -// Sorting works naturally with RFC3339 strings -conversation.messages.sort_by(|a, b| a.timestamp.cmp(&b.timestamp)); -``` - -### Complex Message Types with Optionals - -```rust -// P2P chat pattern: One type handles multiple scenarios -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] -pub struct ChatMessage { - pub id: String, - pub conversation_id: String, - pub sender: String, - pub recipient: Option, // None for group messages - pub recipients: Option>, // Some for group messages - pub content: String, - pub timestamp: String, - pub delivered: bool, - pub file_info: Option, // Optional attachment - pub reply_to: Option, // Optional reply -} - -// This avoids complex enums while supporting: -// - Direct messages (recipient = Some, recipients = None) -// - Group messages (recipient = None, recipients = Some) -// - Messages with/without files -// - Messages with/without replies -``` - -### HashMap in State, Vec in API - -```rust -// Internal state uses HashMap for efficiency -#[derive(Default, Serialize, Deserialize)] -pub struct SamchatState { - conversations: HashMap, - my_node_id: Option, -} - -// But expose as Vec through endpoints -#[http] -async fn get_conversations(&self, _request_body: String) -> Vec { - self.conversations.values() - .map(|conv| ConversationSummary { - id: conv.id.clone(), - participants: conv.participants.clone(), - last_updated: conv.last_updated.clone(), - is_group: conv.is_group, - group_name: conv.group_name.clone(), - }) - .collect() -} -``` - -### Binary Data Transfer - -```rust -// Backend: Vec for file data -#[http] -async fn upload_file(&mut self, file_name: String, mime_type: String, file_data: Vec) -> Result { - // Process binary data -} - -// Frontend TypeScript: number[] maps to Vec -export interface UploadFileRequest { - UploadFile: [string, string, number[]]; // file_name, mime_type, file_data -} -``` - -## TypeScript/JavaScript Compatibility - -### camelCase Serialization - -When your frontend uses TypeScript/JavaScript conventions, use serde's rename attributes: - -```rust -// ✅ Rust snake_case -> TypeScript camelCase -#[derive(Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct UserProfile { - pub user_id: String, // -> userId - pub display_name: String, // -> displayName - pub created_at: u64, // -> createdAt - pub is_active: bool, // -> isActive -} - -// ✅ Works with enums too -#[derive(Serialize, Deserialize)] -pub enum ApiMessage { - #[serde(rename_all = "camelCase")] - UserJoined { - user_id: String, - joined_at: u64, - }, - - #[serde(rename_all = "camelCase")] - MessageSent { - message_id: String, - sender_id: String, - sent_at: u64, - }, -} - -// ✅ Different rename patterns -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "PascalCase")] // For C# style -pub struct ConfigData { - pub app_name: String, // -> AppName - pub version: String, // -> Version -} - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] // For constants -pub struct Constants { - pub max_users: u32, // -> MAX_USERS - pub timeout_ms: u64, // -> TIMEOUT_MS -} -``` - -### Skip Serialization - -For internal fields that shouldn't be exposed: - -```rust -#[derive(Default, Serialize, Deserialize)] -pub struct AppState { - // Public fields - pub users: Vec, - pub settings: Settings, - - // Internal cache - not serialized - #[serde(skip)] - user_cache: HashMap, - - // Skip with default value on deserialize - #[serde(skip_deserializing, default)] - computed_stats: Stats, - - // Custom default function - #[serde(skip, default = "default_processors")] - processors: HashMap, -} - -fn default_processors() -> HashMap { - HashMap::new() -} -``` - -## Best Practices - -### 1. Always Add PartialEq - -```rust -// WIT-exposed types need PartialEq -#[derive(Serialize, Deserialize, PartialEq)] -pub struct MyType { - pub field: String, -} -``` - -### 2. Use Builder Pattern for Complex Types - -```rust -#[derive(Default)] -pub struct GameBuilder { - name: Option, - max_players: Option, - settings: GameSettings, -} - -impl GameBuilder { - pub fn name(mut self, name: String) -> Self { - self.name = Some(name); - self - } - - pub fn max_players(mut self, max: u8) -> Self { - self.max_players = Some(max); - self - } - - pub fn build(self) -> Result { - Ok(Game { - id: uuid::Uuid::new_v4().to_string(), - name: self.name.ok_or("Name required")?, - max_players: self.max_players.unwrap_or(4), - settings: self.settings, - // ... initialize other fields - }) - } -} -``` - -### 3. Version Your Data Models - -```rust -#[derive(Serialize, Deserialize)] -pub struct SaveData { - pub version: u32, - pub data: serde_json::Value, -} - -impl SaveData { - pub fn migrate(self) -> Result { - match self.version { - 1 => migrate_v1_to_v2(self.data), - 2 => Ok(serde_json::from_value(self.data)?), - _ => Err(format!("Unknown version: {}", self.version)), - } - } -} -``` - -### 4. Document Your Types - -```rust -/// Represents a player in the game -#[derive(Serialize, Deserialize, PartialEq)] -pub struct Player { - /// Unique identifier for the player - pub id: String, - - /// Display name chosen by the player - pub name: String, - - /// Current score in the game - pub score: u32, - - /// Whether the player is currently active - pub active: bool, -} -``` - -## Common Patterns Reference - -### Pattern 1: ID-based Lookups -```rust -// Store as HashMap internally, expose as list -pub struct AppState { - items_map: HashMap, -} - -#[http] -async fn get_item(&self, request_body: String) -> Result { - let id: String = serde_json::from_str(&request_body)?; - self.items_map.get(&id).cloned() - .ok_or_else(|| "Not found".to_string()) -} - -#[http] -async fn list_items(&self, _request_body: String) -> Vec { - self.items_map.values().cloned().collect() -} -``` - -### Pattern 2: Pagination -```rust -#[derive(Deserialize)] -pub struct PageRequest { - pub page: usize, - pub per_page: usize, -} - -#[derive(Serialize, PartialEq)] -pub struct PageResponse { - pub items: Vec, - pub total: usize, - pub page: usize, - pub per_page: usize, -} - -#[http] -async fn list_paginated(&self, request_body: String) -> PageResponse { - let req: PageRequest = serde_json::from_str(&request_body) - .unwrap_or(PageRequest { page: 0, per_page: 20 }); - - let start = req.page * req.per_page; - let items: Vec<_> = self.items - .iter() - .skip(start) - .take(req.per_page) - .cloned() - .collect(); - - PageResponse { - items, - total: self.items.len(), - page: req.page, - per_page: req.per_page, - } -} -``` - -### Pattern 3: Result Types -```rust -#[derive(Serialize, Deserialize, PartialEq)] -pub struct ApiResult { - pub success: bool, - pub data: Option, - pub error: Option, -} - -impl ApiResult { - pub fn ok(data: T) -> Self { - Self { - success: true, - data: Some(data), - error: None, - } - } - - pub fn err(error: String) -> Self { - Self { - success: false, - data: None, - error: Some(error), - } - } -} - -#[http] -async fn safe_operation(&mut self, request_body: String) -> ApiResult { - match self.do_operation(request_body) { - Ok(result) => ApiResult::ok(result), - Err(e) => ApiResult::err(e.to_string()), - } -} -``` - -## Remember - -1. **When in doubt, use JSON strings** - They always work -2. **All public fields** - WIT needs to see them -3. **Test incrementally** - Build often to catch type issues early -4. **Keep it simple** - Complex types cause problems -5. **Document patterns** - Future you will thank you - -## See Also - -- [Troubleshooting Guide](./02-TROUBLESHOOTING.md#error-found-types-used-that-are-neither-wit-built-ins-nor-defined-locally) - For WIT type errors -- [Common Patterns](./01-COMMON-PATTERNS.md) - For implementation examples -- [Complete Examples](./07-COMPLETE-EXAMPLES.md) - For real-world usage \ No newline at end of file diff --git a/src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/05-UI-FRONTEND-GUIDE.md b/src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/05-UI-FRONTEND-GUIDE.md deleted file mode 100644 index c78443ae..00000000 --- a/src/new/templates/rust/ui/hyperapp-skeleton/resources/guides/05-UI-FRONTEND-GUIDE.md +++ /dev/null @@ -1,1553 +0,0 @@ -# đŸ’ģ UI/Frontend Development Guide - -## Frontend Stack Overview - -- **React 18** - UI framework -- **TypeScript** - Type safety -- **Zustand** - State management -- **Vite** - Build tool -- **CSS Modules** or plain CSS - Styling - -## Critical Setup Requirements - -### 1. The `/our.js` Script (MANDATORY) - -```html - - - - - - - - - - My Hyperware App - - -
- - - -``` - -### 2. Global Types Setup - -```typescript -// src/types/global.ts -declare global { - interface Window { - our?: { - node: string; // e.g., "alice.os" - process: string; // e.g., "myapp:myapp:publisher.os" - }; - } -} - -export const BASE_URL = ''; // Empty in production - -export const isHyperwareEnvironment = (): boolean => { - return typeof window !== 'undefined' && window.our !== undefined; -}; - -export const getNodeId = (): string | null => { - return window.our?.node || null; -}; -``` - -## API Communication Patterns - -### 1. Basic API Service - -```typescript -// src/utils/api.ts -import { BASE_URL } from '../types/global'; - -// IMPORTANT: Backend HTTP methods return String or Result -// Complex data is serialized as JSON strings that must be parsed on frontend - -// Generic API call function -export async function makeApiCall( - method: string, - data?: TRequest -): Promise { - const body = data !== undefined - ? { [method]: data } - : { [method]: "" }; // Empty string for no params - - const response = await fetch(`${BASE_URL}/api`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - }); - - if (!response.ok) { - const error = await response.text(); - throw new Error(`API Error: ${response.status} - ${error}`); - } - - return response.json(); -} - -// Typed API methods -export const api = { - // No parameters - backend returns JSON string - async getStatus() { - const response = await makeApiCall('GetStatus', ""); - return JSON.parse(response) as StatusResponse; - }, - - // Single parameter - backend returns JSON string - async getItem(id: string) { - const response = await makeApiCall('GetItem', id); - return JSON.parse(response) as Item; - }, - - // Multiple parameters (as JSON object - common pattern) - async createItem(name: string, description: string) { - const response = await makeApiCall( - 'CreateItem', - JSON.stringify({ name, description }) - ); - return JSON.parse(response) as CreateResponse; - }, - - // Complex object (send as JSON string) - async updateSettings(settings: Settings) { - return makeApiCall( - 'UpdateSettings', - JSON.stringify(settings) - ); - }, -}; -``` - -### 2. Error Handling - -```typescript -// src/utils/errors.ts -export class ApiError extends Error { - constructor( - message: string, - public status?: number, - public details?: unknown - ) { - super(message); - this.name = 'ApiError'; - } -} - -export function getErrorMessage(error: unknown): string { - if (error instanceof ApiError) { - return error.message; - } - if (error instanceof Error) { - return error.message; - } - if (typeof error === 'string') { - return error; - } - return 'An unknown error occurred'; -} - -// Wrapper with error handling -export async function apiCallWithRetry( - apiCall: () => Promise, - maxRetries = 3 -): Promise { - let lastError: unknown; - - for (let i = 0; i < maxRetries; i++) { - try { - return await apiCall(); - } catch (error) { - lastError = error; - if (i < maxRetries - 1) { - // Exponential backoff - await new Promise(resolve => - setTimeout(resolve, Math.pow(2, i) * 1000) - ); - } - } - } - - throw lastError; -} -``` - -## State Management with Zustand - -### 1. Store Structure - -```typescript -// src/store/app.ts -import { create } from 'zustand'; -import { devtools, persist } from 'zustand/middleware'; -import { immer } from 'zustand/middleware/immer'; - -interface AppState { - // Connection - nodeId: string | null; - isConnected: boolean; - - // Data - items: Item[]; - currentItem: Item | null; - - // UI State - isLoading: boolean; - error: string | null; - - // Filters/Settings - filters: { - search: string; - category: string | null; - sortBy: 'name' | 'date' | 'priority'; - }; -} - -interface AppActions { - // Connection - initialize: () => void; - - // Data operations - fetchItems: () => Promise; - createItem: (data: CreateItemData) => Promise; - updateItem: (id: string, updates: Partial) => Promise; - deleteItem: (id: string) => Promise; - selectItem: (id: string | null) => void; - - // UI operations - setError: (error: string | null) => void; - clearError: () => void; - setFilter: (filter: Partial) => void; - - // P2P operations - syncWithNode: (nodeId: string) => Promise; -} - -export const useAppStore = create()( - devtools( - persist( - immer((set, get) => ({ - // Initial state - nodeId: null, - isConnected: false, - items: [], - currentItem: null, - isLoading: false, - error: null, - filters: { - search: '', - category: null, - sortBy: 'name', - }, - - // Actions - initialize: () => { - const nodeId = getNodeId(); - set(state => { - state.nodeId = nodeId; - state.isConnected = nodeId !== null; - }); - - if (nodeId) { - get().fetchItems(); - } - }, - - fetchItems: async () => { - set(state => { - state.isLoading = true; - state.error = null; - }); - - try { - const items = await api.getItems(); - set(state => { - state.items = items; - state.isLoading = false; - }); - } catch (error) { - set(state => { - state.error = getErrorMessage(error); - state.isLoading = false; - }); - } - }, - - createItem: async (data) => { - set(state => { state.isLoading = true; }); - - try { - const response = await api.createItem(data); - - // Optimistic update - const newItem: Item = { - id: response.id, - ...data, - createdAt: new Date().toISOString(), - }; - - set(state => { - state.items.push(newItem); - state.currentItem = newItem; - state.isLoading = false; - }); - - // Refresh to ensure consistency - await get().fetchItems(); - } catch (error) { - set(state => { - state.error = getErrorMessage(error); - state.isLoading = false; - }); - throw error; // Re-throw for form handling - } - }, - - // ... other actions - })), - { - name: 'app-storage', - partialize: (state) => ({ - // Only persist UI preferences, not data - filters: state.filters, - }), - } - ) - ) -); - -// Selector hooks -export const useItems = () => { - const { items, filters } = useAppStore(); - - return items.filter(item => { - if (filters.search && !item.name.toLowerCase().includes(filters.search.toLowerCase())) { - return false; - } - if (filters.category && item.category !== filters.category) { - return false; - } - return true; - }).sort((a, b) => { - switch (filters.sortBy) { - case 'name': - return a.name.localeCompare(b.name); - case 'date': - return b.createdAt.localeCompare(a.createdAt); - case 'priority': - return b.priority - a.priority; - } - }); -}; - -export const useCurrentItem = () => useAppStore(state => state.currentItem); -export const useIsLoading = () => useAppStore(state => state.isLoading); -export const useError = () => useAppStore(state => state.error); -``` - -### 2. React Components - -```typescript -// src/components/ItemList.tsx -import React, { useEffect } from 'react'; -import { useAppStore, useItems } from '../store/app'; -import { ErrorMessage } from './ErrorMessage'; -import { LoadingSpinner } from './LoadingSpinner'; - -export const ItemList: React.FC = () => { - const items = useItems(); - const { isLoading, error, selectItem, currentItem } = useAppStore(); - - if (error) return ; - if (isLoading && items.length === 0) return ; - - return ( -
- {items.map(item => ( -
selectItem(item.id)} - > -

{item.name}

-

{item.description}

- - {new Date(item.createdAt).toLocaleDateString()} - -
- ))} - - {items.length === 0 && ( -
-

No items found

- -
- )} -
- ); -}; -``` - -### 3. Forms with Validation - -```typescript -// src/components/CreateItemForm.tsx -import React, { useState } from 'react'; -import { useAppStore } from '../store/app'; - -interface FormData { - name: string; - description: string; - category: string; -} - -interface FormErrors { - name?: string; - description?: string; - category?: string; -} - -export const CreateItemForm: React.FC<{ onClose: () => void }> = ({ onClose }) => { - const { createItem, isLoading } = useAppStore(); - const [formData, setFormData] = useState({ - name: '', - description: '', - category: '', - }); - const [errors, setErrors] = useState({}); - const [submitError, setSubmitError] = useState(null); - - const validate = (): boolean => { - const newErrors: FormErrors = {}; - - if (!formData.name.trim()) { - newErrors.name = 'Name is required'; - } else if (formData.name.length < 3) { - newErrors.name = 'Name must be at least 3 characters'; - } - - if (!formData.description.trim()) { - newErrors.description = 'Description is required'; - } - - if (!formData.category) { - newErrors.category = 'Please select a category'; - } - - setErrors(newErrors); - return Object.keys(newErrors).length === 0; - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (!validate()) return; - - setSubmitError(null); - - try { - await createItem(formData); - onClose(); - } catch (error) { - setSubmitError(getErrorMessage(error)); - } - }; - - const handleChange = (field: keyof FormData) => ( - e: React.ChangeEvent - ) => { - setFormData(prev => ({ ...prev, [field]: e.target.value })); - // Clear error when user types - if (errors[field]) { - setErrors(prev => ({ ...prev, [field]: undefined })); - } - }; - - return ( -
-

Create New Item

- - {submitError && ( -
{submitError}
- )} - -
- - - {errors.name && {errors.name}} -
- -
- -