mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-11-06 17:14:28 +00:00
Implement instance creation and editing modal
This commit is contained in:
546
ui/package-lock.json
generated
546
ui/package-lock.json
generated
@@ -9,6 +9,9 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-checkbox": "^1.3.2",
|
||||||
|
"@radix-ui/react-dialog": "^1.1.14",
|
||||||
|
"@radix-ui/react-label": "^2.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@tailwindcss/vite": "^4.1.11",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
@@ -17,7 +20,8 @@
|
|||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwindcss": "^4.1.11"
|
"tailwindcss": "^4.1.11",
|
||||||
|
"zod": "^4.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.0.15",
|
"@types/node": "^24.0.15",
|
||||||
@@ -787,6 +791,42 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-checkbox": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.2",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-presence": "1.1.4",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||||
|
"@radix-ui/react-use-previous": "1.1.1",
|
||||||
|
"@radix-ui/react-use-size": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-compose-refs": {
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
@@ -802,6 +842,236 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog": {
|
||||||
|
"version": "1.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz",
|
||||||
|
"integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.2",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-context": "1.1.2",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.1.10",
|
||||||
|
"@radix-ui/react-focus-guards": "1.1.2",
|
||||||
|
"@radix-ui/react-focus-scope": "1.1.7",
|
||||||
|
"@radix-ui/react-id": "1.1.1",
|
||||||
|
"@radix-ui/react-portal": "1.1.9",
|
||||||
|
"@radix-ui/react-presence": "1.1.4",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-slot": "1.2.3",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||||
|
"aria-hidden": "^1.2.4",
|
||||||
|
"react-remove-scroll": "^2.6.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
|
"version": "1.1.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
|
||||||
|
"integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.2",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||||
|
"@radix-ui/react-use-escape-keydown": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-focus-guards": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-focus-scope": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-id": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-label": {
|
||||||
|
"version": "2.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz",
|
||||||
|
"integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.1.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-portal": {
|
||||||
|
"version": "1.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
||||||
|
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
@@ -820,6 +1090,124 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-controllable-state": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-effect-event": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-escape-keydown": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-previous": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-use-size": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rolldown/pluginutils": {
|
"node_modules/@rolldown/pluginutils": {
|
||||||
"version": "1.0.0-beta.27",
|
"version": "1.0.0-beta.27",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
||||||
@@ -1424,7 +1812,7 @@
|
|||||||
"version": "19.1.6",
|
"version": "19.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz",
|
||||||
"integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==",
|
"integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.0.0"
|
"@types/react": "^19.0.0"
|
||||||
@@ -1451,6 +1839,18 @@
|
|||||||
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/aria-hidden": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.25.1",
|
"version": "4.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
|
||||||
@@ -1576,6 +1976,12 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/detect-node-es": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.187",
|
"version": "1.5.187",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz",
|
||||||
@@ -1685,6 +2091,15 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-nonce": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/graceful-fs": {
|
"node_modules/graceful-fs": {
|
||||||
"version": "4.2.11",
|
"version": "4.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
@@ -2134,6 +2549,75 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-remove-scroll": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react-remove-scroll-bar": "^2.3.7",
|
||||||
|
"react-style-singleton": "^2.2.3",
|
||||||
|
"tslib": "^2.1.0",
|
||||||
|
"use-callback-ref": "^1.3.3",
|
||||||
|
"use-sidecar": "^1.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-remove-scroll-bar": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
|
||||||
|
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react-style-singleton": "^2.2.2",
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-style-singleton": {
|
||||||
|
"version": "2.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
|
||||||
|
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"get-nonce": "^1.0.0",
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.45.1",
|
"version": "4.45.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz",
|
||||||
@@ -2265,6 +2749,12 @@
|
|||||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
"node_modules/tw-animate-css": {
|
"node_modules/tw-animate-css": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.5.tgz",
|
||||||
@@ -2327,6 +2817,49 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-callback-ref": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/use-sidecar": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-node-es": "^1.1.0",
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "7.0.5",
|
"version": "7.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz",
|
||||||
@@ -2407,6 +2940,15 @@
|
|||||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "4.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.0.5.tgz",
|
||||||
|
"integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,9 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-checkbox": "^1.3.2",
|
||||||
|
"@radix-ui/react-dialog": "^1.1.14",
|
||||||
|
"@radix-ui/react-label": "^2.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@tailwindcss/vite": "^4.1.11",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
@@ -20,7 +23,8 @@
|
|||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwindcss": "^4.1.11"
|
"tailwindcss": "^4.1.11",
|
||||||
|
"zod": "^4.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.0.15",
|
"@types/node": "^24.0.15",
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
|
import InstanceModal from '@/components/InstanceModal'
|
||||||
|
import { CreateInstanceOptions } from '@/types/instance'
|
||||||
|
|
||||||
function Header() {
|
function Header() {
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||||
|
|
||||||
const handleCreateInstance = () => {
|
const handleCreateInstance = () => {
|
||||||
// TODO: Open create instance dialog
|
setIsModalOpen(true)
|
||||||
console.log('Create instance clicked')
|
}
|
||||||
|
|
||||||
|
const handleSaveInstance = (name: string, options: CreateInstanceOptions) => {
|
||||||
|
// TODO: Implement API call to create instance
|
||||||
|
console.log('Creating instance:', { name, options })
|
||||||
|
// For now, just log the data - you'll implement the API call later
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<header className="bg-white border-b border-gray-200">
|
<header className="bg-white border-b border-gray-200">
|
||||||
<div className="container mx-auto max-w-4xl px-4 py-4">
|
<div className="container mx-auto max-w-4xl px-4 py-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
@@ -20,6 +31,13 @@ function Header() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<InstanceModal
|
||||||
|
open={isModalOpen}
|
||||||
|
onOpenChange={setIsModalOpen}
|
||||||
|
onSave={handleSaveInstance}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
212
ui/src/components/InstanceModal.tsx
Normal file
212
ui/src/components/InstanceModal.tsx
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
// ui/src/components/InstanceModal.tsx
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { Label } from '@/components/ui/label'
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog'
|
||||||
|
import { CreateInstanceOptions, Instance } from '@/types/instance'
|
||||||
|
import { getBasicFields, getAdvancedFields } from '@/lib/zodFormUtils'
|
||||||
|
import { ChevronDown, ChevronRight } from 'lucide-react'
|
||||||
|
import ZodFormField from '@/components/ZodFormField'
|
||||||
|
|
||||||
|
interface InstanceModalProps {
|
||||||
|
open: boolean
|
||||||
|
onOpenChange: (open: boolean) => void
|
||||||
|
onSave: (name: string, options: CreateInstanceOptions) => void
|
||||||
|
instance?: Instance // For editing existing instance
|
||||||
|
}
|
||||||
|
|
||||||
|
const InstanceModal: React.FC<InstanceModalProps> = ({
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
onSave,
|
||||||
|
instance
|
||||||
|
}) => {
|
||||||
|
const isEditing = !!instance
|
||||||
|
|
||||||
|
const [instanceName, setInstanceName] = useState('')
|
||||||
|
const [formData, setFormData] = useState<CreateInstanceOptions>({})
|
||||||
|
const [showAdvanced, setShowAdvanced] = useState(false)
|
||||||
|
const [nameError, setNameError] = useState('')
|
||||||
|
|
||||||
|
// Get field lists dynamically from the type
|
||||||
|
const basicFields = getBasicFields()
|
||||||
|
const advancedFields = getAdvancedFields()
|
||||||
|
|
||||||
|
// Reset form when modal opens/closes or when instance changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
if (instance) {
|
||||||
|
// Populate form with existing instance data
|
||||||
|
setInstanceName(instance.name)
|
||||||
|
setFormData(instance.options || {})
|
||||||
|
} else {
|
||||||
|
// Reset form for new instance
|
||||||
|
setInstanceName('')
|
||||||
|
setFormData({
|
||||||
|
auto_restart: true, // Default value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setShowAdvanced(false) // Always start with basic view
|
||||||
|
setNameError('') // Reset any name errors
|
||||||
|
}
|
||||||
|
}, [open, instance])
|
||||||
|
|
||||||
|
const handleFieldChange = (key: keyof CreateInstanceOptions, value: any) => {
|
||||||
|
setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
[key]: value
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNameChange = (name: string) => {
|
||||||
|
setInstanceName(name)
|
||||||
|
// Validate instance name
|
||||||
|
if (!name.trim()) {
|
||||||
|
setNameError('Instance name is required')
|
||||||
|
} else if (!/^[a-zA-Z0-9-_]+$/.test(name)) {
|
||||||
|
setNameError('Instance name can only contain letters, numbers, hyphens, and underscores')
|
||||||
|
} else {
|
||||||
|
setNameError('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
// Validate instance name before saving
|
||||||
|
if (!instanceName.trim()) {
|
||||||
|
setNameError('Instance name is required')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up undefined values to avoid sending empty fields
|
||||||
|
const cleanOptions: CreateInstanceOptions = {}
|
||||||
|
Object.entries(formData).forEach(([key, value]) => {
|
||||||
|
if (value !== undefined && value !== '' && value !== null) {
|
||||||
|
// Handle arrays - don't include empty arrays
|
||||||
|
if (Array.isArray(value) && value.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
;(cleanOptions as any)[key] = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onSave(instanceName, cleanOptions)
|
||||||
|
onOpenChange(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
onOpenChange(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleAdvanced = () => {
|
||||||
|
setShowAdvanced(!showAdvanced)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent className="sm:max-w-[600px] max-h-[80vh] overflow-hidden flex flex-col">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>
|
||||||
|
{isEditing ? 'Edit Instance' : 'Create New Instance'}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
{isEditing
|
||||||
|
? 'Modify the instance configuration below.'
|
||||||
|
: 'Configure your new llama-server instance below.'}
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="flex-1 overflow-y-auto">
|
||||||
|
<div className="grid gap-6 py-4">
|
||||||
|
{/* Instance Name - Special handling since it's not in CreateInstanceOptions */}
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor="name">
|
||||||
|
Instance Name <span className="text-red-500">*</span>
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
value={instanceName}
|
||||||
|
onChange={(e) => handleNameChange(e.target.value)}
|
||||||
|
placeholder="my-instance"
|
||||||
|
disabled={isEditing} // Don't allow name changes when editing
|
||||||
|
className={nameError ? 'border-red-500' : ''}
|
||||||
|
/>
|
||||||
|
{nameError && (
|
||||||
|
<p className="text-sm text-red-500">{nameError}</p>
|
||||||
|
)}
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Unique identifier for the instance
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Basic Fields - Automatically generated from type */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h3 className="text-lg font-medium">Basic Configuration</h3>
|
||||||
|
{basicFields.map((fieldKey) => (
|
||||||
|
<ZodFormField
|
||||||
|
key={fieldKey}
|
||||||
|
fieldKey={fieldKey}
|
||||||
|
value={formData[fieldKey]}
|
||||||
|
onChange={handleFieldChange}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Advanced Fields Toggle */}
|
||||||
|
<div className="border-t pt-4">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={toggleAdvanced}
|
||||||
|
className="flex items-center gap-2 p-0 h-auto font-medium"
|
||||||
|
>
|
||||||
|
{showAdvanced ? (
|
||||||
|
<ChevronDown className="h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
)}
|
||||||
|
Advanced Configuration
|
||||||
|
<span className="text-muted-foreground text-sm font-normal">
|
||||||
|
({advancedFields.length} options)
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Advanced Fields - Automatically generated from type */}
|
||||||
|
{showAdvanced && (
|
||||||
|
<div className="space-y-4 pl-6 border-l-2 border-muted">
|
||||||
|
<div className="space-y-4">
|
||||||
|
{advancedFields.sort().map((fieldKey) => (
|
||||||
|
<ZodFormField
|
||||||
|
key={fieldKey}
|
||||||
|
fieldKey={fieldKey}
|
||||||
|
value={formData[fieldKey]}
|
||||||
|
onChange={handleFieldChange}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter className="pt-4 border-t">
|
||||||
|
<Button variant="outline" onClick={handleCancel}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleSave} disabled={!instanceName.trim() || !!nameError}>
|
||||||
|
{isEditing ? 'Save Changes' : 'Create Instance'}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InstanceModal
|
||||||
119
ui/src/components/ZodFormField.tsx
Normal file
119
ui/src/components/ZodFormField.tsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { Label } from '@/components/ui/label'
|
||||||
|
import { Checkbox } from '@/components/ui/checkbox'
|
||||||
|
import { CreateInstanceOptions } from '@/types/instance'
|
||||||
|
import { getFieldType, basicFieldsConfig } from '@/lib/zodFormUtils'
|
||||||
|
|
||||||
|
interface ZodFormFieldProps {
|
||||||
|
fieldKey: keyof CreateInstanceOptions
|
||||||
|
value: any
|
||||||
|
onChange: (key: keyof CreateInstanceOptions, value: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ZodFormField: React.FC<ZodFormFieldProps> = ({ fieldKey, value, onChange }) => {
|
||||||
|
// Get configuration for basic fields, or use field name for advanced fields
|
||||||
|
const config = basicFieldsConfig[fieldKey as string] || { label: fieldKey }
|
||||||
|
|
||||||
|
// Get type from Zod schema
|
||||||
|
const fieldType = getFieldType(fieldKey)
|
||||||
|
|
||||||
|
const handleChange = (newValue: any) => {
|
||||||
|
onChange(fieldKey, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderField = () => {
|
||||||
|
switch (fieldType) {
|
||||||
|
case 'boolean':
|
||||||
|
return (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id={fieldKey}
|
||||||
|
checked={value || false}
|
||||||
|
onCheckedChange={(checked) => handleChange(checked)}
|
||||||
|
/>
|
||||||
|
<Label htmlFor={fieldKey} className="text-sm font-normal">
|
||||||
|
{config.label}
|
||||||
|
{config.description && (
|
||||||
|
<span className="text-muted-foreground ml-1">- {config.description}</span>
|
||||||
|
)}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
case 'number':
|
||||||
|
return (
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor={fieldKey}>
|
||||||
|
{config.label}
|
||||||
|
{config.required && <span className="text-red-500 ml-1">*</span>}
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id={fieldKey}
|
||||||
|
type="number"
|
||||||
|
value={value || ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
const numValue = e.target.value ? parseFloat(e.target.value) : undefined
|
||||||
|
handleChange(numValue)
|
||||||
|
}}
|
||||||
|
placeholder={config.placeholder}
|
||||||
|
/>
|
||||||
|
{config.description && (
|
||||||
|
<p className="text-sm text-muted-foreground">{config.description}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
case 'array':
|
||||||
|
return (
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor={fieldKey}>
|
||||||
|
{config.label}
|
||||||
|
{config.required && <span className="text-red-500 ml-1">*</span>}
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id={fieldKey}
|
||||||
|
type="text"
|
||||||
|
value={Array.isArray(value) ? value.join(', ') : ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
const arrayValue = e.target.value
|
||||||
|
? e.target.value.split(',').map(s => s.trim()).filter(Boolean)
|
||||||
|
: undefined
|
||||||
|
handleChange(arrayValue)
|
||||||
|
}}
|
||||||
|
placeholder="item1, item2, item3"
|
||||||
|
/>
|
||||||
|
{config.description && (
|
||||||
|
<p className="text-sm text-muted-foreground">{config.description}</p>
|
||||||
|
)}
|
||||||
|
<p className="text-xs text-muted-foreground">Separate multiple values with commas</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
case 'text':
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor={fieldKey}>
|
||||||
|
{config.label}
|
||||||
|
{config.required && <span className="text-red-500 ml-1">*</span>}
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id={fieldKey}
|
||||||
|
type="text"
|
||||||
|
value={value || ''}
|
||||||
|
onChange={(e) => handleChange(e.target.value || undefined)}
|
||||||
|
placeholder={config.placeholder}
|
||||||
|
/>
|
||||||
|
{config.description && (
|
||||||
|
<p className="text-sm text-muted-foreground">{config.description}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="space-y-2">{renderField()}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ZodFormField
|
||||||
30
ui/src/components/ui/checkbox.tsx
Normal file
30
ui/src/components/ui/checkbox.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
||||||
|
import { CheckIcon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Checkbox({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<CheckboxPrimitive.Root
|
||||||
|
data-slot="checkbox"
|
||||||
|
className={cn(
|
||||||
|
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<CheckboxPrimitive.Indicator
|
||||||
|
data-slot="checkbox-indicator"
|
||||||
|
className="flex items-center justify-center text-current transition-none"
|
||||||
|
>
|
||||||
|
<CheckIcon className="size-3.5" />
|
||||||
|
</CheckboxPrimitive.Indicator>
|
||||||
|
</CheckboxPrimitive.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Checkbox }
|
||||||
141
ui/src/components/ui/dialog.tsx
Normal file
141
ui/src/components/ui/dialog.tsx
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||||
|
import { XIcon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Dialog({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||||
|
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogTrigger({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||||
|
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogPortal({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||||
|
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogClose({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||||
|
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogOverlay({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||||
|
return (
|
||||||
|
<DialogPrimitive.Overlay
|
||||||
|
data-slot="dialog-overlay"
|
||||||
|
className={cn(
|
||||||
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogContent({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
showCloseButton = true,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
||||||
|
showCloseButton?: boolean
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<DialogPortal data-slot="dialog-portal">
|
||||||
|
<DialogOverlay />
|
||||||
|
<DialogPrimitive.Content
|
||||||
|
data-slot="dialog-content"
|
||||||
|
className={cn(
|
||||||
|
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
{showCloseButton && (
|
||||||
|
<DialogPrimitive.Close
|
||||||
|
data-slot="dialog-close"
|
||||||
|
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||||
|
>
|
||||||
|
<XIcon />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</DialogPrimitive.Close>
|
||||||
|
)}
|
||||||
|
</DialogPrimitive.Content>
|
||||||
|
</DialogPortal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="dialog-header"
|
||||||
|
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="dialog-footer"
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogTitle({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||||
|
return (
|
||||||
|
<DialogPrimitive.Title
|
||||||
|
data-slot="dialog-title"
|
||||||
|
className={cn("text-lg leading-none font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogDescription({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||||
|
return (
|
||||||
|
<DialogPrimitive.Description
|
||||||
|
data-slot="dialog-description"
|
||||||
|
className={cn("text-muted-foreground text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogOverlay,
|
||||||
|
DialogPortal,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
}
|
||||||
21
ui/src/components/ui/input.tsx
Normal file
21
ui/src/components/ui/input.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
data-slot="input"
|
||||||
|
className={cn(
|
||||||
|
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
|
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||||
|
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Input }
|
||||||
22
ui/src/components/ui/label.tsx
Normal file
22
ui/src/components/ui/label.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Label({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
data-slot="label"
|
||||||
|
className={cn(
|
||||||
|
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Label }
|
||||||
49
ui/src/lib/zodFormUtils.ts
Normal file
49
ui/src/lib/zodFormUtils.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { CreateInstanceOptions, getAllFieldKeys } from '@/schemas/instanceOptions'
|
||||||
|
|
||||||
|
// Only define the basic fields we want to show by default
|
||||||
|
export const basicFieldsConfig: Record<string, {
|
||||||
|
label: string
|
||||||
|
description?: string
|
||||||
|
placeholder?: string
|
||||||
|
required?: boolean
|
||||||
|
}> = {
|
||||||
|
auto_restart: {
|
||||||
|
label: 'Auto Restart',
|
||||||
|
description: 'Automatically restart the instance on failure'
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
label: 'Model Path',
|
||||||
|
placeholder: '/path/to/model.gguf',
|
||||||
|
description: 'Path to the model file'
|
||||||
|
},
|
||||||
|
hf_repo: {
|
||||||
|
label: 'Hugging Face Repository',
|
||||||
|
placeholder: 'microsoft/DialoGPT-medium',
|
||||||
|
description: 'Hugging Face model repository'
|
||||||
|
},
|
||||||
|
hf_file: {
|
||||||
|
label: 'Hugging Face File',
|
||||||
|
placeholder: 'model.gguf',
|
||||||
|
description: 'Specific file in the repository'
|
||||||
|
},
|
||||||
|
gpu_layers: {
|
||||||
|
label: 'GPU Layers',
|
||||||
|
placeholder: '0',
|
||||||
|
description: 'Number of layers to offload to GPU'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBasicField(key: keyof CreateInstanceOptions): boolean {
|
||||||
|
return key in basicFieldsConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBasicFields(): (keyof CreateInstanceOptions)[] {
|
||||||
|
return Object.keys(basicFieldsConfig) as (keyof CreateInstanceOptions)[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAdvancedFields(): (keyof CreateInstanceOptions)[] {
|
||||||
|
return getAllFieldKeys().filter(key => !isBasicField(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-export the Zod-based functions
|
||||||
|
export { getFieldType } from '@/schemas/instanceOptions'
|
||||||
199
ui/src/schemas/instanceOptions.ts
Normal file
199
ui/src/schemas/instanceOptions.ts
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
// Define the Zod schema
|
||||||
|
export const CreateInstanceOptionsSchema = z.object({
|
||||||
|
// Restart options
|
||||||
|
auto_restart: z.boolean().optional(),
|
||||||
|
max_restarts: z.number().optional(),
|
||||||
|
restart_delay: z.number().optional(),
|
||||||
|
|
||||||
|
// Common params
|
||||||
|
verbose_prompt: z.boolean().optional(),
|
||||||
|
threads: z.number().optional(),
|
||||||
|
threads_batch: z.number().optional(),
|
||||||
|
cpu_mask: z.string().optional(),
|
||||||
|
cpu_range: z.string().optional(),
|
||||||
|
cpu_strict: z.number().optional(),
|
||||||
|
priority: z.number().optional(),
|
||||||
|
poll: z.number().optional(),
|
||||||
|
cpu_mask_batch: z.string().optional(),
|
||||||
|
cpu_range_batch: z.string().optional(),
|
||||||
|
cpu_strict_batch: z.number().optional(),
|
||||||
|
priority_batch: z.number().optional(),
|
||||||
|
poll_batch: z.number().optional(),
|
||||||
|
ctx_size: z.number().optional(),
|
||||||
|
predict: z.number().optional(),
|
||||||
|
batch_size: z.number().optional(),
|
||||||
|
ubatch_size: z.number().optional(),
|
||||||
|
keep: z.number().optional(),
|
||||||
|
flash_attn: z.boolean().optional(),
|
||||||
|
no_perf: z.boolean().optional(),
|
||||||
|
escape: z.boolean().optional(),
|
||||||
|
no_escape: z.boolean().optional(),
|
||||||
|
rope_scaling: z.string().optional(),
|
||||||
|
rope_scale: z.number().optional(),
|
||||||
|
rope_freq_base: z.number().optional(),
|
||||||
|
rope_freq_scale: z.number().optional(),
|
||||||
|
yarn_orig_ctx: z.number().optional(),
|
||||||
|
yarn_ext_factor: z.number().optional(),
|
||||||
|
yarn_attn_factor: z.number().optional(),
|
||||||
|
yarn_beta_slow: z.number().optional(),
|
||||||
|
yarn_beta_fast: z.number().optional(),
|
||||||
|
dump_kv_cache: z.boolean().optional(),
|
||||||
|
no_kv_offload: z.boolean().optional(),
|
||||||
|
cache_type_k: z.string().optional(),
|
||||||
|
cache_type_v: z.string().optional(),
|
||||||
|
defrag_thold: z.number().optional(),
|
||||||
|
parallel: z.number().optional(),
|
||||||
|
mlock: z.boolean().optional(),
|
||||||
|
no_mmap: z.boolean().optional(),
|
||||||
|
numa: z.string().optional(),
|
||||||
|
device: z.string().optional(),
|
||||||
|
override_tensor: z.array(z.string()).optional(),
|
||||||
|
gpu_layers: z.number().optional(),
|
||||||
|
split_mode: z.string().optional(),
|
||||||
|
tensor_split: z.string().optional(),
|
||||||
|
main_gpu: z.number().optional(),
|
||||||
|
check_tensors: z.boolean().optional(),
|
||||||
|
override_kv: z.array(z.string()).optional(),
|
||||||
|
lora: z.array(z.string()).optional(),
|
||||||
|
lora_scaled: z.array(z.string()).optional(),
|
||||||
|
control_vector: z.array(z.string()).optional(),
|
||||||
|
control_vector_scaled: z.array(z.string()).optional(),
|
||||||
|
control_vector_layer_range: z.string().optional(),
|
||||||
|
model: z.string().optional(),
|
||||||
|
model_url: z.string().optional(),
|
||||||
|
hf_repo: z.string().optional(),
|
||||||
|
hf_repo_draft: z.string().optional(),
|
||||||
|
hf_file: z.string().optional(),
|
||||||
|
hf_repo_v: z.string().optional(),
|
||||||
|
hf_file_v: z.string().optional(),
|
||||||
|
hf_token: z.string().optional(),
|
||||||
|
log_disable: z.boolean().optional(),
|
||||||
|
log_file: z.string().optional(),
|
||||||
|
log_colors: z.boolean().optional(),
|
||||||
|
verbose: z.boolean().optional(),
|
||||||
|
verbosity: z.number().optional(),
|
||||||
|
log_prefix: z.boolean().optional(),
|
||||||
|
log_timestamps: z.boolean().optional(),
|
||||||
|
|
||||||
|
// Sampling params
|
||||||
|
samplers: z.string().optional(),
|
||||||
|
seed: z.number().optional(),
|
||||||
|
sampling_seq: z.string().optional(),
|
||||||
|
ignore_eos: z.boolean().optional(),
|
||||||
|
temperature: z.number().optional(),
|
||||||
|
top_k: z.number().optional(),
|
||||||
|
top_p: z.number().optional(),
|
||||||
|
min_p: z.number().optional(),
|
||||||
|
xtc_probability: z.number().optional(),
|
||||||
|
xtc_threshold: z.number().optional(),
|
||||||
|
typical: z.number().optional(),
|
||||||
|
repeat_last_n: z.number().optional(),
|
||||||
|
repeat_penalty: z.number().optional(),
|
||||||
|
presence_penalty: z.number().optional(),
|
||||||
|
frequency_penalty: z.number().optional(),
|
||||||
|
dry_multiplier: z.number().optional(),
|
||||||
|
dry_base: z.number().optional(),
|
||||||
|
dry_allowed_length: z.number().optional(),
|
||||||
|
dry_penalty_last_n: z.number().optional(),
|
||||||
|
dry_sequence_breaker: z.array(z.string()).optional(),
|
||||||
|
dynatemp_range: z.number().optional(),
|
||||||
|
dynatemp_exp: z.number().optional(),
|
||||||
|
mirostat: z.number().optional(),
|
||||||
|
mirostat_lr: z.number().optional(),
|
||||||
|
mirostat_ent: z.number().optional(),
|
||||||
|
logit_bias: z.array(z.string()).optional(),
|
||||||
|
grammar: z.string().optional(),
|
||||||
|
grammar_file: z.string().optional(),
|
||||||
|
json_schema: z.string().optional(),
|
||||||
|
json_schema_file: z.string().optional(),
|
||||||
|
|
||||||
|
// Server/Example-specific params
|
||||||
|
no_context_shift: z.boolean().optional(),
|
||||||
|
special: z.boolean().optional(),
|
||||||
|
no_warmup: z.boolean().optional(),
|
||||||
|
spm_infill: z.boolean().optional(),
|
||||||
|
pooling: z.string().optional(),
|
||||||
|
cont_batching: z.boolean().optional(),
|
||||||
|
no_cont_batching: z.boolean().optional(),
|
||||||
|
mmproj: z.string().optional(),
|
||||||
|
mmproj_url: z.string().optional(),
|
||||||
|
no_mmproj: z.boolean().optional(),
|
||||||
|
no_mmproj_offload: z.boolean().optional(),
|
||||||
|
alias: z.string().optional(),
|
||||||
|
host: z.string().optional(),
|
||||||
|
port: z.number().optional(),
|
||||||
|
path: z.string().optional(),
|
||||||
|
no_webui: z.boolean().optional(),
|
||||||
|
embedding: z.boolean().optional(),
|
||||||
|
reranking: z.boolean().optional(),
|
||||||
|
api_key: z.string().optional(),
|
||||||
|
api_key_file: z.string().optional(),
|
||||||
|
ssl_key_file: z.string().optional(),
|
||||||
|
ssl_cert_file: z.string().optional(),
|
||||||
|
chat_template_kwargs: z.string().optional(),
|
||||||
|
timeout: z.number().optional(),
|
||||||
|
threads_http: z.number().optional(),
|
||||||
|
cache_reuse: z.number().optional(),
|
||||||
|
metrics: z.boolean().optional(),
|
||||||
|
slots: z.boolean().optional(),
|
||||||
|
props: z.boolean().optional(),
|
||||||
|
no_slots: z.boolean().optional(),
|
||||||
|
slot_save_path: z.string().optional(),
|
||||||
|
jinja: z.boolean().optional(),
|
||||||
|
reasoning_format: z.string().optional(),
|
||||||
|
reasoning_budget: z.number().optional(),
|
||||||
|
chat_template: z.string().optional(),
|
||||||
|
chat_template_file: z.string().optional(),
|
||||||
|
no_prefill_assistant: z.boolean().optional(),
|
||||||
|
slot_prompt_similarity: z.number().optional(),
|
||||||
|
lora_init_without_apply: z.boolean().optional(),
|
||||||
|
|
||||||
|
// Speculative decoding params
|
||||||
|
draft_max: z.number().optional(),
|
||||||
|
draft_min: z.number().optional(),
|
||||||
|
draft_p_min: z.number().optional(),
|
||||||
|
ctx_size_draft: z.number().optional(),
|
||||||
|
device_draft: z.string().optional(),
|
||||||
|
gpu_layers_draft: z.number().optional(),
|
||||||
|
model_draft: z.string().optional(),
|
||||||
|
cache_type_k_draft: z.string().optional(),
|
||||||
|
cache_type_v_draft: z.string().optional(),
|
||||||
|
|
||||||
|
// Audio/TTS params
|
||||||
|
model_vocoder: z.string().optional(),
|
||||||
|
tts_use_guide_tokens: z.boolean().optional(),
|
||||||
|
|
||||||
|
// Default model params
|
||||||
|
embd_bge_small_en_default: z.boolean().optional(),
|
||||||
|
embd_e5_small_en_default: z.boolean().optional(),
|
||||||
|
embd_gte_small_default: z.boolean().optional(),
|
||||||
|
fim_qwen_1_5b_default: z.boolean().optional(),
|
||||||
|
fim_qwen_3b_default: z.boolean().optional(),
|
||||||
|
fim_qwen_7b_default: z.boolean().optional(),
|
||||||
|
fim_qwen_7b_spec: z.boolean().optional(),
|
||||||
|
fim_qwen_14b_spec: z.boolean().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Infer the TypeScript type from the schema
|
||||||
|
export type CreateInstanceOptions = z.infer<typeof CreateInstanceOptionsSchema>
|
||||||
|
|
||||||
|
// Helper to get all field keys
|
||||||
|
export function getAllFieldKeys(): (keyof CreateInstanceOptions)[] {
|
||||||
|
return Object.keys(CreateInstanceOptionsSchema.shape) as (keyof CreateInstanceOptions)[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get field type from Zod schema
|
||||||
|
export function getFieldType(key: keyof CreateInstanceOptions): 'text' | 'number' | 'boolean' | 'array' {
|
||||||
|
const fieldSchema = CreateInstanceOptionsSchema.shape[key]
|
||||||
|
if (!fieldSchema) return 'text'
|
||||||
|
|
||||||
|
// Handle ZodOptional wrapper
|
||||||
|
const innerSchema = fieldSchema instanceof z.ZodOptional ? fieldSchema.unwrap() : fieldSchema
|
||||||
|
|
||||||
|
if (innerSchema instanceof z.ZodBoolean) return 'boolean'
|
||||||
|
if (innerSchema instanceof z.ZodNumber) return 'number'
|
||||||
|
if (innerSchema instanceof z.ZodArray) return 'array'
|
||||||
|
return 'text' // ZodString and others default to text
|
||||||
|
}
|
||||||
@@ -1,181 +1,9 @@
|
|||||||
|
import { CreateInstanceOptions } from '@/schemas/instanceOptions'
|
||||||
|
|
||||||
|
export { type CreateInstanceOptions } from '@/schemas/instanceOptions'
|
||||||
|
|
||||||
export interface Instance {
|
export interface Instance {
|
||||||
name: string;
|
name: string;
|
||||||
running: boolean;
|
running: boolean;
|
||||||
options?: CreateInstanceOptions;
|
options?: CreateInstanceOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateInstanceOptions {
|
|
||||||
|
|
||||||
auto_restart?: boolean;
|
|
||||||
max_restarts?: number;
|
|
||||||
restart_delay?: number;
|
|
||||||
|
|
||||||
// Llama server options
|
|
||||||
// Common params
|
|
||||||
verbose_prompt?: boolean;
|
|
||||||
threads?: number;
|
|
||||||
threads_batch?: number;
|
|
||||||
cpu_mask?: string;
|
|
||||||
cpu_range?: string;
|
|
||||||
cpu_strict?: number;
|
|
||||||
priority?: number;
|
|
||||||
poll?: number;
|
|
||||||
cpu_mask_batch?: string;
|
|
||||||
cpu_range_batch?: string;
|
|
||||||
cpu_strict_batch?: number;
|
|
||||||
priority_batch?: number;
|
|
||||||
poll_batch?: number;
|
|
||||||
ctx_size?: number;
|
|
||||||
predict?: number;
|
|
||||||
batch_size?: number;
|
|
||||||
ubatch_size?: number;
|
|
||||||
keep?: number;
|
|
||||||
flash_attn?: boolean;
|
|
||||||
no_perf?: boolean;
|
|
||||||
escape?: boolean;
|
|
||||||
no_escape?: boolean;
|
|
||||||
rope_scaling?: string;
|
|
||||||
rope_scale?: number;
|
|
||||||
rope_freq_base?: number;
|
|
||||||
rope_freq_scale?: number;
|
|
||||||
yarn_orig_ctx?: number;
|
|
||||||
yarn_ext_factor?: number;
|
|
||||||
yarn_attn_factor?: number;
|
|
||||||
yarn_beta_slow?: number;
|
|
||||||
yarn_beta_fast?: number;
|
|
||||||
dump_kv_cache?: boolean;
|
|
||||||
no_kv_offload?: boolean;
|
|
||||||
cache_type_k?: string;
|
|
||||||
cache_type_v?: string;
|
|
||||||
defrag_thold?: number;
|
|
||||||
parallel?: number;
|
|
||||||
mlock?: boolean;
|
|
||||||
no_mmap?: boolean;
|
|
||||||
numa?: string;
|
|
||||||
device?: string;
|
|
||||||
override_tensor?: string[];
|
|
||||||
gpu_layers?: number;
|
|
||||||
split_mode?: string;
|
|
||||||
tensor_split?: string;
|
|
||||||
main_gpu?: number;
|
|
||||||
check_tensors?: boolean;
|
|
||||||
override_kv?: string[];
|
|
||||||
lora?: string[];
|
|
||||||
lora_scaled?: string[];
|
|
||||||
control_vector?: string[];
|
|
||||||
control_vector_scaled?: string[];
|
|
||||||
control_vector_layer_range?: string;
|
|
||||||
model?: string;
|
|
||||||
model_url?: string;
|
|
||||||
hf_repo?: string;
|
|
||||||
hf_repo_draft?: string;
|
|
||||||
hf_file?: string;
|
|
||||||
hf_repo_v?: string;
|
|
||||||
hf_file_v?: string;
|
|
||||||
hf_token?: string;
|
|
||||||
log_disable?: boolean;
|
|
||||||
log_file?: string;
|
|
||||||
log_colors?: boolean;
|
|
||||||
verbose?: boolean;
|
|
||||||
verbosity?: number;
|
|
||||||
log_prefix?: boolean;
|
|
||||||
log_timestamps?: boolean;
|
|
||||||
|
|
||||||
// Sampling params
|
|
||||||
samplers?: string;
|
|
||||||
seed?: number;
|
|
||||||
sampling_seq?: string;
|
|
||||||
ignore_eos?: boolean;
|
|
||||||
temperature?: number;
|
|
||||||
top_k?: number;
|
|
||||||
top_p?: number;
|
|
||||||
min_p?: number;
|
|
||||||
xtc_probability?: number;
|
|
||||||
xtc_threshold?: number;
|
|
||||||
typical?: number;
|
|
||||||
repeat_last_n?: number;
|
|
||||||
repeat_penalty?: number;
|
|
||||||
presence_penalty?: number;
|
|
||||||
frequency_penalty?: number;
|
|
||||||
dry_multiplier?: number;
|
|
||||||
dry_base?: number;
|
|
||||||
dry_allowed_length?: number;
|
|
||||||
dry_penalty_last_n?: number;
|
|
||||||
dry_sequence_breaker?: string[];
|
|
||||||
dynatemp_range?: number;
|
|
||||||
dynatemp_exp?: number;
|
|
||||||
mirostat?: number;
|
|
||||||
mirostat_lr?: number;
|
|
||||||
mirostat_ent?: number;
|
|
||||||
logit_bias?: string[];
|
|
||||||
grammar?: string;
|
|
||||||
grammar_file?: string;
|
|
||||||
json_schema?: string;
|
|
||||||
json_schema_file?: string;
|
|
||||||
|
|
||||||
// Server/Example-specific params
|
|
||||||
no_context_shift?: boolean;
|
|
||||||
special?: boolean;
|
|
||||||
no_warmup?: boolean;
|
|
||||||
spm_infill?: boolean;
|
|
||||||
pooling?: string;
|
|
||||||
cont_batching?: boolean;
|
|
||||||
no_cont_batching?: boolean;
|
|
||||||
mmproj?: string;
|
|
||||||
mmproj_url?: string;
|
|
||||||
no_mmproj?: boolean;
|
|
||||||
no_mmproj_offload?: boolean;
|
|
||||||
alias?: string;
|
|
||||||
host?: string;
|
|
||||||
port?: number;
|
|
||||||
path?: string;
|
|
||||||
no_webui?: boolean;
|
|
||||||
embedding?: boolean;
|
|
||||||
reranking?: boolean;
|
|
||||||
api_key?: string;
|
|
||||||
api_key_file?: string;
|
|
||||||
ssl_key_file?: string;
|
|
||||||
ssl_cert_file?: string;
|
|
||||||
chat_template_kwargs?: string;
|
|
||||||
timeout?: number;
|
|
||||||
threads_http?: number;
|
|
||||||
cache_reuse?: number;
|
|
||||||
metrics?: boolean;
|
|
||||||
slots?: boolean;
|
|
||||||
props?: boolean;
|
|
||||||
no_slots?: boolean;
|
|
||||||
slot_save_path?: string;
|
|
||||||
jinja?: boolean;
|
|
||||||
reasoning_format?: string;
|
|
||||||
reasoning_budget?: number;
|
|
||||||
chat_template?: string;
|
|
||||||
chat_template_file?: string;
|
|
||||||
no_prefill_assistant?: boolean;
|
|
||||||
slot_prompt_similarity?: number;
|
|
||||||
lora_init_without_apply?: boolean;
|
|
||||||
|
|
||||||
// Speculative decoding params
|
|
||||||
draft_max?: number;
|
|
||||||
draft_min?: number;
|
|
||||||
draft_p_min?: number;
|
|
||||||
ctx_size_draft?: number;
|
|
||||||
device_draft?: string;
|
|
||||||
gpu_layers_draft?: number;
|
|
||||||
model_draft?: string;
|
|
||||||
cache_type_k_draft?: string;
|
|
||||||
cache_type_v_draft?: string;
|
|
||||||
|
|
||||||
// Audio/TTS params
|
|
||||||
model_vocoder?: string;
|
|
||||||
tts_use_guide_tokens?: boolean;
|
|
||||||
|
|
||||||
// Default model params
|
|
||||||
embd_bge_small_en_default?: boolean;
|
|
||||||
embd_e5_small_en_default?: boolean;
|
|
||||||
embd_gte_small_default?: boolean;
|
|
||||||
fim_qwen_1_5b_default?: boolean;
|
|
||||||
fim_qwen_3b_default?: boolean;
|
|
||||||
fim_qwen_7b_default?: boolean;
|
|
||||||
fim_qwen_7b_spec?: boolean;
|
|
||||||
fim_qwen_14b_spec?: boolean;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user