diff --git a/hello-world/cargo-generate.toml b/hello-world/cargo-generate.toml index 00d1294..fdab1c8 100644 --- a/hello-world/cargo-generate.toml +++ b/hello-world/cargo-generate.toml @@ -1,7 +1,7 @@ [placeholders] backend_type = { type = "string", prompt = "What's your backend weapon of choice?", choices = ["motoko", "rust"], default = "motoko" } # We're only supporting react at the moment, so this variable is not really used -frontend_type = { type = "string", prompt = "Pick a frontend framework:", choices = ["react"], default = "react" } +frontend_type = { type = "string", prompt = "Pick a frontend framework:", choices = ["react", "vue"], default = "react" } network_type = { type = "string", prompt = "Use the default network or a dockerized one?", choices = ["Default", "Docker"], default = "Default" } [conditional.'backend_type == "rust"'] @@ -10,6 +10,12 @@ ignore = [ "motoko-backend" ] [conditional.'backend_type == "motoko"'] ignore = [ "rust-backend" ] +[conditional.'frontend_type == "react"'] +ignore = [ "vue-frontend" ] + +[conditional.'frontend_type == "vue"'] +ignore = [ "react-frontend" ] + [hooks] -pre = ["rename-backend-dir.rhai"] +pre = ["rename-dirs.rhai"] post = ["../_shared/write-agent-files.rhai"] diff --git a/hello-world/frontend/.gitignore b/hello-world/react-frontend/.gitignore similarity index 100% rename from hello-world/frontend/.gitignore rename to hello-world/react-frontend/.gitignore diff --git a/hello-world/frontend/.node-version b/hello-world/react-frontend/.node-version similarity index 100% rename from hello-world/frontend/.node-version rename to hello-world/react-frontend/.node-version diff --git a/hello-world/frontend/app/eslint.config.js b/hello-world/react-frontend/app/eslint.config.js similarity index 100% rename from hello-world/frontend/app/eslint.config.js rename to hello-world/react-frontend/app/eslint.config.js diff --git a/hello-world/frontend/app/index.html b/hello-world/react-frontend/app/index.html similarity index 83% rename from hello-world/frontend/app/index.html rename to hello-world/react-frontend/app/index.html index b21cfb5..51113dd 100644 --- a/hello-world/frontend/app/index.html +++ b/hello-world/react-frontend/app/index.html @@ -5,7 +5,7 @@ - ICP Frontend Environment Variables + Hello World @@ -13,4 +13,4 @@ - \ No newline at end of file + diff --git a/hello-world/frontend/app/package.json b/hello-world/react-frontend/app/package.json similarity index 79% rename from hello-world/frontend/app/package.json rename to hello-world/react-frontend/app/package.json index 0fba1ff..15d633d 100644 --- a/hello-world/frontend/app/package.json +++ b/hello-world/react-frontend/app/package.json @@ -5,8 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "generate": "icp-bindgen --did-file ../../backend/backend.did --out-dir ./src/backend/api --force", - "build": "tsc -b && vite build", + "build": "vite build", "lint": "eslint .", "preview": "vite preview" }, @@ -17,7 +16,7 @@ }, "devDependencies": { "@eslint/js": "~9.33.0", - "@icp-sdk/bindgen": "~0.3.0", + "@icp-sdk/bindgen": "^0.4.0", "@types/react": "~19.1.10", "@types/react-dom": "~19.1.7", "@vitejs/plugin-react": "~5.0.0", diff --git a/hello-world/frontend/app/public/icp.svg b/hello-world/react-frontend/app/public/icp.svg similarity index 100% rename from hello-world/frontend/app/public/icp.svg rename to hello-world/react-frontend/app/public/icp.svg diff --git a/hello-world/react-frontend/app/public/react.svg b/hello-world/react-frontend/app/public/react.svg new file mode 100644 index 0000000..4dc8e4c --- /dev/null +++ b/hello-world/react-frontend/app/public/react.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/hello-world/frontend/app/public/vite.svg b/hello-world/react-frontend/app/public/vite.svg similarity index 100% rename from hello-world/frontend/app/public/vite.svg rename to hello-world/react-frontend/app/public/vite.svg diff --git a/hello-world/frontend/app/src/App.css b/hello-world/react-frontend/app/src/App.css similarity index 96% rename from hello-world/frontend/app/src/App.css rename to hello-world/react-frontend/app/src/App.css index 0504930..beb0677 100644 --- a/hello-world/frontend/app/src/App.css +++ b/hello-world/react-frontend/app/src/App.css @@ -99,6 +99,11 @@ gap: 12px; } +.brand-framework { + width: 40px; + height: 40px; +} + .brand-vite { width: 40px; height: 40px; diff --git a/hello-world/frontend/app/src/App.tsx b/hello-world/react-frontend/app/src/App.tsx similarity index 93% rename from hello-world/frontend/app/src/App.tsx rename to hello-world/react-frontend/app/src/App.tsx index ba964ed..e8813a0 100644 --- a/hello-world/frontend/app/src/App.tsx +++ b/hello-world/react-frontend/app/src/App.tsx @@ -43,9 +43,11 @@ function App() {
ICP logo + + React logo + + Vite logo
-

