Skip to content

Commit 1fc1382

Browse files
committed
feat(frontend): implement diagnostic AST visualization with Apple-inspired design
1 parent 368e2a4 commit 1fc1382

2 files changed

Lines changed: 365 additions & 80 deletions

File tree

apps/frontend/src/app/globals.css

Lines changed: 210 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@
33
:root {
44
color-scheme: light;
55
--ios-purple: #8c52ff;
6-
--ios-teal: #5ce1e6;
7-
--ios-gold: #f4ca44;
8-
--ios-green: #59b379;
9-
--ios-ink: #0b0b16;
10-
--ios-ink-soft: #151528;
6+
--ios-teal: #00d2ff;
7+
--ios-gold: #ffbd2e;
8+
--ios-red: #ff3b30;
9+
--ios-green: #34c759;
10+
--ios-ink: #000000;
11+
--ios-ink-soft: #48484a;
1112
--ios-surface: #ffffff;
12-
--ios-surface-muted: #f6f7fb;
13-
--ios-outline: rgba(15, 24, 46, 0.12);
14-
--ios-shadow: 0 20px 60px rgba(24, 28, 52, 0.12);
15-
--ios-glow: 0 0 80px rgba(92, 225, 230, 0.35);
16-
--ios-font: "SF Pro Display", "SF Pro Text", -apple-system, BlinkMacSystemFont,
17-
"Segoe UI", sans-serif;
13+
--ios-surface-muted: #f2f2f7;
14+
--ios-outline: rgba(0, 0, 0, 0.08);
15+
--ios-shadow: 0 10px 40px rgba(0, 0, 0, 0.06);
16+
--ios-glass: rgba(255, 255, 255, 0.72);
17+
--ios-font: "SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1818
}
1919

