Frank 4 ماه پیش
والد
کامیت
f14cd4a3db

+ 66 - 0
packages/console/app/src/component/modal.css

@@ -0,0 +1,66 @@
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+  }
+
+  to {
+    opacity: 1;
+  }
+}
+
+@keyframes slideUp {
+  from {
+    opacity: 0;
+    transform: translateY(20px);
+  }
+
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+[data-component="modal"][data-slot="overlay"] {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 9999;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: rgba(0, 0, 0, 0.5);
+  animation: fadeIn 0.2s ease;
+
+  @media (prefers-color-scheme: dark) {
+    background-color: rgba(0, 0, 0, 0.7);
+  }
+
+  [data-slot="content"] {
+    background-color: var(--color-bg);
+    border: 1px solid var(--color-border);
+    border-radius: var(--border-radius-md);
+    padding: var(--space-6);
+    min-width: 400px;
+    max-width: 90vw;
+    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
+    animation: slideUp 0.2s ease;
+
+    @media (max-width: 30rem) {
+      min-width: 300px;
+      padding: var(--space-4);
+    }
+
+    @media (prefers-color-scheme: dark) {
+      box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
+    }
+  }
+
+  [data-slot="title"] {
+    margin: 0 0 var(--space-4) 0;
+    font-size: var(--font-size-lg);
+    font-weight: 600;
+    color: var(--color-text);
+  }
+}

+ 24 - 0
packages/console/app/src/component/modal.tsx

@@ -0,0 +1,24 @@
+import { JSX, Show } from "solid-js"
+import "./modal.css"
+
+interface ModalProps {
+  open: boolean
+  onClose: () => void
+  title?: string
+  children: JSX.Element
+}
+
+export function Modal(props: ModalProps) {
+  return (
+    <Show when={props.open}>
+      <div data-component="modal" data-slot="overlay" onClick={props.onClose}>
+        <div data-slot="content" onClick={(e) => e.stopPropagation()}>
+          <Show when={props.title}>
+            <h2 data-slot="title">{props.title}</h2>
+          </Show>
+          {props.children}
+        </div>
+      </div>
+    </Show>
+  )
+}

+ 16 - 19
packages/console/app/src/routes/workspace-picker.css

@@ -1,15 +1,15 @@
 [data-component="workspace-picker"] {
   position: relative;
-  /* Override blue accent colors with neutral colors */
-  --color-accent: var(--color-border);
-  --color-accent-hover: var(--color-border);
-  --color-accent-active: var(--color-border);
-  --color-primary: var(--color-border);
-  --color-primary-hover: var(--color-border);
-  --color-primary-active: var(--color-border);
-  --color-primary-alpha-20: transparent;
 
   [data-slot="trigger"] {
+    /* Override blue accent colors with neutral colors for dropdown trigger */
+    --color-accent: var(--color-border);
+    --color-accent-hover: var(--color-border);
+    --color-accent-active: var(--color-border);
+    --color-primary: var(--color-border);
+    --color-primary-hover: var(--color-border);
+    --color-primary-active: var(--color-border);
+    --color-primary-alpha-20: transparent;
     display: flex;
     align-items: center;
     justify-content: space-between;
@@ -73,22 +73,19 @@
   }
 
   [data-slot="create-form"] {
-    margin-top: var(--space-4);
-    padding: var(--space-4);
-    border: 1px solid var(--color-border);
-    border-radius: var(--border-radius-sm);
-    background-color: var(--color-surface);
+    width: 100%;
   }
 
   [data-slot="create-input-group"] {
     display: flex;
-    gap: var(--space-2);
-    align-items: center;
+    flex-direction: column;
+    gap: var(--space-3);
+  }
 
-    @media (max-width: 30rem) {
-      flex-direction: column;
-      align-items: stretch;
-    }
+  [data-slot="button-group"] {
+    display: flex;
+    gap: var(--space-2);
+    justify-content: flex-end;
   }
 
   [data-slot="create-input"] {

+ 19 - 9
packages/console/app/src/routes/workspace-picker.tsx

@@ -8,6 +8,7 @@ import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.j
 import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
 import { Workspace } from "@opencode-ai/console-core/workspace.js"
 import { IconChevron } from "~/component/icon"
+import { Modal } from "~/component/modal"
 import "./workspace-picker.css"
 
 const getWorkspaces = query(async () => {
@@ -46,6 +47,7 @@ export function WorkspacePicker() {
     showDropdown: false,
   })
   let dropdownRef: HTMLDivElement | undefined
+  let inputRef: HTMLInputElement | undefined
 
   const currentWorkspace = () => {
     const ws = workspaces()?.find((w) => w.id === params.id)
@@ -56,6 +58,12 @@ export function WorkspacePicker() {
     setStore({ showForm: true, showDropdown: false })
   }
 
+  createEffect(() => {
+    if (store.showForm && inputRef) {
+      setTimeout(() => inputRef?.focus(), 0)
+    }
+  })
+
   const handleSelectWorkspace = (workspaceID: string) => {
     if (workspaceID === params.id) {
       setStore("showDropdown", false)
@@ -112,26 +120,28 @@ export function WorkspacePicker() {
         </Show>
       </div>
 
-      <Show when={store.showForm}>
+      <Modal open={store.showForm} onClose={() => setStore("showForm", false)} title="Create New Workspace">
         <form data-slot="create-form" action={createWorkspace} method="post">
           <div data-slot="create-input-group">
             <input
+              ref={inputRef}
               data-slot="create-input"
               type="text"
               name="workspaceName"
               placeholder="Enter workspace name"
               required
-              autofocus
             />
-            <button type="submit" data-color="primary">
-              Create
-            </button>
-            <button type="button" onClick={() => setStore("showForm", false)}>
-              Cancel
-            </button>
+            <div data-slot="button-group">
+              <button type="button" data-color="ghost" onClick={() => setStore("showForm", false)}>
+                Cancel
+              </button>
+              <button type="submit" data-color="primary">
+                Create
+              </button>
+            </div>
           </div>
         </form>
-      </Show>
+      </Modal>
     </div>
   )
 }