Frontend Environment Variables

+

Hello World

Call the backend canister and get a greeting.

diff --git a/hello-world/frontend/app/src/index.css b/hello-world/react-frontend/app/src/index.css similarity index 100% rename from hello-world/frontend/app/src/index.css rename to hello-world/react-frontend/app/src/index.css diff --git a/hello-world/frontend/app/src/main.tsx b/hello-world/react-frontend/app/src/main.tsx similarity index 100% rename from hello-world/frontend/app/src/main.tsx rename to hello-world/react-frontend/app/src/main.tsx diff --git a/hello-world/frontend/app/src/vite-env.d.ts b/hello-world/react-frontend/app/src/vite-env.d.ts similarity index 100% rename from hello-world/frontend/app/src/vite-env.d.ts rename to hello-world/react-frontend/app/src/vite-env.d.ts diff --git a/hello-world/frontend/app/tsconfig.app.json b/hello-world/react-frontend/app/tsconfig.app.json similarity index 100% rename from hello-world/frontend/app/tsconfig.app.json rename to hello-world/react-frontend/app/tsconfig.app.json diff --git a/hello-world/frontend/app/tsconfig.json b/hello-world/react-frontend/app/tsconfig.json similarity index 100% rename from hello-world/frontend/app/tsconfig.json rename to hello-world/react-frontend/app/tsconfig.json diff --git a/hello-world/frontend/app/tsconfig.node.json b/hello-world/react-frontend/app/tsconfig.node.json similarity index 100% rename from hello-world/frontend/app/tsconfig.node.json rename to hello-world/react-frontend/app/tsconfig.node.json diff --git a/hello-world/frontend/app/vite.config.ts b/hello-world/react-frontend/app/vite.config.ts similarity index 100% rename from hello-world/frontend/app/vite.config.ts rename to hello-world/react-frontend/app/vite.config.ts diff --git a/hello-world/frontend/canister.yaml b/hello-world/react-frontend/canister.yaml similarity index 66% rename from hello-world/frontend/canister.yaml rename to hello-world/react-frontend/canister.yaml index 4b72291..3a684e6 100644 --- a/hello-world/frontend/canister.yaml +++ b/hello-world/react-frontend/canister.yaml @@ -10,9 +10,5 @@ recipe: # Install the dependencies # Eventually you might want to use `npm ci` to lock your dependencies - npm install - # Generate the bindings from backend.did file in the backend canister - # You could choose to run this once manually and checkin the generated code - # instead of running it every single time - - npm run generate --prefix app - npm run build dir: app/dist diff --git a/hello-world/frontend/package.json b/hello-world/react-frontend/package.json similarity index 100% rename from hello-world/frontend/package.json rename to hello-world/react-frontend/package.json diff --git a/hello-world/frontend/tsconfig.node.json b/hello-world/react-frontend/tsconfig.node.json similarity index 100% rename from hello-world/frontend/tsconfig.node.json rename to hello-world/react-frontend/tsconfig.node.json diff --git a/hello-world/rename-backend-dir.rhai b/hello-world/rename-dirs.rhai similarity index 57% rename from hello-world/rename-backend-dir.rhai rename to hello-world/rename-dirs.rhai index 437c899..4bb57f8 100644 --- a/hello-world/rename-backend-dir.rhai +++ b/hello-world/rename-dirs.rhai @@ -1,4 +1,5 @@ // This script just renames the `-backend` directory to `backend` +// and renames the `-frontend` directory to `frontend` // By the time this script is called the conditionals would have filtered // all the unselected backend folders @@ -13,3 +14,14 @@ switch backend_type { file::rename("motoko-backend", "backend"); } } + +let frontend_type = variable::get("frontend_type"); +debug(`frontend_type: ${frontend_type}`); +switch frontend_type { + "react" => { + file::rename("react-frontend", "frontend"); + } + "vue" => { + file::rename("vue-frontend", "frontend"); + } +} diff --git a/hello-world/vue-frontend/.gitignore b/hello-world/vue-frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/hello-world/vue-frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/hello-world/vue-frontend/canister.yaml b/hello-world/vue-frontend/canister.yaml new file mode 100644 index 0000000..a9273aa --- /dev/null +++ b/hello-world/vue-frontend/canister.yaml @@ -0,0 +1,14 @@ +# yaml-language-server: $schema=https://github.com/dfinity/icp-cli/raw/refs/tags/v0.1.0/docs/schemas/canister-yaml-schema.json + +name: frontend + +recipe: + # https://github.com/dfinity/icp-cli-recipes/blob/main/recipes/asset-canister/README.md + type: "@dfinity/asset-canister@v2.1.0" + configuration: + build: + # Install the dependencies + # Eventually you might want to use `npm ci` to lock your dependencies + - npm install + - npm run build + dir: dist diff --git a/hello-world/vue-frontend/index.html b/hello-world/vue-frontend/index.html new file mode 100644 index 0000000..b219649 --- /dev/null +++ b/hello-world/vue-frontend/index.html @@ -0,0 +1,16 @@ + + + + + + + + Hello World + + + +
+ + + + diff --git a/hello-world/vue-frontend/package.json b/hello-world/vue-frontend/package.json new file mode 100644 index 0000000..8821e46 --- /dev/null +++ b/hello-world/vue-frontend/package.json @@ -0,0 +1,21 @@ +{ + "name": "MotokoVue_frontend", + "version": "0.0.0", + "scripts": { + "build": "vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "@icp-sdk/core": "^5.4.0", + "pinia": "^3.0.4", + "vue": "^3.0.0" + }, + "devDependencies": { + "@icp-sdk/bindgen": "^0.4.0", + "@vitejs/plugin-vue": "~6.0.7", + "@vue/tsconfig": "^0.9.1", + "sass-embedded": "^1.99.0", + "vite": "~7.1.11" + } +} diff --git a/hello-world/vue-frontend/public/icp.svg b/hello-world/vue-frontend/public/icp.svg new file mode 100644 index 0000000..a3142ea --- /dev/null +++ b/hello-world/vue-frontend/public/icp.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hello-world/vue-frontend/public/vite.svg b/hello-world/vue-frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/hello-world/vue-frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/hello-world/vue-frontend/public/vue.svg b/hello-world/vue-frontend/public/vue.svg new file mode 100644 index 0000000..a1d285e --- /dev/null +++ b/hello-world/vue-frontend/public/vue.svg @@ -0,0 +1,2 @@ + + diff --git a/hello-world/vue-frontend/src/App.scss b/hello-world/vue-frontend/src/App.scss new file mode 100644 index 0000000..beb0677 --- /dev/null +++ b/hello-world/vue-frontend/src/App.scss @@ -0,0 +1,120 @@ +/* Layout */ +.page { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 24px; +} + +.panel { + width: 100%; + max-width: 640px; + background: var(--panel); + border: 1px solid var(--border); + border-radius: 14px; + box-shadow: var(--shadow); + padding: 24px; + display: flex; + flex-direction: column; + align-items: center; +} + +.title { + margin: 10px 0 6px; + font-size: 22px; + line-height: 1.3; +} + +.subtitle { + margin: 0 0 20px; + color: var(--muted); + font-size: 14px; +} + +.form { + display: grid; + gap: 10px; + min-width: 70%; +} + +.form label { + font-weight: 600; + font-size: 14px; +} + +.controls { + display: grid; + grid-template-columns: 1fr auto; + gap: 10px; +} + +.input { + appearance: none; + background: var(--input); + color: var(--fg); + border: 1px solid var(--border); + border-radius: 10px; + padding: 10px 12px; + font-size: 16px; +} + +.input::placeholder { + color: var(--muted); +} + +.button { + appearance: none; + border: 1px solid transparent; + background: var(--accent); + color: var(--accent-fg); + border-radius: 10px; + padding: 10px 14px; + font-size: 14px; + font-weight: 600; + cursor: pointer; +} + +.button:hover { + filter: brightness(1.05); +} + +.button:active { + filter: brightness(0.95); +} + +.greeting { + margin-top: 16px; + padding-top: 12px; + border-top: 1px dashed var(--border); + min-height: 24px; + color: var(--fg); +} + +/* Brand header */ +.brand { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; +} + +.brand-framework { + width: 40px; + height: 40px; +} + +.brand-vite { + width: 40px; + height: 40px; +} + +.brand-icp { + width: 70px; + height: 70px; +} + +.brand .plus { + font-weight: 700; + color: var(--muted); +} diff --git a/hello-world/vue-frontend/src/App.vue b/hello-world/vue-frontend/src/App.vue new file mode 100644 index 0000000..5841879 --- /dev/null +++ b/hello-world/vue-frontend/src/App.vue @@ -0,0 +1,60 @@ + + + diff --git a/hello-world/vue-frontend/src/index.css b/hello-world/vue-frontend/src/index.css new file mode 100644 index 0000000..a802c30 --- /dev/null +++ b/hello-world/vue-frontend/src/index.css @@ -0,0 +1,103 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + /* Theme variables (light by default) */ + --bg: #ffffff; + --fg: #0f172a; + --muted: #475569; + --panel: #f8fafc; + --border: #e2e8f0; + --input: #ffffff; + --accent: #4f46e5; + --accent-fg: #ffffff; + --shadow: 0 1px 2px rgba(2, 6, 23, 0.08), 0 2px 6px rgba(2, 6, 23, 0.06); + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +html, body, #root { + height: 100%; + width: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; + background: var(--bg); + color: var(--fg); + font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, + Helvetica Neue, Arial, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: dark) { + :root { + --bg: #0b1220; + --fg: #e5e7eb; + --muted: #94a3b8; + --panel: #0f172a; + --border: #1f2937; + --input: #0b1220; + --accent: #6366f1; + --accent-fg: #ffffff; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 2px 6px rgba(0, 0, 0, 0.25); + } +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/hello-world/vue-frontend/src/main.js b/hello-world/vue-frontend/src/main.js new file mode 100644 index 0000000..548a192 --- /dev/null +++ b/hello-world/vue-frontend/src/main.js @@ -0,0 +1,7 @@ +import { createPinia } from 'pinia'; +import { createApp } from 'vue'; +import './index.css'; +import './App.scss'; +import App from './App.vue'; + +createApp(App).use(createPinia()).mount('#root'); diff --git a/hello-world/vue-frontend/src/vite-env.d.ts b/hello-world/vue-frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/hello-world/vue-frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/hello-world/vue-frontend/tsconfig.json b/hello-world/vue-frontend/tsconfig.json new file mode 100644 index 0000000..6dc43dd --- /dev/null +++ b/hello-world/vue-frontend/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "@vue/tsconfig/tsconfig.json", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "types": ["vite/client"] + }, + "include": ["src"] +} diff --git a/hello-world/vue-frontend/vite.config.js b/hello-world/vue-frontend/vite.config.js new file mode 100644 index 0000000..f766382 --- /dev/null +++ b/hello-world/vue-frontend/vite.config.js @@ -0,0 +1,33 @@ +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; +import { icpBindgen } from '@icp-sdk/bindgen/plugins/vite'; + +// Change these values to match your local replica. +// The `icp network start` command will print the root key +// and the `icp deploy` command will print the backend canister id. +const IC_ROOT_KEY_HEX = + "308182301d060d2b0601040182dc7c0503010201060c2b0601040182dc7c050302010361008b52b4994f94c7ce4be1c1542d7c81dc79fea17d49efe8fa42e8566373581d4b969c4a59e96a0ef51b711fe5027ec01601182519d0a788f4bfe388e593b97cd1d7e44904de79422430bca686ac8c21305b3397b5ba4d7037d17877312fb7ee34"; +const BACKEND_CANISTER_ID = "txyno-ch777-77776-aaaaq-cai"; + +export default defineConfig({ + plugins: [ + vue(), + icpBindgen({ + didFile: '../backend/backend.did', + outDir: './src/bindings' + }), + ], + server: { + headers: { + "Set-Cookie": `ic_env=${encodeURIComponent( + `ic_root_key=${IC_ROOT_KEY_HEX}&PUBLIC_CANISTER_ID:backend=${BACKEND_CANISTER_ID}` + )}; SameSite=Lax;`, + }, + proxy: { + '/api': { + target: 'http://127.0.0.1:8000', + changeOrigin: true, + }, + }, + }, +});