2020
* {
@@ -71,15 +71,15 @@ body {
7171
display: flex;
7272
align-items: center;
7373
justify-content: space-between;
74-
max-width: 1100px;
74+
max-width: 1000px;
7575
width: 100%;
76-
margin: 0 auto 40px;
77-
padding: 16px 20px;
78-
background: rgba(255, 255, 255, 0.7);
76+
margin: 0 auto 32px;
77+
padding: 12px 24px;
78+
background: var(--ios-glass);
7979
border: 1px solid var(--ios-outline);
80-
border-radius: 18px;
80+
border-radius: 20px;
8181
box-shadow: var(--ios-shadow);
82-
backdrop-filter: blur(16px);
82+
backdrop-filter: blur(20px) saturate(180%);
8383
}
8484

8585
.logo {
@@ -139,26 +139,28 @@ body {
139139
}
140140

141141
.primary-button {
142-
color: #101019;
143-
background: linear-gradient(90deg, var(--ios-gold), var(--ios-green));
144-
box-shadow: 0 16px 30px rgba(89, 179, 123, 0.28);
142+
color: #fff;
143+
background: linear-gradient(135deg, #007aff, #00c6ff);
144+
box-shadow: 0 8px 16px rgba(0, 122, 255, 0.2);
145+
}
146+
147+
.primary-button:active {
148+
transform: scale(0.98);
145149
}
146150

147151
.secondary-button {
148-
background: var(--ios-surface);
152+
background: rgba(0, 0, 0, 0.04);
149153
color: var(--ios-ink);
150-
border: 1px solid var(--ios-outline);
154+
border: 1px solid transparent;
151155
}
152156

153-
.ghost-button {
154-
background: transparent;
155-
color: var(--ios-ink);
157+
.secondary-button:hover {
158+
background: rgba(0, 0, 0, 0.08);
156159
}
157160

158-
.primary-button:hover,
159-
.secondary-button:hover,
160-
.ghost-button:hover {
161-
transform: translateY(-1px);
161+
.ghost-button {
162+
background: transparent;
163+
color: #007aff;
162164
}
163165

164166
.shell {
@@ -285,31 +287,45 @@ body {
285287
}
286288

287289
.dropzone {
288-
border: 2px dashed rgba(140, 82, 255, 0.25);
289-
border-radius: 20px;
290-
padding: 24px;
290+
border: 2px dashed rgba(0, 122, 255, 0.2);
291+
border-radius: 24px;
292+
padding: 40px 24px;
291293
display: flex;
292294
flex-direction: column;
293-
gap: 14px;
295+
gap: 20px;
294296
align-items: center;
295297
text-align: center;
296-
background: rgba(140, 82, 255, 0.06);
298+
background: linear-gradient(180deg, rgba(0, 122, 255, 0.02), rgba(0, 122, 255, 0.05));
299+
transition: border-color 0.2s ease, background 0.2s ease;
300+
}
301+
302+
.dropzone:hover {
303+
border-color: rgba(0, 122, 255, 0.4);
304+
background: rgba(0, 122, 255, 0.08);
297305
}
298306

299307
.dropzone-content {
300308
display: grid;
301-
gap: 8px;
309+
gap: 4px;
302310
justify-items: center;
303311
}
304312

313+
.dropzone strong {
314+
font-size: 16px;
315+
color: var(--ios-ink);
316+
}
317+
305318
.dropzone span {
306319
display: block;
307-
font-size: 12px;
308-
color: rgba(11, 11, 22, 0.6);
320+
font-size: 13px;
321+
color: var(--ios-ink-soft);
322+
opacity: 0.7;
309323
}
310324

311325
.drop-icon {
312-
font-size: 28px;
326+
font-size: 32px;
327+
margin-bottom: 8px;
328+
color: #007aff;
313329
}
314330

315331
.file-input {
@@ -791,3 +807,159 @@ body {
791807
justify-self: stretch;
792808
}
793809
}
810+
811+
@keyframes slideDown {
812+
from {
813+
opacity: 0;
814+
transform: translateY(-10px);
815+
}
816+
to {
817+
opacity: 1;
818+
transform: translateY(0);
819+
}
820+
}
821+
822+
@keyframes spin {
823+
to { transform: rotate(360deg); }
824+
}
825+
826+
.spinner {
827+
width: 20px;
828+
height: 20px;
829+
border: 2px solid rgba(255, 255, 255, 0.3);
830+
border-top-color: #fff;
831+
border-radius: 50%;
832+
animation: spin 0.8s linear infinite;
833+
}
834+
835+
/* AST Visualization Styles */
836+
.ast-container {
837+
padding: 40px 20px;
838+
background: rgba(0, 0, 0, 0.02);
839+
border-radius: 24px;
840+
overflow-x: auto;
841+
display: flex;
842+
justify-content: center;
843+
min-height: 400px;
844+
position: relative;
845+
}
846+
847+
.ast-tree {
848+
display: flex;
849+
flex-direction: column;
850+
align-items: center;
851+
gap: 60px;
852+
position: relative;
853+
}
854+
855+
.ast-level {
856+
display: flex;
857+
gap: 40px;
858+
justify-content: center;
859+
position: relative;
860+
}
861+
862+
.ast-node {
863+
display: flex;
864+
flex-direction: column;
865+
align-items: center;
866+
gap: 8px;
867+
position: relative;
868+
transition: transform 0.2s ease;
869+
z-index: 2;
870+
}
871+
872+
.ast-node:hover {
873+
transform: scale(1.05);
874+
}
875+
876+
.ast-node-icon {
877+
width: 48px;
878+
height: 48px;
879+
border-radius: 14px;
880+
background: var(--ios-surface);
881+
border: 1px solid var(--ios-outline);
882+
box-shadow: var(--ios-shadow);
883+
display: grid;
884+
place-items: center;
885+
font-size: 20px;
886+
color: #007aff;
887+
transition: all 0.2s ease;
888+
}
889+
890+
.ast-node--error .ast-node-icon {
891+
border-color: rgba(255, 59, 48, 0.4);
892+
background: rgba(255, 59, 48, 0.05);
893+
color: var(--ios-red);
894+
box-shadow: 0 0 20px rgba(255, 59, 48, 0.2);
895+
animation: nodePulse 2s infinite;
896+
}
897+
898+
.ast-node--warning .ast-node-icon {
899+
border-color: rgba(255, 189, 46, 0.4);
900+
background: rgba(255, 189, 46, 0.05);
901+
color: var(--ios-gold);
902+
}
903+
904+
.ast-node-label {
905+
font-size: 11px;
906+
font-weight: 600;
907+
color: var(--ios-ink);
908+
white-space: nowrap;
909+
max-width: 120px;
910+
overflow: hidden;
911+
text-overflow: ellipsis;
912+
background: var(--ios-glass);
913+
padding: 2px 8px;
914+
border-radius: 4px;
915+
backdrop-filter: blur(4px);
916+
}
917+
918+
.ast-node-sublabel {
919+
font-size: 9px;
920+
color: var(--ios-ink-soft);
921+
opacity: 0.6;
922+
}
923+
924+
.ast-connector {
925+
position: absolute;
926+
top: 48px;
927+
left: 50%;
928+
width: 2px;
929+
background: var(--ios-outline);
930+
height: 60px;
931+
transform: translateX(-50%);
932+
z-index: 1;
933+
}
934+
935+
@keyframes nodePulse {
936+
0% { box-shadow: 0 0 0 0 rgba(255, 59, 48, 0.4); }
937+
70% { box-shadow: 0 0 0 15px rgba(255, 59, 48, 0); }
938+
100% { box-shadow: 0 0 0 0 rgba(255, 59, 48, 0); }
939+
}
940+
941+
.mode-toggle {
942+
display: flex;
943+
background: rgba(0, 0, 0, 0.05);
944+
padding: 4px;
945+
border-radius: 12px;
946+
gap: 4px;
947+
}
948+
949+
.mode-toggle button {
950+
padding: 6px 12px;
951+
border-radius: 8px;
952+
font-size: 12px;
953+
font-weight: 600;
954+
border: none;
955+
background: transparent;
956+
color: var(--ios-ink-soft);
957+
cursor: pointer;
958+
transition: all 0.2s ease;
959+
}
960+
961+
.mode-toggle button.is-active {
962+
background: var(--ios-surface);
963+
color: #007aff;
964+
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
965+
}

0 commit comments

Comments
 (0)