Skip to content

Commit e20f910

Browse files
feat(edit-schema): structured change cards + purple dark diagram
Replace the raw SQL block AI returns with a structured change card per change: action label + icon tinted by tone (add / edit / remove), table name as a monospaced heading, free-text aiSummary, and a column grid for CREATE TABLE entries that lines up the type/badge columns across rows via CSS subgrid (display: contents). Columns get PK/FK badges (with tooltips showing the referenced table), plus NOT NULL / UNIQUE / DEFAULT chips. Raw SQL is still available, but tucked behind a "View SQL" details toggle. Recolor the schema diagram in dark mode via mermaid themeVariables + CSS safety net: dark violet backgrounds (#1d143a), muted purple borders (#3d2b6e), light-lilac text and edges, no alternating row stripes — so the diagram has a Rocketadmin-violet feel instead of bare mermaid gray. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4e5c962 commit e20f910

5 files changed

Lines changed: 486 additions & 33 deletions

File tree

frontend/src/app/components/edit-database-schema/edit-database-schema.component.css

Lines changed: 226 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -513,56 +513,260 @@
513513
margin-top: 10px;
514514
}
515515

516-
.schema-editor__change-card {
517-
border: 1px solid rgba(0, 0, 0, 0.1);
518-
border-radius: 6px;
519-
padding: 8px 10px;
520-
background: rgba(0, 0, 0, 0.02);
516+
/* ── Structured change card ── */
517+
518+
.change-card {
519+
border: 1px solid rgba(0, 0, 0, 0.08);
520+
border-radius: 8px;
521+
padding: 10px 12px;
522+
background: #fff;
523+
display: flex;
524+
flex-direction: column;
525+
gap: 8px;
521526
}
522527

523528
@media (prefers-color-scheme: dark) {
524-
.schema-editor__change-card {
525-
border-color: rgba(255, 255, 255, 0.12);
529+
.change-card {
530+
border-color: rgba(255, 255, 255, 0.1);
526531
background: rgba(255, 255, 255, 0.04);
527532
}
528533
}
529534

530-
.schema-editor__change-header {
535+
.change-card__head {
531536
display: flex;
532537
align-items: center;
533-
gap: 8px;
534-
margin-bottom: 6px;
538+
gap: 10px;
535539
}
536540

537-
.schema-editor__change-type {
538-
font-size: 9px;
541+
.change-card__action-icon {
542+
flex-shrink: 0;
543+
width: 28px;
544+
height: 28px;
545+
border-radius: 7px;
546+
display: flex;
547+
align-items: center;
548+
justify-content: center;
549+
}
550+
551+
.change-card__action-icon mat-icon {
552+
font-size: 16px !important;
553+
width: 16px !important;
554+
height: 16px !important;
555+
}
556+
557+
.change-card[data-tone="add"] .change-card__action-icon {
558+
background: color-mix(in srgb, #16a34a 14%, transparent);
559+
color: #15803d;
560+
}
561+
562+
.change-card[data-tone="edit"] .change-card__action-icon {
563+
background: color-mix(in srgb, var(--color-accentedPalette-500) 14%, transparent);
564+
color: var(--color-accentedPalette-500);
565+
}
566+
567+
.change-card[data-tone="remove"] .change-card__action-icon {
568+
background: color-mix(in srgb, #dc2626 14%, transparent);
569+
color: #dc2626;
570+
}
571+
572+
@media (prefers-color-scheme: dark) {
573+
.change-card[data-tone="add"] .change-card__action-icon { color: #4ade80; }
574+
.change-card[data-tone="remove"] .change-card__action-icon { color: #f87171; }
575+
}
576+
577+
.change-card__head-text {
578+
display: flex;
579+
flex-direction: column;
580+
gap: 1px;
581+
min-width: 0;
582+
}
583+
584+
.change-card__action {
585+
font-size: 11px;
539586
font-weight: 600;
540587
text-transform: uppercase;
541-
letter-spacing: 0.3px;
542-
padding: 2px 5px;
588+
letter-spacing: 0.4px;
589+
color: rgba(0, 0, 0, 0.45);
590+
}
591+
592+
@media (prefers-color-scheme: dark) {
593+
.change-card__action {
594+
color: rgba(255, 255, 255, 0.5);
595+
}
596+
}
597+
598+
.change-card__target {
599+
font-size: 14px;
600+
font-weight: 600;
601+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
602+
color: rgba(0, 0, 0, 0.88);
603+
overflow: hidden;
604+
text-overflow: ellipsis;
605+
white-space: nowrap;
606+
}
607+
608+
@media (prefers-color-scheme: dark) {
609+
.change-card__target {
610+
color: rgba(255, 255, 255, 0.92);
611+
}
612+
}
613+
614+
.change-card__summary {
615+
margin: 0;
616+
font-size: 12.5px;
617+
color: rgba(0, 0, 0, 0.62);
618+
line-height: 1.45;
619+
}
620+
621+
@media (prefers-color-scheme: dark) {
622+
.change-card__summary {
623+
color: rgba(255, 255, 255, 0.62);
624+
}
625+
}
626+
627+
.change-card__columns {
628+
list-style: none;
629+
margin: 0;
630+
padding: 0;
631+
display: grid;
632+
grid-template-columns: max-content minmax(0, 1fr) auto;
633+
border: 1px solid rgba(0, 0, 0, 0.06);
634+
border-radius: 6px;
635+
overflow: hidden;
636+
font-size: 12px;
637+
}
638+
639+
@media (prefers-color-scheme: dark) {
640+
.change-card__columns {
641+
border-color: rgba(255, 255, 255, 0.08);
642+
}
643+
}
644+
645+
.change-card__column {
646+
display: contents;
647+
}
648+
649+
.change-card__column > * {
650+
display: flex;
651+
align-items: center;
652+
padding: 6px 10px;
653+
border-top: 1px solid rgba(0, 0, 0, 0.04);
654+
}
655+
656+
.change-card__column:first-child > * {
657+
border-top: none;
658+
}
659+
660+
@media (prefers-color-scheme: dark) {
661+
.change-card__column > * {
662+
border-top-color: rgba(255, 255, 255, 0.06);
663+
}
664+
}
665+
666+
.change-card__column-name {
667+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
668+
font-weight: 500;
669+
color: rgba(0, 0, 0, 0.85);
670+
}
671+
672+
@media (prefers-color-scheme: dark) {
673+
.change-card__column-name {
674+
color: rgba(255, 255, 255, 0.88);
675+
}
676+
}
677+
678+
.change-card__column-type {
679+
color: rgba(0, 0, 0, 0.5);
680+
font-size: 11.5px;
681+
padding-left: 0 !important;
682+
}
683+
684+
@media (prefers-color-scheme: dark) {
685+
.change-card__column-type {
686+
color: rgba(255, 255, 255, 0.55);
687+
}
688+
}
689+
690+
.change-card__column-badges {
691+
flex-wrap: wrap;
692+
gap: 4px;
693+
justify-content: flex-end;
694+
}
695+
696+
.change-card__badge {
697+
font-size: 9px;
698+
font-weight: 700;
699+
letter-spacing: 0.5px;
700+
padding: 2px 6px;
543701
border-radius: 3px;
544-
background: var(--color-primaryPalette-500);
545-
color: white;
702+
line-height: 1.2;
703+
text-transform: uppercase;
704+
}
705+
706+
.change-card__badge--pk {
707+
background: color-mix(in srgb, #f59e0b 18%, transparent);
708+
color: #b45309;
709+
}
710+
711+
.change-card__badge--fk {
712+
background: color-mix(in srgb, var(--color-accentedPalette-500) 18%, transparent);
713+
color: var(--color-accentedPalette-500);
714+
}
715+
716+
.change-card__badge--nn,
717+
.change-card__badge--uq,
718+
.change-card__badge--def {
719+
background: rgba(0, 0, 0, 0.06);
720+
color: rgba(0, 0, 0, 0.6);
721+
}
722+
723+
@media (prefers-color-scheme: dark) {
724+
.change-card__badge--pk { color: #fbbf24; }
725+
.change-card__badge--nn,
726+
.change-card__badge--uq,
727+
.change-card__badge--def {
728+
background: rgba(255, 255, 255, 0.08);
729+
color: rgba(255, 255, 255, 0.6);
730+
}
546731
}
547732

548-
.schema-editor__change-table {
733+
.change-card__sql {
734+
margin: 0;
735+
font-size: 11px;
736+
}
737+
738+
.change-card__sql summary {
739+
cursor: pointer;
740+
user-select: none;
741+
font-size: 11px;
549742
font-weight: 500;
550-
font-size: 12px;
743+
color: rgba(0, 0, 0, 0.5);
744+
padding: 2px 0;
551745
}
552746

553-
.schema-editor__change-sql {
747+
.change-card__sql summary:hover {
748+
color: var(--color-accentedPalette-500);
749+
}
750+
751+
@media (prefers-color-scheme: dark) {
752+
.change-card__sql summary {
753+
color: rgba(255, 255, 255, 0.5);
754+
}
755+
}
756+
757+
.change-card__sql pre {
554758
background: rgba(0, 0, 0, 0.04);
555759
border-radius: 4px;
556760
padding: 8px 10px;
557761
font-size: 11px;
558-
font-family: monospace;
762+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
559763
overflow-x: auto;
560764
white-space: pre-wrap;
561-
margin: 0;
765+
margin: 6px 0 0;
562766
}
563767

564768
@media (prefers-color-scheme: dark) {
565-
.schema-editor__change-sql {
769+
.change-card__sql pre {
566770
background: rgba(255, 255, 255, 0.06);
567771
}
568772
}

frontend/src/app/components/edit-database-schema/edit-database-schema.component.html

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,54 @@ <h1 class="schema-editor__title">Edit Database Schema</h1>
136136
<div class="schema-editor__message schema-editor__message--ai">
137137
<span class="schema-editor__message-text">{{ message.text }}</span>
138138

139-
@if (message.changes?.length) {
139+
@if (message.parsedChanges?.length) {
140140
<div class="schema-editor__changes">
141-
@for (change of message.changes; track change.id) {
142-
<div class="schema-editor__change-card">
143-
<div class="schema-editor__change-header">
144-
<span class="schema-editor__change-type">{{ change.changeType }}</span>
145-
<span class="schema-editor__change-table">{{ change.targetTableName }}</span>
141+
@for (change of message.parsedChanges; track change.id) {
142+
<div class="change-card" [attr.data-tone]="change.tone">
143+
<div class="change-card__head">
144+
<div class="change-card__action-icon">
145+
<mat-icon>{{ change.icon }}</mat-icon>
146+
</div>
147+
<div class="change-card__head-text">
148+
<span class="change-card__action">{{ change.action }}</span>
149+
<span class="change-card__target">{{ change.targetTableName }}</span>
150+
</div>
146151
</div>
147-
<pre class="schema-editor__change-sql">{{ change.forwardSql }}</pre>
152+
@if (change.aiSummary) {
153+
<p class="change-card__summary">{{ change.aiSummary }}</p>
154+
}
155+
@if (change.columns.length) {
156+
<ul class="change-card__columns">
157+
@for (col of change.columns; track col.name) {
158+
<li class="change-card__column">
159+
<span class="change-card__column-name">{{ col.name }}</span>
160+
<span class="change-card__column-type">{{ col.type || '—' }}</span>
161+
<span class="change-card__column-badges">
162+
@if (col.pk) {
163+
<span class="change-card__badge change-card__badge--pk" matTooltip="Primary key">PK</span>
164+
}
165+
@if (col.fk) {
166+
<span class="change-card__badge change-card__badge--fk"
167+
[matTooltip]="col.referenceTable ? '→ ' + col.referenceTable : 'Foreign key'">FK</span>
168+
}
169+
@if (col.notNull && !col.pk) {
170+
<span class="change-card__badge change-card__badge--nn">NOT NULL</span>
171+
}
172+
@if (col.unique && !col.pk) {
173+
<span class="change-card__badge change-card__badge--uq">UNIQUE</span>
174+
}
175+
@if (col.defaultValue) {
176+
<span class="change-card__badge change-card__badge--def">DEFAULT</span>
177+
}
178+
</span>
179+
</li>
180+
}
181+
</ul>
182+
}
183+
<details class="change-card__sql">
184+
<summary>View SQL</summary>
185+
<pre>{{ change.forwardSql }}</pre>
186+
</details>
148187
</div>
149188
}
150189
</div>

0 commit comments

Comments
 (0)