|
1 | 1 | /** |
2 | | - * WelcomeTab - Welcome screen shown when no files are open |
| 2 | + * WelcomeTab - Welcome/Start screen shown when no files are open |
3 | 3 | * |
4 | | - * Displays Cortex IDE branding and keyboard shortcut hints |
5 | | - * to help users get started. Styled with CortexTokens. |
| 4 | + * Pixel-perfect implementation matching Figma screens: |
| 5 | + * - 1027:23374 (IDE start screen with sidebar expanded) |
| 6 | + * - 645:12763 (start screen with sidebar collapsed) |
| 7 | + * - 1125:18513 (start screen no sidebar) |
| 8 | + * - 1239:21482 (compact option) |
| 9 | + * |
| 10 | + * Layout: centered container with title text + AI prompt input |
| 11 | + * Typography: Figtree 32px/40px weight 500 for title |
| 12 | + * Input: bg #1C1C1D, border 1px #2E2F31, border-radius 16px |
6 | 13 | */ |
7 | 14 |
|
8 | | -import { type JSX } from "solid-js"; |
9 | | -import { Icon } from "../ui/Icon"; |
10 | | -import { CortexTokens } from "@/design-system/tokens/cortex-tokens"; |
| 15 | +import { type JSX, createSignal } from "solid-js"; |
11 | 16 |
|
12 | 17 | export interface WelcomeTabProps { |
13 | 18 | class?: string; |
14 | 19 | style?: JSX.CSSProperties; |
| 20 | + compact?: boolean; |
15 | 21 | } |
16 | 22 |
|
17 | 23 | export function WelcomeTab(props: WelcomeTabProps) { |
18 | | - const containerStyle = (): JSX.CSSProperties => ({ |
| 24 | + const [inputValue, setInputValue] = createSignal(""); |
| 25 | + const [inputFocused, setInputFocused] = createSignal(false); |
| 26 | + |
| 27 | + const handleKeyDown = (e: KeyboardEvent) => { |
| 28 | + if (e.key === "Enter" && !e.shiftKey) { |
| 29 | + e.preventDefault(); |
| 30 | + const value = inputValue().trim(); |
| 31 | + if (value) { |
| 32 | + window.dispatchEvent( |
| 33 | + new CustomEvent("chat:submit", { detail: { message: value } }) |
| 34 | + ); |
| 35 | + setInputValue(""); |
| 36 | + } |
| 37 | + } |
| 38 | + }; |
| 39 | + |
| 40 | + const handleSendClick = () => { |
| 41 | + const value = inputValue().trim(); |
| 42 | + if (value) { |
| 43 | + window.dispatchEvent( |
| 44 | + new CustomEvent("chat:submit", { detail: { message: value } }) |
| 45 | + ); |
| 46 | + setInputValue(""); |
| 47 | + } |
| 48 | + }; |
| 49 | + |
| 50 | + const workspaceStyle = (): JSX.CSSProperties => ({ |
19 | 51 | display: "flex", |
20 | 52 | flex: "1", |
21 | 53 | "flex-direction": "column", |
22 | 54 | "align-items": "center", |
23 | 55 | "justify-content": "center", |
24 | | - gap: "32px", |
25 | | - background: CortexTokens.colors.bg.primary, |
| 56 | + background: "var(--cortex-bg-primary)", |
26 | 57 | "min-height": "0", |
27 | 58 | ...props.style, |
28 | 59 | }); |
29 | 60 |
|
30 | | - const brandStyle: JSX.CSSProperties = { |
| 61 | + const containerStyle = (): JSX.CSSProperties => ({ |
31 | 62 | display: "flex", |
32 | 63 | "flex-direction": "column", |
33 | 64 | "align-items": "center", |
34 | | - gap: "12px", |
| 65 | + gap: "28px", |
| 66 | + "max-width": props.compact ? "none" : "922px", |
| 67 | + width: props.compact ? "auto" : "100%", |
| 68 | + }); |
| 69 | + |
| 70 | + const titleStyle: JSX.CSSProperties = { |
| 71 | + margin: "0", |
| 72 | + "font-family": "var(--cortex-font-sans)", |
| 73 | + "font-size": "32px", |
| 74 | + "font-weight": "500", |
| 75 | + "line-height": "40px", |
| 76 | + "letter-spacing": "0px", |
| 77 | + "text-align": "center", |
| 78 | + color: "#FCFCFC", |
| 79 | + width: "100%", |
35 | 80 | }; |
36 | 81 |
|
37 | | - const logoStyle: JSX.CSSProperties = { |
38 | | - width: "48px", |
| 82 | + const inputContainerStyle = (): JSX.CSSProperties => ({ |
| 83 | + display: "flex", |
| 84 | + "flex-direction": "column", |
| 85 | + width: props.compact ? "100%" : "686px", |
| 86 | + background: "#1C1C1D", |
| 87 | + border: inputFocused() |
| 88 | + ? "1px solid var(--cortex-accent-primary)" |
| 89 | + : "1px solid #2E2F31", |
| 90 | + "border-radius": "16px", |
| 91 | + overflow: "hidden", |
| 92 | + transition: "border-color 150ms ease", |
| 93 | + }); |
| 94 | + |
| 95 | + const typeAreaStyle: JSX.CSSProperties = { |
| 96 | + display: "flex", |
| 97 | + "align-items": "center", |
| 98 | + gap: "8px", |
| 99 | + padding: "16px", |
39 | 100 | height: "48px", |
40 | | - color: CortexTokens.colors.accent.primary, |
41 | | - opacity: "0.6", |
| 101 | + "box-sizing": "border-box", |
42 | 102 | }; |
43 | 103 |
|
44 | | - const titleStyle: JSX.CSSProperties = { |
| 104 | + const inputStyle: JSX.CSSProperties = { |
| 105 | + flex: "1", |
| 106 | + background: "transparent", |
| 107 | + border: "none", |
| 108 | + outline: "none", |
| 109 | + color: "#FCFCFC", |
| 110 | + "font-family": "var(--cortex-font-sans)", |
| 111 | + "font-size": "14px", |
| 112 | + "font-weight": "400", |
| 113 | + "line-height": "16px", |
| 114 | + padding: "0", |
45 | 115 | margin: "0", |
46 | | - "font-size": "20px", |
47 | | - "font-weight": "600", |
48 | | - color: CortexTokens.colors.text.primary, |
49 | | - "font-family": "var(--cortex-font-sans, Inter, system-ui, sans-serif)", |
50 | | - "letter-spacing": "-0.02em", |
51 | 116 | }; |
52 | 117 |
|
53 | | - const subtitleStyle: JSX.CSSProperties = { |
54 | | - margin: "0", |
55 | | - "font-size": "14px", |
56 | | - color: CortexTokens.colors.text.muted, |
57 | | - "font-family": "var(--cortex-font-sans, Inter, system-ui, sans-serif)", |
| 118 | + const actionAreaStyle: JSX.CSSProperties = { |
| 119 | + display: "flex", |
| 120 | + "align-items": "center", |
| 121 | + "justify-content": "space-between", |
| 122 | + padding: "0 16px 16px 16px", |
| 123 | + height: "44px", |
| 124 | + "box-sizing": "border-box", |
58 | 125 | }; |
59 | 126 |
|
60 | | - const shortcutsStyle: JSX.CSSProperties = { |
| 127 | + const attachButtonStyle: JSX.CSSProperties = { |
61 | 128 | display: "flex", |
62 | | - "flex-direction": "column", |
63 | | - gap: "8px", |
| 129 | + "align-items": "center", |
| 130 | + "justify-content": "center", |
| 131 | + width: "28px", |
| 132 | + height: "28px", |
| 133 | + background: "transparent", |
| 134 | + border: "none", |
| 135 | + "border-radius": "8px", |
| 136 | + cursor: "pointer", |
| 137 | + padding: "0", |
| 138 | + color: "#8C8D8F", |
64 | 139 | }; |
65 | 140 |
|
66 | | - const shortcutRowStyle: JSX.CSSProperties = { |
| 141 | + const actionsRightStyle: JSX.CSSProperties = { |
67 | 142 | display: "flex", |
68 | 143 | "align-items": "center", |
69 | 144 | gap: "12px", |
70 | | - "font-size": "13px", |
71 | | - color: CortexTokens.colors.text.secondary, |
72 | | - "font-family": "var(--cortex-font-sans, Inter, system-ui, sans-serif)", |
73 | 145 | }; |
74 | 146 |
|
75 | | - const kbdStyle: JSX.CSSProperties = { |
| 147 | + const pillButtonStyle: JSX.CSSProperties = { |
76 | 148 | display: "inline-flex", |
77 | 149 | "align-items": "center", |
78 | | - "justify-content": "center", |
79 | | - "min-width": "24px", |
80 | | - padding: "2px 8px", |
81 | | - "font-size": "12px", |
82 | | - "font-family": "var(--cortex-font-mono, 'JetBrains Mono', monospace)", |
83 | | - background: CortexTokens.colors.bg.elevated, |
84 | | - border: `1px solid ${CortexTokens.colors.border.default}`, |
85 | | - "border-radius": "var(--cortex-radius-xs, 4px)", |
86 | | - color: CortexTokens.colors.text.primary, |
| 150 | + gap: "4px", |
| 151 | + padding: "6px", |
| 152 | + background: "transparent", |
| 153 | + border: "none", |
| 154 | + "border-radius": "8px", |
| 155 | + cursor: "pointer", |
| 156 | + "font-family": "var(--cortex-font-sans)", |
| 157 | + "font-size": "14px", |
| 158 | + "font-weight": "500", |
| 159 | + "line-height": "16px", |
| 160 | + color: "#FCFCFC", |
87 | 161 | "white-space": "nowrap", |
88 | 162 | }; |
89 | 163 |
|
90 | | - const shortcuts = [ |
91 | | - { keys: "Ctrl+P", label: "Quick Open File" }, |
92 | | - { keys: "Ctrl+N", label: "New File" }, |
93 | | - { keys: "Ctrl+O", label: "Open File" }, |
94 | | - { keys: "Ctrl+Shift+P", label: "Command Palette" }, |
95 | | - ]; |
| 164 | + const sendButtonStyle = (): JSX.CSSProperties => ({ |
| 165 | + display: "flex", |
| 166 | + "align-items": "center", |
| 167 | + "justify-content": "center", |
| 168 | + width: "28px", |
| 169 | + height: "28px", |
| 170 | + background: inputValue().trim() ? "#4C4C4D" : "#2E2F31", |
| 171 | + border: "none", |
| 172 | + "border-radius": "999px", |
| 173 | + cursor: inputValue().trim() ? "pointer" : "default", |
| 174 | + padding: "0", |
| 175 | + transition: "background 100ms ease", |
| 176 | + }); |
96 | 177 |
|
97 | 178 | return ( |
98 | | - <div class={props.class} style={containerStyle()}> |
99 | | - <div style={brandStyle}> |
100 | | - <Icon name="brain" style={logoStyle} /> |
101 | | - <h2 style={titleStyle}>Cortex IDE</h2> |
102 | | - <p style={subtitleStyle}>AI-Powered Development Environment</p> |
103 | | - </div> |
| 179 | + <div class={props.class} style={workspaceStyle()}> |
| 180 | + <div style={containerStyle()}> |
| 181 | + <h2 style={titleStyle}> |
| 182 | + Hey, start building or open your project. |
| 183 | + </h2> |
| 184 | + |
| 185 | + <div style={inputContainerStyle()}> |
| 186 | + {/* Type area */} |
| 187 | + <div style={typeAreaStyle}> |
| 188 | + <input |
| 189 | + type="text" |
| 190 | + value={inputValue()} |
| 191 | + placeholder="Ask Cortex anything..." |
| 192 | + style={inputStyle} |
| 193 | + onInput={(e) => setInputValue(e.currentTarget.value)} |
| 194 | + onKeyDown={handleKeyDown} |
| 195 | + onFocus={() => setInputFocused(true)} |
| 196 | + onBlur={() => setInputFocused(false)} |
| 197 | + /> |
| 198 | + </div> |
| 199 | + |
| 200 | + {/* Action area */} |
| 201 | + <div style={actionAreaStyle}> |
| 202 | + <button style={attachButtonStyle} aria-label="Attach file"> |
| 203 | + <svg |
| 204 | + width="16" |
| 205 | + height="16" |
| 206 | + viewBox="0 0 16 16" |
| 207 | + fill="none" |
| 208 | + xmlns="http://www.w3.org/2000/svg" |
| 209 | + > |
| 210 | + <path |
| 211 | + d="M14.1667 7.36666L8.18 13.3533C7.31222 14.2211 6.13777 14.7088 4.91333 14.7088C3.6889 14.7088 2.51445 14.2211 1.64667 13.3533C0.778889 12.4856 0.291199 11.3111 0.291199 10.0867C0.291199 8.86222 0.778889 7.68777 1.64667 6.82L7.63333 0.833328C8.21222 0.254438 9.00222 -0.0722656 9.82667 -0.0722656C10.6511 -0.0722656 11.4411 0.254438 12.02 0.833328C12.5989 1.41222 12.9256 2.20222 12.9256 3.02667C12.9256 3.85111 12.5989 4.64111 12.02 5.22L5.98 11.2067C5.69056 11.4961 5.29556 11.6594 4.88333 11.6594C4.47111 11.6594 4.07611 11.4961 3.78667 11.2067C3.49722 10.9172 3.33389 10.5222 3.33389 10.11C3.33389 9.69778 3.49722 9.30278 3.78667 9.01333L9.22 3.58" |
| 212 | + stroke="currentColor" |
| 213 | + stroke-width="1.2" |
| 214 | + stroke-linecap="round" |
| 215 | + stroke-linejoin="round" |
| 216 | + /> |
| 217 | + </svg> |
| 218 | + </button> |
| 219 | + |
| 220 | + <div style={actionsRightStyle}> |
| 221 | + <div style={{ display: "flex", "align-items": "center", gap: "4px" }}> |
| 222 | + <button style={pillButtonStyle}> |
| 223 | + <svg |
| 224 | + width="16" |
| 225 | + height="16" |
| 226 | + viewBox="0 0 16 16" |
| 227 | + fill="none" |
| 228 | + xmlns="http://www.w3.org/2000/svg" |
| 229 | + > |
| 230 | + <path |
| 231 | + d="M8 1L10 5L14.5 5.5L11.25 8.5L12 13L8 10.5L4 13L4.75 8.5L1.5 5.5L6 5L8 1Z" |
| 232 | + fill="#FF4081" |
| 233 | + /> |
| 234 | + </svg> |
| 235 | + <span>Build</span> |
| 236 | + <svg |
| 237 | + width="16" |
| 238 | + height="16" |
| 239 | + viewBox="0 0 16 16" |
| 240 | + fill="none" |
| 241 | + xmlns="http://www.w3.org/2000/svg" |
| 242 | + > |
| 243 | + <path |
| 244 | + d="M4 6L8 10L12 6" |
| 245 | + stroke="#8C8D8F" |
| 246 | + stroke-width="1.5" |
| 247 | + stroke-linecap="round" |
| 248 | + stroke-linejoin="round" |
| 249 | + /> |
| 250 | + </svg> |
| 251 | + </button> |
| 252 | + |
| 253 | + <button style={pillButtonStyle}> |
| 254 | + <svg |
| 255 | + width="16" |
| 256 | + height="16" |
| 257 | + viewBox="0 0 16 16" |
| 258 | + fill="none" |
| 259 | + xmlns="http://www.w3.org/2000/svg" |
| 260 | + > |
| 261 | + <circle cx="8" cy="8" r="6" fill="#D97757" /> |
| 262 | + </svg> |
| 263 | + <span>Claude-Opus-4.5</span> |
| 264 | + <svg |
| 265 | + width="16" |
| 266 | + height="16" |
| 267 | + viewBox="0 0 16 16" |
| 268 | + fill="none" |
| 269 | + xmlns="http://www.w3.org/2000/svg" |
| 270 | + > |
| 271 | + <path |
| 272 | + d="M4 6L8 10L12 6" |
| 273 | + stroke="#8C8D8F" |
| 274 | + stroke-width="1.5" |
| 275 | + stroke-linecap="round" |
| 276 | + stroke-linejoin="round" |
| 277 | + /> |
| 278 | + </svg> |
| 279 | + </button> |
| 280 | + </div> |
104 | 281 |
|
105 | | - <div style={shortcutsStyle}> |
106 | | - {shortcuts.map((shortcut) => ( |
107 | | - <div style={shortcutRowStyle}> |
108 | | - <kbd style={kbdStyle}>{shortcut.keys}</kbd> |
109 | | - <span>{shortcut.label}</span> |
| 282 | + <button |
| 283 | + style={sendButtonStyle()} |
| 284 | + onClick={handleSendClick} |
| 285 | + aria-label="Send message" |
| 286 | + > |
| 287 | + <svg |
| 288 | + width="16" |
| 289 | + height="16" |
| 290 | + viewBox="0 0 16 16" |
| 291 | + fill="none" |
| 292 | + xmlns="http://www.w3.org/2000/svg" |
| 293 | + > |
| 294 | + <path |
| 295 | + d="M14.5 1.5L7.5 8.5M14.5 1.5L10 14.5L7.5 8.5M14.5 1.5L1.5 6L7.5 8.5" |
| 296 | + stroke={inputValue().trim() ? "#0D0D0E" : "#8C8D8F"} |
| 297 | + stroke-width="1.5" |
| 298 | + stroke-linecap="round" |
| 299 | + stroke-linejoin="round" |
| 300 | + fill="none" |
| 301 | + /> |
| 302 | + </svg> |
| 303 | + </button> |
| 304 | + </div> |
110 | 305 | </div> |
111 | | - ))} |
| 306 | + </div> |
112 | 307 | </div> |
113 | 308 | </div> |
114 | 309 | ); |
|
0 commit comments