Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>CodeSignal Interview Prompt Generator</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --bg: #0a0c10; | |
| --surface: #111318; | |
| --surface2: #191d24; | |
| --border: #252b36; | |
| --accent: #f97316; | |
| --accent2: #fb923c; | |
| --green: #22c55e; | |
| --blue: #38bdf8; | |
| --purple: #a78bfa; | |
| --red: #f87171; | |
| --text: #e2e8f0; | |
| --muted: #64748b; | |
| --heading: #f8fafc; | |
| } | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { | |
| background: var(--bg); | |
| color: var(--text); | |
| font-family: 'Syne', sans-serif; | |
| min-height: 100vh; | |
| overflow-x: hidden; | |
| } | |
| /* GRID BACKGROUND */ | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| inset: 0; | |
| background-image: | |
| linear-gradient(rgba(249,115,22,0.03) 1px, transparent 1px), | |
| linear-gradient(90deg, rgba(249,115,22,0.03) 1px, transparent 1px); | |
| background-size: 48px 48px; | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| /* NOISE OVERLAY */ | |
| body::after { | |
| content: ''; | |
| position: fixed; | |
| inset: 0; | |
| background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E"); | |
| pointer-events: none; | |
| z-index: 0; | |
| opacity: 0.4; | |
| } | |
| .container { | |
| position: relative; | |
| z-index: 1; | |
| max-width: 1100px; | |
| margin: 0 auto; | |
| padding: 0 24px 80px; | |
| } | |
| /* HEADER */ | |
| header { | |
| padding: 48px 0 40px; | |
| border-bottom: 1px solid var(--border); | |
| margin-bottom: 48px; | |
| display: flex; | |
| align-items: flex-end; | |
| justify-content: space-between; | |
| gap: 24px; | |
| } | |
| .brand { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| } | |
| .brand-tag { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 11px; | |
| color: var(--accent); | |
| letter-spacing: 0.2em; | |
| text-transform: uppercase; | |
| } | |
| h1 { | |
| font-size: clamp(28px, 5vw, 44px); | |
| font-weight: 800; | |
| color: var(--heading); | |
| line-height: 1.1; | |
| letter-spacing: -0.02em; | |
| } | |
| h1 span { | |
| color: var(--accent); | |
| } | |
| .header-meta { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 12px; | |
| color: var(--muted); | |
| text-align: right; | |
| line-height: 1.8; | |
| } | |
| /* MAIN LAYOUT */ | |
| .layout { | |
| display: grid; | |
| grid-template-columns: 340px 1fr; | |
| gap: 32px; | |
| align-items: start; | |
| /* Prevent children from blowing out the grid */ | |
| width: 100%; | |
| } | |
| /* Critical: grid children must not exceed their track */ | |
| .layout > * { | |
| min-width: 0; | |
| width: 100%; | |
| } | |
| @media (max-width: 768px) { | |
| .layout { grid-template-columns: 1fr; } | |
| header { flex-direction: column; align-items: flex-start; } | |
| .header-meta { text-align: left; } | |
| } | |
| /* SIDEBAR / CONFIG PANEL */ | |
| .panel { | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 28px; | |
| position: sticky; | |
| top: 24px; | |
| } | |
| .panel-title { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 11px; | |
| color: var(--accent); | |
| letter-spacing: 0.18em; | |
| text-transform: uppercase; | |
| margin-bottom: 24px; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .panel-title::before { | |
| content: '//'; | |
| opacity: 0.5; | |
| } | |
| .field { | |
| margin-bottom: 20px; | |
| } | |
| label { | |
| display: block; | |
| font-size: 12px; | |
| font-weight: 600; | |
| color: var(--muted); | |
| letter-spacing: 0.08em; | |
| text-transform: uppercase; | |
| margin-bottom: 8px; | |
| } | |
| input, select, textarea { | |
| width: 100%; | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| color: var(--text); | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 13px; | |
| padding: 11px 14px; | |
| outline: none; | |
| transition: border-color 0.2s, box-shadow 0.2s; | |
| appearance: none; | |
| } | |
| input::placeholder, textarea::placeholder { color: var(--muted); } | |
| input:focus, select:focus, textarea:focus { | |
| border-color: var(--accent); | |
| box-shadow: 0 0 0 3px rgba(249,115,22,0.12); | |
| } | |
| select { | |
| cursor: pointer; | |
| background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); | |
| background-repeat: no-repeat; | |
| background-position: right 12px center; | |
| padding-right: 36px; | |
| } | |
| textarea { resize: vertical; min-height: 80px; line-height: 1.5; } | |
| .api-key-wrap { | |
| position: relative; | |
| } | |
| .api-key-wrap input { padding-right: 44px; } | |
| .toggle-vis { | |
| position: absolute; | |
| right: 12px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| background: none; | |
| border: none; | |
| cursor: pointer; | |
| color: var(--muted); | |
| padding: 4px; | |
| display: flex; | |
| align-items: center; | |
| transition: color 0.2s; | |
| } | |
| .toggle-vis:hover { color: var(--text); } | |
| /* GENERATE BUTTON */ | |
| .btn-generate { | |
| width: 100%; | |
| background: var(--accent); | |
| color: #fff; | |
| border: none; | |
| border-radius: 8px; | |
| font-family: 'Syne', sans-serif; | |
| font-weight: 700; | |
| font-size: 14px; | |
| letter-spacing: 0.05em; | |
| text-transform: uppercase; | |
| padding: 14px; | |
| cursor: pointer; | |
| position: relative; | |
| overflow: hidden; | |
| transition: background 0.2s, transform 0.1s; | |
| margin-top: 8px; | |
| } | |
| .btn-generate:hover { background: var(--accent2); } | |
| .btn-generate:active { transform: scale(0.98); } | |
| .btn-generate:disabled { opacity: 0.5; cursor: not-allowed; transform: none; } | |
| .btn-generate .spinner { | |
| display: none; | |
| width: 16px; | |
| height: 16px; | |
| border: 2px solid rgba(255,255,255,0.3); | |
| border-top-color: white; | |
| border-radius: 50%; | |
| animation: spin 0.7s linear infinite; | |
| margin-right: 8px; | |
| } | |
| .btn-generate.loading .spinner { display: inline-block; } | |
| .btn-generate.loading .btn-text::before { content: 'Generating'; } | |
| .btn-generate:not(.loading) .btn-text::before { content: '⚡ Generate Prompt'; } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| .btn-generate-inner { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| /* DIVIDER */ | |
| .divider { | |
| border: none; | |
| border-top: 1px solid var(--border); | |
| margin: 20px 0; | |
| } | |
| .systems-presets { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 6px; | |
| margin-top: -8px; | |
| } | |
| .preset-chip { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 10px; | |
| padding: 4px 10px; | |
| border-radius: 20px; | |
| border: 1px solid var(--border); | |
| background: transparent; | |
| color: var(--muted); | |
| cursor: pointer; | |
| transition: all 0.15s; | |
| white-space: nowrap; | |
| } | |
| .preset-chip:hover { | |
| border-color: var(--accent); | |
| color: var(--accent); | |
| background: rgba(249,115,22,0.06); | |
| } | |
| /* OUTPUT AREA */ | |
| .output-area { | |
| min-height: 500px; | |
| min-width: 0; | |
| width: 100%; | |
| overflow: hidden; | |
| } | |
| .output-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-bottom: 24px; | |
| } | |
| .output-title { | |
| font-size: 18px; | |
| font-weight: 700; | |
| color: var(--heading); | |
| } | |
| .output-actions { | |
| display: flex; | |
| gap: 8px; | |
| } | |
| .btn-sm { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 11px; | |
| padding: 7px 14px; | |
| border-radius: 6px; | |
| border: 1px solid var(--border); | |
| background: var(--surface); | |
| color: var(--muted); | |
| cursor: pointer; | |
| transition: all 0.15s; | |
| letter-spacing: 0.05em; | |
| } | |
| .btn-sm:hover { border-color: var(--accent); color: var(--accent); } | |
| /* PLACEHOLDER */ | |
| .placeholder { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| min-height: 420px; | |
| border: 1px dashed var(--border); | |
| border-radius: 12px; | |
| gap: 16px; | |
| } | |
| .placeholder-icon { | |
| font-size: 48px; | |
| opacity: 0.3; | |
| } | |
| .placeholder p { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 13px; | |
| color: var(--muted); | |
| text-align: center; | |
| line-height: 1.6; | |
| } | |
| /* LEVEL TABS */ | |
| .level-tabs { | |
| display: flex; | |
| gap: 0; | |
| margin-bottom: 24px; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .level-tab { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 12px; | |
| font-weight: 700; | |
| padding: 10px 20px; | |
| border: none; | |
| background: transparent; | |
| color: var(--muted); | |
| cursor: pointer; | |
| border-bottom: 2px solid transparent; | |
| margin-bottom: -1px; | |
| transition: all 0.15s; | |
| letter-spacing: 0.05em; | |
| } | |
| .level-tab:hover { color: var(--text); } | |
| .level-tab.active { | |
| color: var(--accent); | |
| border-bottom-color: var(--accent); | |
| } | |
| .level-tab .level-badge { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 18px; | |
| height: 18px; | |
| border-radius: 4px; | |
| font-size: 10px; | |
| margin-right: 6px; | |
| } | |
| .level-tab[data-level="1"] .level-badge { background: rgba(34,197,94,0.15); color: var(--green); } | |
| .level-tab[data-level="2"] .level-badge { background: rgba(56,189,248,0.15); color: var(--blue); } | |
| .level-tab[data-level="3"] .level-badge { background: rgba(167,139,250,0.15); color: var(--purple); } | |
| .level-tab[data-level="4"] .level-badge { background: rgba(249,115,22,0.15); color: var(--accent); } | |
| /* LEVEL CONTENT PANELS */ | |
| .level-panel { display: none; } | |
| .level-panel.active { display: block; animation: fadeUp 0.3s ease; } | |
| @keyframes fadeUp { | |
| from { opacity: 0; transform: translateY(8px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .level-card { | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| overflow: hidden; | |
| min-width: 0; | |
| width: 100%; | |
| } | |
| .level-card-header { | |
| padding: 20px 24px; | |
| border-bottom: 1px solid var(--border); | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| min-width: 0; | |
| } | |
| .level-dot { | |
| width: 10px; | |
| height: 10px; | |
| border-radius: 50%; | |
| } | |
| [data-level="1"] .level-dot { background: var(--green); box-shadow: 0 0 8px var(--green); } | |
| [data-level="2"] .level-dot { background: var(--blue); box-shadow: 0 0 8px var(--blue); } | |
| [data-level="3"] .level-dot { background: var(--purple); box-shadow: 0 0 8px var(--purple); } | |
| [data-level="4"] .level-dot { background: var(--accent); box-shadow: 0 0 8px var(--accent); } | |
| .level-card-title { | |
| font-size: 15px; | |
| font-weight: 700; | |
| color: var(--heading); | |
| min-width: 0; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| flex: 1; | |
| } | |
| .level-card-subtitle { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 11px; | |
| color: var(--muted); | |
| margin-left: auto; | |
| white-space: nowrap; | |
| flex-shrink: 0; | |
| } | |
| .level-card-body { | |
| padding: 24px; | |
| min-width: 0; | |
| overflow: hidden; | |
| } | |
| /* CONTENT SECTIONS */ | |
| .section { | |
| margin-bottom: 24px; | |
| } | |
| .section:last-child { margin-bottom: 0; } | |
| .section-label { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 10px; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| color: var(--muted); | |
| margin-bottom: 10px; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .section-label::after { | |
| content: ''; | |
| flex: 1; | |
| height: 1px; | |
| background: var(--border); | |
| } | |
| .section p, .section li { | |
| font-size: 14px; | |
| line-height: 1.75; | |
| color: var(--text); | |
| overflow-wrap: break-word; | |
| word-break: break-word; | |
| } | |
| .section ul { | |
| list-style: none; | |
| padding: 0; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| } | |
| .section ul li::before { | |
| content: '→'; | |
| color: var(--accent); | |
| margin-right: 10px; | |
| font-family: 'JetBrains Mono', monospace; | |
| } | |
| /* CODE BLOCKS */ | |
| pre, .code-block { | |
| background: #060810; | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| padding: 16px 18px; | |
| overflow-x: auto; | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 13px; | |
| line-height: 1.65; | |
| color: #c9d1d9; | |
| position: relative; | |
| max-width: 100%; | |
| width: 100%; | |
| box-sizing: border-box; | |
| word-break: normal; | |
| white-space: pre; | |
| } | |
| .code-block-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-bottom: 10px; | |
| } | |
| .code-lang { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 10px; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| color: var(--accent); | |
| background: rgba(249,115,22,0.1); | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| } | |
| .copy-btn { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 10px; | |
| padding: 3px 10px; | |
| border-radius: 4px; | |
| border: 1px solid var(--border); | |
| background: var(--surface2); | |
| color: var(--muted); | |
| cursor: pointer; | |
| transition: all 0.15s; | |
| } | |
| .copy-btn:hover { color: var(--green); border-color: var(--green); } | |
| .copy-btn.copied { color: var(--green); border-color: var(--green); } | |
| /* KEYWORD HIGHLIGHTING */ | |
| .kw-method { color: #38bdf8; } | |
| .kw-type { color: #a78bfa; } | |
| .kw-string { color: #86efac; } | |
| .kw-comment { color: #475569; font-style: italic; } | |
| .kw-num { color: #fb923c; } | |
| /* SYSTEM INFO BANNER */ | |
| .system-banner { | |
| background: linear-gradient(135deg, rgba(249,115,22,0.08), rgba(167,139,250,0.06)); | |
| border: 1px solid rgba(249,115,22,0.2); | |
| border-radius: 10px; | |
| padding: 16px 20px; | |
| margin-bottom: 24px; | |
| display: flex; | |
| align-items: center; | |
| gap: 14px; | |
| min-width: 0; | |
| width: 100%; | |
| box-sizing: border-box; | |
| overflow: hidden; | |
| } | |
| .system-banner-icon { | |
| font-size: 24px; | |
| } | |
| .system-banner-name { | |
| font-size: 18px; | |
| font-weight: 800; | |
| color: var(--heading); | |
| } | |
| .system-banner-desc { | |
| font-size: 13px; | |
| color: var(--muted); | |
| margin-top: 2px; | |
| } | |
| .difficulty-pills { | |
| margin-left: auto; | |
| display: flex; | |
| gap: 6px; | |
| } | |
| .pill { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 10px; | |
| padding: 3px 10px; | |
| border-radius: 20px; | |
| font-weight: 700; | |
| } | |
| .pill-easy { background: rgba(34,197,94,0.15); color: var(--green); } | |
| .pill-med { background: rgba(56,189,248,0.15); color: var(--blue); } | |
| .pill-hard { background: rgba(167,139,250,0.15); color: var(--purple); } | |
| .pill-expert { background: rgba(249,115,22,0.15); color: var(--accent); } | |
| /* ERROR */ | |
| .error-box { | |
| background: rgba(248,113,113,0.08); | |
| border: 1px solid rgba(248,113,113,0.25); | |
| border-radius: 8px; | |
| padding: 14px 18px; | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 12px; | |
| color: var(--red); | |
| margin-bottom: 16px; | |
| } | |
| /* STREAM CURSOR */ | |
| .cursor { | |
| display: inline-block; | |
| width: 2px; | |
| height: 1em; | |
| background: var(--accent); | |
| margin-left: 2px; | |
| animation: blink 1s step-end infinite; | |
| vertical-align: text-bottom; | |
| } | |
| @keyframes blink { 0%,100%{opacity:1} 50%{opacity:0} } | |
| #structuredOutput { | |
| min-width: 0; | |
| width: 100%; | |
| overflow: hidden; | |
| } | |
| .level-panel { | |
| min-width: 0; | |
| width: 100%; | |
| } | |
| .raw-view { | |
| display: none; | |
| background: #060810; | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 24px; | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 12px; | |
| line-height: 1.7; | |
| color: #c9d1d9; | |
| white-space: pre-wrap; | |
| word-break: break-word; | |
| overflow-wrap: break-word; | |
| max-height: 600px; | |
| overflow-y: auto; | |
| overflow-x: hidden; | |
| width: 100%; | |
| box-sizing: border-box; | |
| } | |
| .raw-view.visible { display: block; } | |
| /* FOOTER */ | |
| footer { | |
| margin-top: 60px; | |
| border-top: 1px solid var(--border); | |
| padding-top: 24px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 11px; | |
| color: var(--muted); | |
| } | |
| footer a { color: var(--accent); text-decoration: none; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <div class="brand"> | |
| <div class="brand-tag">// codesignal · oop systems design</div> | |
| <h1>CodeSignal<br><span>Prompt</span> Generator</h1> | |
| </div> | |
| <div class="header-meta"> | |
| Powered by GPT-4o<br> | |
| OOP System Design<br> | |
| 4-Level Interview Format | |
| </div> | |
| </header> | |
| <div class="layout"> | |
| <!-- SIDEBAR --> | |
| <aside> | |
| <div class="panel"> | |
| <div class="panel-title">Configuration</div> | |
| <div class="field"> | |
| <label>System Name</label> | |
| <input type="text" id="systemName" placeholder="e.g. LRU Cache" /> | |
| </div> | |
| <div class="field"> | |
| <label>Quick Presets</label> | |
| <div class="systems-presets"> | |
| <button class="preset-chip" onclick="setPreset('Time-Based Key-Value Store')">KV Store</button> | |
| <button class="preset-chip" onclick="setPreset('In-Memory Database')">In-Memory DB</button> | |
| <button class="preset-chip" onclick="setPreset('LRU / LFU Cache')">LRU/LFU Cache</button> | |
| <button class="preset-chip" onclick="setPreset('C-Like Memory Allocator')">Allocator</button> | |
| <button class="preset-chip" onclick="setPreset('Type Inference Engine')">Type Inference</button> | |
| <button class="preset-chip" onclick="setPreset('Circuit Breaker')">Circuit Breaker</button> | |
| <button class="preset-chip" onclick="setPreset('API Gateway with Rate Limiting')">API Gateway</button> | |
| <button class="preset-chip" onclick="setPreset('Thread Pool / Task Scheduler')">Thread Pool</button> | |
| <button class="preset-chip" onclick="setPreset('Transaction / Credit System')">Transaction</button> | |
| <button class="preset-chip" onclick="setPreset('Employee Management System')">Employee Mgmt</button> | |
| </div> | |
| </div> | |
| <div class="field"> | |
| <label>Brief Description <span style="color:var(--muted);font-weight:400">(optional)</span></label> | |
| <textarea id="systemDesc" placeholder="Any extra context or constraints for this system..."></textarea> | |
| </div> | |
| <hr class="divider"> | |
| <div class="field"> | |
| <label>OpenAI API Key</label> | |
| <div class="api-key-wrap"> | |
| <input type="password" id="apiKey" placeholder="sk-..." autocomplete="off" /> | |
| <button class="toggle-vis" onclick="toggleKey()" title="Show/hide key"> | |
| <svg id="eyeIcon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="field"> | |
| <label>Model</label> | |
| <select id="modelSelect"> | |
| <option value="gpt-4o">gpt-4o (recommended)</option> | |
| <option value="gpt-4o-mini">gpt-4o-mini (faster)</option> | |
| <option value="gpt-4-turbo">gpt-4-turbo</option> | |
| <option value="gpt-4">gpt-4</option> | |
| </select> | |
| </div> | |
| <button class="btn-generate" id="generateBtn" onclick="generate()"> | |
| <div class="btn-generate-inner"> | |
| <span class="spinner"></span> | |
| <span class="btn-text"></span> | |
| </div> | |
| </button> | |
| <div style="text-align:center;margin-top:12px"> | |
| <button class="btn-sm" style="width:100%;justify-content:center;display:flex" onclick="copyPromptSidebar(this)">⎘ Copy Prompt Only</button> | |
| </div> | |
| </div> | |
| </aside> | |
| <!-- OUTPUT --> | |
| <main class="output-area"> | |
| <div class="output-header"> | |
| <div class="output-title">Generated Prompt</div> | |
| <div class="output-actions" id="outputActions" style="display:none"> | |
| <button class="btn-sm" onclick="copyPrompt(this)">⎘ Copy Prompt</button> | |
| <button class="btn-sm" onclick="toggleRaw()">⊞ Raw</button> | |
| <button class="btn-sm" onclick="copyAll()">⎘ Copy Output</button> | |
| </div> | |
| </div> | |
| <div id="errorBox" class="error-box" style="display:none"></div> | |
| <div id="placeholder" class="placeholder"> | |
| <div class="placeholder-icon">⚙</div> | |
| <p>Enter a system name and your OpenAI key,<br>then click Generate to build your interview prompt.</p> | |
| </div> | |
| <!-- STRUCTURED OUTPUT --> | |
| <div id="structuredOutput" style="display:none"> | |
| <div class="system-banner" id="systemBanner"> | |
| <div class="system-banner-icon" id="bannerIcon">🏗️</div> | |
| <div> | |
| <div class="system-banner-name" id="bannerName"></div> | |
| <div class="system-banner-desc" id="bannerDesc"></div> | |
| </div> | |
| <div class="difficulty-pills"> | |
| <span class="pill pill-easy">L1 Easy</span> | |
| <span class="pill pill-med">L2 Medium</span> | |
| <span class="pill pill-hard">L3 Hard</span> | |
| <span class="pill pill-expert">L4 Expert</span> | |
| </div> | |
| </div> | |
| <div class="level-tabs"> | |
| <button class="level-tab active" data-level="1" onclick="switchLevel(1)"> | |
| <span class="level-badge">1</span>Foundation | |
| </button> | |
| <button class="level-tab" data-level="2" onclick="switchLevel(2)"> | |
| <span class="level-badge">2</span>Extended | |
| </button> | |
| <button class="level-tab" data-level="3" onclick="switchLevel(3)"> | |
| <span class="level-badge">3</span>Concurrent | |
| </button> | |
| <button class="level-tab" data-level="4" onclick="switchLevel(4)"> | |
| <span class="level-badge">4</span>Production | |
| </button> | |
| </div> | |
| <div id="lvl1" class="level-panel active" data-level="1"></div> | |
| <div id="lvl2" class="level-panel" data-level="2"></div> | |
| <div id="lvl3" class="level-panel" data-level="3"></div> | |
| <div id="lvl4" class="level-panel" data-level="4"></div> | |
| </div> | |
| <!-- RAW OUTPUT --> | |
| <div id="rawOutput" class="raw-view"></div> | |
| </main> | |
| </div> | |
| <footer> | |
| <span>CodeSignal-style Interview Prompt Generator · Prompt credits: <a href="https://www.yuan-meng.com/posts/mle_interviews_2.0/" target="_blank">Yuan Meng ↗</a></span> | |
| <span>4-Level OOP Systems Design · <a href="https://platform.openai.com/docs" target="_blank">OpenAI Docs ↗</a></span> | |
| </footer> | |
| </div> | |
| <script> | |
| const LEVEL_DESCRIPTIONS = ['Foundation', 'Extended APIs', 'Concurrency', 'Production-Ready']; | |
| const LEVEL_ICONS = ['🟢', '🔵', '🟣', '🟠']; | |
| const SYSTEM_ICONS = { | |
| 'cache': '🗃️', 'store': '📦', 'database': '🗄️', 'memory': '💾', | |
| 'allocator': '⚙️', 'type': '🔍', 'circuit': '⚡', 'gateway': '🚦', | |
| 'thread': '🧵', 'task': '📋', 'transaction': '💳', 'credit': '💰', | |
| 'employee': '👥', 'default': '🏗️' | |
| }; | |
| let rawText = ''; | |
| let showingRaw = false; | |
| let levels = { 1: '', 2: '', 3: '', 4: '' }; | |
| function setPreset(name) { | |
| document.getElementById('systemName').value = name; | |
| } | |
| function toggleKey() { | |
| const inp = document.getElementById('apiKey'); | |
| inp.type = inp.type === 'password' ? 'text' : 'password'; | |
| } | |
| function getSystemIcon(name) { | |
| const lower = name.toLowerCase(); | |
| for (const [k, v] of Object.entries(SYSTEM_ICONS)) { | |
| if (lower.includes(k)) return v; | |
| } | |
| return SYSTEM_ICONS.default; | |
| } | |
| function switchLevel(num) { | |
| document.querySelectorAll('.level-tab').forEach(t => t.classList.toggle('active', parseInt(t.dataset.level) === num)); | |
| document.querySelectorAll('.level-panel').forEach(p => p.classList.toggle('active', p.id === `lvl${num}`)); | |
| } | |
| function toggleRaw() { | |
| showingRaw = !showingRaw; | |
| document.getElementById('rawOutput').classList.toggle('visible', showingRaw); | |
| document.getElementById('structuredOutput').style.display = showingRaw ? 'none' : 'block'; | |
| document.querySelector('[onclick="toggleRaw()"]').textContent = showingRaw ? '⊞ Structured' : '⊞ Raw'; | |
| } | |
| async function copyPrompt(btn) { | |
| const systemName = document.getElementById('systemName').value.trim(); | |
| const desc = document.getElementById('systemDesc').value.trim(); | |
| if (!systemName) { | |
| alert('Enter a system name first.'); | |
| return; | |
| } | |
| const prompt = buildPrompt(systemName, desc); | |
| try { | |
| await navigator.clipboard.writeText(prompt); | |
| const label = btn.textContent; | |
| btn.textContent = '✓ Copied!'; | |
| setTimeout(() => btn.textContent = label, 2000); | |
| } catch (e) {} | |
| } | |
| function copyPromptSidebar(btn) { copyPrompt(btn); } | |
| async function copyAll() { | |
| try { | |
| await navigator.clipboard.writeText(rawText); | |
| const btn = document.querySelector('[onclick="copyAll()"]'); | |
| btn.textContent = '✓ Copied!'; | |
| setTimeout(() => btn.textContent = '⎘ Copy Output', 2000); | |
| } catch (e) {} | |
| } | |
| function copyCode(btn, text) { | |
| navigator.clipboard.writeText(text).then(() => { | |
| btn.textContent = '✓ Copied'; | |
| btn.classList.add('copied'); | |
| setTimeout(() => { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 2000); | |
| }); | |
| } | |
| function buildLevelCard(levelNum, content) { | |
| const descriptions = ['Basic Functionality & Core Classes', 'Constraints & Additional APIs', 'Concurrency & Performance', 'Extensibility & Production Requirements']; | |
| const colors = ['var(--green)', 'var(--blue)', 'var(--purple)', 'var(--accent)']; | |
| const card = document.createElement('div'); | |
| card.className = 'level-card'; | |
| card.dataset.level = levelNum; | |
| const sections = parseContent(content); | |
| card.innerHTML = ` | |
| <div class="level-card-header" data-level="${levelNum}"> | |
| <div class="level-dot"></div> | |
| <div class="level-card-title">Level ${levelNum} — ${descriptions[levelNum - 1]}</div> | |
| <div class="level-card-subtitle">${LEVEL_ICONS[levelNum - 1]} ${LEVEL_DESCRIPTIONS[levelNum - 1]}</div> | |
| </div> | |
| <div class="level-card-body"> | |
| ${sections} | |
| </div>`; | |
| return card; | |
| } | |
| function parseContent(text) { | |
| // Detect code blocks | |
| const parts = text.split(/(```[\s\S]*?```)/g); | |
| let out = ''; | |
| for (const part of parts) { | |
| if (part.startsWith('```')) { | |
| const lines = part.split('\n'); | |
| const lang = lines[0].replace('```', '').trim() || 'code'; | |
| const code = lines.slice(1, -1).join('\n'); | |
| const id = Math.random().toString(36).substr(2, 8); | |
| out += ` | |
| <div class="section"> | |
| <div class="code-block-header"> | |
| <span class="code-lang">${lang}</span> | |
| <button class="copy-btn" onclick="copyCode(this, ${JSON.stringify(code)})">Copy</button> | |
| </div> | |
| <pre>${escapeHtml(code)}</pre> | |
| </div>`; | |
| } else { | |
| out += convertMarkdown(part); | |
| } | |
| } | |
| return out; | |
| } | |
| function convertMarkdown(text) { | |
| const lines = text.split('\n'); | |
| let out = ''; | |
| let inList = false; | |
| let listItems = []; | |
| const flushList = () => { | |
| if (listItems.length) { | |
| out += `<div class="section"><ul>${listItems.map(li => `<li>${li}</li>`).join('')}</ul></div>`; | |
| listItems = []; | |
| } | |
| inList = false; | |
| }; | |
| for (let i = 0; i < lines.length; i++) { | |
| const line = lines[i]; | |
| const trimmed = line.trim(); | |
| if (!trimmed) { | |
| if (inList) flushList(); | |
| continue; | |
| } | |
| // Section labels (##, ###, ####) | |
| if (/^#{2,4}\s/.test(trimmed)) { | |
| if (inList) flushList(); | |
| const label = trimmed.replace(/^#{2,4}\s/, ''); | |
| out += `<div class="section"><div class="section-label">${label}</div></div>`; | |
| continue; | |
| } | |
| // Bullet points | |
| if (/^[-*]\s/.test(trimmed)) { | |
| inList = true; | |
| const item = trimmed.replace(/^[-*]\s/, '').replace(/`([^`]+)`/g, '<code style="font-family:JetBrains Mono,monospace;background:#060810;padding:1px 5px;border-radius:3px;font-size:12px;color:#38bdf8">$1</code>').replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>'); | |
| listItems.push(item); | |
| continue; | |
| } | |
| if (inList) flushList(); | |
| // Bold **text** | |
| const processed = trimmed | |
| .replace(/`([^`]+)`/g, '<code style="font-family:JetBrains Mono,monospace;background:#060810;padding:1px 5px;border-radius:3px;font-size:12px;color:#38bdf8">$1</code>') | |
| .replace(/\*\*([^*]+)\*\*/g, '<strong style="color:var(--heading)">$1</strong>'); | |
| out += `<div class="section"><p>${processed}</p></div>`; | |
| } | |
| if (inList) flushList(); | |
| return out; | |
| } | |
| function escapeHtml(s) { | |
| return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); | |
| } | |
| function parseLevels(text) { | |
| const result = { 1: '', 2: '', 3: '', 4: '' }; | |
| // Try to split on Level N headers | |
| const levelRegex = /(?:^|\n)(?:#{1,4}\s*)?Level\s*(\d)[\s\S]*?(?=\n(?:#{1,4}\s*)?Level\s*\d|$)/gi; | |
| let match; | |
| while ((match = levelRegex.exec(text)) !== null) { | |
| const num = parseInt(match[1]); | |
| if (num >= 1 && num <= 4) { | |
| result[num] = match[0].trim(); | |
| } | |
| } | |
| // If nothing found, try to split by "---" or lines starting with ## Level | |
| if (!result[1]) { | |
| const chunks = text.split(/\n#{1,3}\s*Level/i); | |
| for (let i = 1; i < chunks.length && i <= 4; i++) { | |
| result[i] = ('Level' + chunks[i]).trim(); | |
| } | |
| } | |
| // Fallback: split evenly | |
| if (!result[1]) { | |
| const quarterLen = Math.floor(text.length / 4); | |
| for (let i = 1; i <= 4; i++) { | |
| result[i] = text.slice((i - 1) * quarterLen, i * quarterLen); | |
| } | |
| } | |
| return result; | |
| } | |
| async function generate() { | |
| const systemName = document.getElementById('systemName').value.trim(); | |
| const apiKey = document.getElementById('apiKey').value.trim(); | |
| const desc = document.getElementById('systemDesc').value.trim(); | |
| const model = document.getElementById('modelSelect').value; | |
| const errorBox = document.getElementById('errorBox'); | |
| errorBox.style.display = 'none'; | |
| if (!systemName) { | |
| errorBox.textContent = '⚠ Please enter a system name.'; | |
| errorBox.style.display = 'block'; | |
| return; | |
| } | |
| if (!apiKey || !apiKey.startsWith('sk-')) { | |
| errorBox.textContent = '⚠ Please enter a valid OpenAI API key starting with sk-'; | |
| errorBox.style.display = 'block'; | |
| return; | |
| } | |
| const btn = document.getElementById('generateBtn'); | |
| btn.disabled = true; | |
| btn.classList.add('loading'); | |
| document.getElementById('placeholder').style.display = 'none'; | |
| document.getElementById('structuredOutput').style.display = 'none'; | |
| document.getElementById('rawOutput').classList.remove('visible'); | |
| document.getElementById('outputActions').style.display = 'none'; | |
| rawText = ''; | |
| showingRaw = false; | |
| const prompt = buildPrompt(systemName, desc); | |
| try { | |
| const response = await fetch('https://api.openai.com/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${apiKey}` | |
| }, | |
| body: JSON.stringify({ | |
| model, | |
| messages: [{ role: 'user', content: prompt }], | |
| stream: true, | |
| temperature: 0.7, | |
| max_tokens: 4096 | |
| }) | |
| }); | |
| if (!response.ok) { | |
| const err = await response.json(); | |
| throw new Error(err.error?.message || `HTTP ${response.status}`); | |
| } | |
| // Show streaming raw view first | |
| document.getElementById('rawOutput').classList.add('visible'); | |
| document.getElementById('rawOutput').textContent = ''; | |
| showingRaw = true; | |
| const reader = response.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| const chunk = decoder.decode(value); | |
| const lines = chunk.split('\n').filter(l => l.startsWith('data: ') && l !== 'data: [DONE]'); | |
| for (const line of lines) { | |
| try { | |
| const data = JSON.parse(line.slice(6)); | |
| const delta = data.choices?.[0]?.delta?.content || ''; | |
| rawText += delta; | |
| document.getElementById('rawOutput').textContent = rawText; | |
| } catch {} | |
| } | |
| } | |
| // Parse and render structured output | |
| renderStructured(systemName, desc); | |
| } catch (e) { | |
| errorBox.textContent = `⚠ Error: ${e.message}`; | |
| errorBox.style.display = 'block'; | |
| document.getElementById('rawOutput').classList.remove('visible'); | |
| document.getElementById('placeholder').style.display = 'flex'; | |
| } finally { | |
| btn.disabled = false; | |
| btn.classList.remove('loading'); | |
| } | |
| } | |
| function renderStructured(systemName, desc) { | |
| // Switch to structured view | |
| document.getElementById('rawOutput').classList.remove('visible'); | |
| showingRaw = false; | |
| // Setup banner | |
| document.getElementById('bannerIcon').textContent = getSystemIcon(systemName); | |
| document.getElementById('bannerName').textContent = systemName; | |
| document.getElementById('bannerDesc').textContent = desc || 'CodeSignal-style 4-level OOP interview prompt'; | |
| // Parse levels | |
| const parsedLevels = parseLevels(rawText); | |
| for (let i = 1; i <= 4; i++) { | |
| const container = document.getElementById(`lvl${i}`); | |
| container.innerHTML = ''; | |
| const card = buildLevelCard(i, parsedLevels[i]); | |
| container.appendChild(card); | |
| } | |
| switchLevel(1); | |
| document.getElementById('structuredOutput').style.display = 'block'; | |
| document.getElementById('outputActions').style.display = 'flex'; | |
| document.getElementById('rawOutput').textContent = rawText; | |
| // Update raw ref | |
| document.querySelector('[onclick="toggleRaw()"]').textContent = '⊞ Raw'; | |
| } | |
| function buildPrompt(systemName, description) { | |
| const descPart = description ? `\nAdditional context for this system: ${description}\n` : ''; | |
| return `You are an interview question designer. | |
| For the system listed below, generate a **CodeSignal-style 4-level coding prompt** that incrementally builds an object-oriented system. | |
| ### System | |
| - ${systemName} | |
| ${descPart} | |
| ### Requirements | |
| - Generate **4 levels**, where: | |
| - **Level 1**: Basic functionality and core classes (single-threaded, minimal features) | |
| - **Level 2**: Adds constraints, edge cases, or additional APIs | |
| - **Level 3**: Introduces concurrency, performance constraints, or correctness guarantees | |
| - **Level 4**: Extensibility or production-like requirements (pluggability, configuration, failure handling) | |
| - Each level must include: | |
| - A clear problem statement | |
| - Required public methods and their signatures (use code blocks with \`\`\`python or \`\`\`java) | |
| - Explicit constraints and assumptions | |
| - Example inputs/outputs or representative test cases (in code blocks) | |
| - Keep the scope realistic for a **45–60 minute interview**. | |
| - Focus on **class design, APIs, and invariants**, not UI or persistence. | |
| - Do **not** include the solution—only the prompt. | |
| ### Output Format | |
| - Use clear section headers for each level: ## Level 1, ## Level 2, ## Level 3, ## Level 4 | |
| - Under each level use subsections: ### Problem Statement, ### API, ### Constraints, ### Examples | |
| - Keep descriptions concise and interview-realistic.`; | |
| } | |
| </script> | |
| </body> | |
| </html> |