From 3d0b6bdc132bc12bab8f61d216cf22f33dea4c07 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Mon, 18 May 2026 15:51:32 +0300 Subject: [PATCH 1/6] feat(feature-notification): SVG animation for Edit Schema with AI Replace the saved-filters GIF with an inline animated SVG that shows the AI prompt bubble, three tables appearing in sequence, animated relationship arrows, and pulsing sparkles. Updates the feature ID so users see the new notification, and refreshes the title/description for the schema editor. Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/src/app/app.component.ts | 2 +- .../feature-notification.component.css | 150 +++++++++++++++++- .../feature-notification.component.html | 82 +++++++++- 3 files changed, 225 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 8d08e92af..3332934f8 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -62,7 +62,7 @@ export class AppComponent { public appVersion = version; userActivity; userInactive: Subject = new Subject(); - currentFeatureNotificationId: string = 'saved-filters'; + currentFeatureNotificationId: string = 'edit-schema'; isFeatureNotificationShown: boolean = false; userLoggedIn = null; diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.css b/frontend/src/app/components/feature-notification/feature-notification.component.css index f5e7c6dcb..6832f5f67 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.css +++ b/frontend/src/app/components/feature-notification/feature-notification.component.css @@ -18,11 +18,18 @@ } } -.feature-notification__image { +.feature-notification__animation { + width: 100%; border-radius: 8px; - height: auto; + overflow: hidden; margin-bottom: 16px; + color: #6384ff; +} + +.fn-svg { + display: block; width: 100%; + height: auto; } .feature-notification__title { @@ -47,3 +54,142 @@ gap: 8px; margin-top: 16px; } + +/* ── Animation ── */ + +/* Animation cycle: 5.4s total + * 0.0–0.6s: prompt bubble slides in + * 0.6–1.2s: table 1 appears + * 1.2–1.8s: table 2 appears + * 1.8–2.4s: table 3 appears + * 1.8–2.6s: link 1 draws + * 2.4–3.0s: link 2 draws + * 3.0–4.6s: sparkles pulse + * 4.6–5.4s: hold, then loop + */ + +.fn-prompt { + transform-origin: 14px 39px; + transform: translateX(-20px); + opacity: 0; + animation: fn-prompt-in 5.4s ease-in-out infinite; +} + +.fn-table { + opacity: 0; + transform-origin: center; + transform: translateY(8px) scale(0.9); +} + +.fn-table--1 { animation: fn-table-in 5.4s ease-out infinite; animation-delay: 0s; } +.fn-table--2 { animation: fn-table-in 5.4s ease-out infinite; animation-delay: 0s; } +.fn-table--3 { animation: fn-table-in 5.4s ease-out infinite; animation-delay: 0s; } + +.fn-link { + stroke-dasharray: 60; + stroke-dashoffset: 60; + animation: fn-link-draw 5.4s ease-in-out infinite; +} + +.fn-link--1 { animation-delay: 0s; } +.fn-link--2 { animation-delay: 0s; } + +.fn-sparkle { + transform-origin: center; + opacity: 0; +} + +.fn-sparkle--a { animation: fn-sparkle 5.4s ease-in-out infinite; transform-origin: 120px 23.6px; } +.fn-sparkle--b { animation: fn-sparkle 5.4s ease-in-out infinite 0.2s; transform-origin: 310px 43.6px; } +.fn-sparkle--c { animation: fn-sparkle 5.4s ease-in-out infinite 0.4s; transform-origin: 340px 133.6px; } + +.fn-cursor { + opacity: 0; + animation: fn-cursor 5.4s linear infinite; +} + +/* Per-table staggered timing via dedicated keyframes */ +.fn-table--1 { animation-name: fn-table-1-in; } +.fn-table--2 { animation-name: fn-table-2-in; } +.fn-table--3 { animation-name: fn-table-3-in; } + +.fn-link--1 { animation-name: fn-link-1-draw; } +.fn-link--2 { animation-name: fn-link-2-draw; } + +@keyframes fn-prompt-in { + 0% { opacity: 0; transform: translateX(-20px); } + 8% { opacity: 1; transform: translateX(0); } + 88% { opacity: 1; transform: translateX(0); } + 100% { opacity: 0; transform: translateX(-20px); } +} + +@keyframes fn-table-1-in { + 0%, 11% { opacity: 0; transform: translateY(8px) scale(0.92); } + 22% { opacity: 1; transform: translateY(0) scale(1); } + 88% { opacity: 1; transform: translateY(0) scale(1); } + 100% { opacity: 0; transform: translateY(8px) scale(0.92); } +} + +@keyframes fn-table-2-in { + 0%, 22% { opacity: 0; transform: translateY(8px) scale(0.92); } + 33% { opacity: 1; transform: translateY(0) scale(1); } + 88% { opacity: 1; transform: translateY(0) scale(1); } + 100% { opacity: 0; transform: translateY(8px) scale(0.92); } +} + +@keyframes fn-table-3-in { + 0%, 33% { opacity: 0; transform: translateY(8px) scale(0.92); } + 44% { opacity: 1; transform: translateY(0) scale(1); } + 88% { opacity: 1; transform: translateY(0) scale(1); } + 100% { opacity: 0; transform: translateY(8px) scale(0.92); } +} + +@keyframes fn-link-1-draw { + 0%, 33% { stroke-dashoffset: 60; } + 48% { stroke-dashoffset: 0; } + 88% { stroke-dashoffset: 0; } + 100% { stroke-dashoffset: 60; } +} + +@keyframes fn-link-2-draw { + 0%, 44% { stroke-dashoffset: 60; } + 56% { stroke-dashoffset: 0; } + 88% { stroke-dashoffset: 0; } + 100% { stroke-dashoffset: 60; } +} + +@keyframes fn-sparkle { + 0%, 60% { opacity: 0; transform: scale(0.4); } + 68% { opacity: 1; transform: scale(1.2); } + 78% { opacity: 1; transform: scale(1); } + 86% { opacity: 0; transform: scale(0.4); } + 100% { opacity: 0; transform: scale(0.4); } +} + +@keyframes fn-cursor { + 0% { opacity: 0; transform: translate(80px, 32px); } + 5% { opacity: 1; transform: translate(80px, 32px); } + 18% { opacity: 1; transform: translate(110px, 28px); } + 24% { opacity: 0; transform: translate(110px, 28px); } + 100% { opacity: 0; transform: translate(110px, 28px); } +} + +@media (prefers-reduced-motion: reduce) { + .fn-prompt, + .fn-table, + .fn-link, + .fn-sparkle, + .fn-cursor { + animation: none !important; + opacity: 1 !important; + transform: none !important; + stroke-dashoffset: 0 !important; + } +} + +@media (prefers-color-scheme: dark) { + .fn-bg { fill: #2a2540; } + .fn-table rect:first-child { fill: #1f1e2c; stroke: #3a3b58; } + .fn-prompt rect:first-child { fill: #1f1e2c; stroke: #3a3b58; } + .fn-table text { fill: #d8dcff; } +} diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.html b/frontend/src/app/components/feature-notification/feature-notification.component.html index b232bf608..a423fd23d 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.html +++ b/frontend/src/app/components/feature-notification/feature-notification.component.html @@ -1,15 +1,85 @@
- Feature Notification Icon -

New: Fast Filters

+ +

New: Edit Schema with AI

- Save your filter setups and quickly change options on the fly + Describe the tables you need and let AI generate the schema for you.

- From 65c59a7e5479543cf62060f9ef3e3218a59ccd56 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Mon, 18 May 2026 16:07:11 +0300 Subject: [PATCH 2/6] feat(feature-notification): merge schema-build and find-it-here into one scene MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Combine the two acts into a single 5.2s scene: a slim connection card (my_database / PostgreSQL) appears at the top, the cursor moves to the pulsing schema icon, and a wavy three-table diagram unfolds below with curved relationship lines. Reshape the schema glyph to match the actual Material "schema" icon and rotate it 90° CCW. Update the copy to mention creating *and* editing tables. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../feature-notification.component.css | 169 +++++++++--------- .../feature-notification.component.html | 123 +++++++------ 2 files changed, 160 insertions(+), 132 deletions(-) diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.css b/frontend/src/app/components/feature-notification/feature-notification.component.css index 6832f5f67..8910b78c6 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.css +++ b/frontend/src/app/components/feature-notification/feature-notification.component.css @@ -55,131 +55,136 @@ margin-top: 16px; } -/* ── Animation ── */ - -/* Animation cycle: 5.4s total - * 0.0–0.6s: prompt bubble slides in - * 0.6–1.2s: table 1 appears - * 1.2–1.8s: table 2 appears - * 1.8–2.4s: table 3 appears - * 1.8–2.6s: link 1 draws - * 2.4–3.0s: link 2 draws - * 3.0–4.6s: sparkles pulse - * 4.6–5.4s: hold, then loop - */ - -.fn-prompt { - transform-origin: 14px 39px; - transform: translateX(-20px); +/* ──────────────────────────────────────────── */ +/* Single-scene animation */ +/* Cycle: 5.2s */ +/* 0–0.4s: card slides in */ +/* 0.4–1.3s: cursor approaches schema icon */ +/* 1.0–1.5s: pulse rings on icon */ +/* 1.5–2.7s: tables fade in + links draw */ +/* 2.7–3.0s: sparkle */ +/* 3.0–4.8s: hold */ +/* 4.8–5.2s: fade out → loop */ +/* ──────────────────────────────────────────── */ + +.fn-connection-card { opacity: 0; - animation: fn-prompt-in 5.4s ease-in-out infinite; + transform: translateY(-6px); + animation: fn-card-in 5.2s ease-out infinite; +} + +.fn-pulse { + transform-origin: 322px 30px; + opacity: 0; +} + +.fn-pulse--1 { animation: fn-pulse-ring 5.2s ease-out infinite; } +.fn-pulse--2 { animation: fn-pulse-ring 5.2s ease-out infinite -0.45s; } + +.fn-pointer { + opacity: 0; + animation: fn-pointer 5.2s ease-in-out infinite; } .fn-table { opacity: 0; transform-origin: center; - transform: translateY(8px) scale(0.9); + transform: translateY(8px) scale(0.92); } -.fn-table--1 { animation: fn-table-in 5.4s ease-out infinite; animation-delay: 0s; } -.fn-table--2 { animation: fn-table-in 5.4s ease-out infinite; animation-delay: 0s; } -.fn-table--3 { animation: fn-table-in 5.4s ease-out infinite; animation-delay: 0s; } +.fn-table--1 { animation: fn-table-1-in 5.2s ease-out infinite; } +.fn-table--2 { animation: fn-table-2-in 5.2s ease-out infinite; } +.fn-table--3 { animation: fn-table-3-in 5.2s ease-out infinite; } .fn-link { stroke-dasharray: 60; stroke-dashoffset: 60; - animation: fn-link-draw 5.4s ease-in-out infinite; } -.fn-link--1 { animation-delay: 0s; } -.fn-link--2 { animation-delay: 0s; } +.fn-link--1 { animation: fn-link-1-draw 5.2s ease-in-out infinite; } +.fn-link--2 { animation: fn-link-2-draw 5.2s ease-in-out infinite; } .fn-sparkle { - transform-origin: center; + transform-origin: 120px 70px; opacity: 0; + animation: fn-sparkle 5.2s ease-in-out infinite; } -.fn-sparkle--a { animation: fn-sparkle 5.4s ease-in-out infinite; transform-origin: 120px 23.6px; } -.fn-sparkle--b { animation: fn-sparkle 5.4s ease-in-out infinite 0.2s; transform-origin: 310px 43.6px; } -.fn-sparkle--c { animation: fn-sparkle 5.4s ease-in-out infinite 0.4s; transform-origin: 340px 133.6px; } - -.fn-cursor { - opacity: 0; - animation: fn-cursor 5.4s linear infinite; +@keyframes fn-card-in { + 0% { opacity: 0; transform: translateY(-6px); } + 8% { opacity: 1; transform: translateY(0); } + 92% { opacity: 1; transform: translateY(0); } + 100% { opacity: 0; transform: translateY(-6px); } } -/* Per-table staggered timing via dedicated keyframes */ -.fn-table--1 { animation-name: fn-table-1-in; } -.fn-table--2 { animation-name: fn-table-2-in; } -.fn-table--3 { animation-name: fn-table-3-in; } - -.fn-link--1 { animation-name: fn-link-1-draw; } -.fn-link--2 { animation-name: fn-link-2-draw; } +@keyframes fn-pointer { + 0%, 8% { opacity: 0; transform: translate(80px, 90px); } + 14% { opacity: 1; transform: translate(80px, 90px); } + 25% { opacity: 1; transform: translate(312px, 26px); } + 34% { opacity: 1; transform: translate(312px, 26px) scale(0.85); } + 38% { opacity: 1; transform: translate(312px, 26px) scale(1); } + 60% { opacity: 1; transform: translate(312px, 26px); } + 92% { opacity: 0; transform: translate(312px, 26px); } + 100% { opacity: 0; transform: translate(312px, 26px); } +} -@keyframes fn-prompt-in { - 0% { opacity: 0; transform: translateX(-20px); } - 8% { opacity: 1; transform: translateX(0); } - 88% { opacity: 1; transform: translateX(0); } - 100% { opacity: 0; transform: translateX(-20px); } +@keyframes fn-pulse-ring { + 0%, 18% { opacity: 0; transform: scale(0.7); } + 24% { opacity: 0.8; transform: scale(0.9); } + 38% { opacity: 0; transform: scale(1.8); } + 100% { opacity: 0; transform: scale(1.8); } } @keyframes fn-table-1-in { - 0%, 11% { opacity: 0; transform: translateY(8px) scale(0.92); } - 22% { opacity: 1; transform: translateY(0) scale(1); } - 88% { opacity: 1; transform: translateY(0) scale(1); } - 100% { opacity: 0; transform: translateY(8px) scale(0.92); } + 0%, 30% { opacity: 0; transform: translateY(8px) scale(0.92); } + 38% { opacity: 1; transform: translateY(0) scale(1); } + 92% { opacity: 1; transform: translateY(0) scale(1); } + 100% { opacity: 0; transform: translateY(8px) scale(0.92); } } @keyframes fn-table-2-in { - 0%, 22% { opacity: 0; transform: translateY(8px) scale(0.92); } - 33% { opacity: 1; transform: translateY(0) scale(1); } - 88% { opacity: 1; transform: translateY(0) scale(1); } - 100% { opacity: 0; transform: translateY(8px) scale(0.92); } + 0%, 38% { opacity: 0; transform: translateY(8px) scale(0.92); } + 46% { opacity: 1; transform: translateY(0) scale(1); } + 92% { opacity: 1; transform: translateY(0) scale(1); } + 100% { opacity: 0; transform: translateY(8px) scale(0.92); } } @keyframes fn-table-3-in { - 0%, 33% { opacity: 0; transform: translateY(8px) scale(0.92); } - 44% { opacity: 1; transform: translateY(0) scale(1); } - 88% { opacity: 1; transform: translateY(0) scale(1); } - 100% { opacity: 0; transform: translateY(8px) scale(0.92); } + 0%, 46% { opacity: 0; transform: translateY(8px) scale(0.92); } + 54% { opacity: 1; transform: translateY(0) scale(1); } + 92% { opacity: 1; transform: translateY(0) scale(1); } + 100% { opacity: 0; transform: translateY(8px) scale(0.92); } } @keyframes fn-link-1-draw { - 0%, 33% { stroke-dashoffset: 60; } - 48% { stroke-dashoffset: 0; } - 88% { stroke-dashoffset: 0; } - 100% { stroke-dashoffset: 60; } + 0%, 46% { stroke-dashoffset: 60; } + 56% { stroke-dashoffset: 0; } + 92% { stroke-dashoffset: 0; } + 100% { stroke-dashoffset: 60; } } @keyframes fn-link-2-draw { - 0%, 44% { stroke-dashoffset: 60; } - 56% { stroke-dashoffset: 0; } - 88% { stroke-dashoffset: 0; } - 100% { stroke-dashoffset: 60; } + 0%, 54% { stroke-dashoffset: 60; } + 62% { stroke-dashoffset: 0; } + 92% { stroke-dashoffset: 0; } + 100% { stroke-dashoffset: 60; } } @keyframes fn-sparkle { - 0%, 60% { opacity: 0; transform: scale(0.4); } - 68% { opacity: 1; transform: scale(1.2); } - 78% { opacity: 1; transform: scale(1); } - 86% { opacity: 0; transform: scale(0.4); } - 100% { opacity: 0; transform: scale(0.4); } -} - -@keyframes fn-cursor { - 0% { opacity: 0; transform: translate(80px, 32px); } - 5% { opacity: 1; transform: translate(80px, 32px); } - 18% { opacity: 1; transform: translate(110px, 28px); } - 24% { opacity: 0; transform: translate(110px, 28px); } - 100% { opacity: 0; transform: translate(110px, 28px); } + 0%, 60% { opacity: 0; transform: scale(0.4) rotate(0deg); } + 68% { opacity: 1; transform: scale(1.2) rotate(15deg); } + 74% { opacity: 1; transform: scale(1) rotate(0deg); } + 84% { opacity: 0; transform: scale(0.4) rotate(-15deg); } + 100% { opacity: 0; transform: scale(0.4); } } @media (prefers-reduced-motion: reduce) { - .fn-prompt, + .fn-connection-card, + .fn-pulse, + .fn-pointer, .fn-table, .fn-link, - .fn-sparkle, - .fn-cursor { + .fn-sparkle { animation: none !important; opacity: 1 !important; transform: none !important; @@ -189,7 +194,7 @@ @media (prefers-color-scheme: dark) { .fn-bg { fill: #2a2540; } - .fn-table rect:first-child { fill: #1f1e2c; stroke: #3a3b58; } - .fn-prompt rect:first-child { fill: #1f1e2c; stroke: #3a3b58; } + .fn-table rect:first-child, + .fn-connection-card rect:first-child { fill: #1f1e2c; stroke: #3a3b58; } .fn-table text { fill: #d8dcff; } } diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.html b/frontend/src/app/components/feature-notification/feature-notification.component.html index a423fd23d..73d0a05d5 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.html +++ b/frontend/src/app/components/feature-notification/feature-notification.component.html @@ -15,68 +15,91 @@ - - - - - - - - + + + - - - - - users - id - email - name - + + + + + + + + my_database + PostgreSQL - - - - - posts - id - user_id - title + + + + + + + + + + + + + + + + + + + - - - - - comments - id - post_id + + + - - - + + + + + + + users + id + email + name + - - - - - - - - - - + + + + + posts + id + user_id + title + - - - + + + + + comments + id + post_id + + + + + + + + + +
-

New: Edit Schema with AI

+

New: Create & Edit Schema with AI

- Describe the tables you need and let AI generate the schema for you. + Create and edit your database tables — just describe what you need and AI does the rest.

From db842ef7b6382e50a87ae81807cf2270c4eb2b9c Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Wed, 20 May 2026 13:45:35 +0300 Subject: [PATCH 3/6] chore(feature-notification): show Edit schema as text button in card MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the popup animation to match the new connections-list design: the slim connection card keeps its db icon + name on the left, and the top-right corner (where the schema icon button used to sit) now shows a pseudo-button "✨ Edit schema →" with pulse rings, a fade-in trailing arrow, and the cursor traveling to and clicking it. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../feature-notification.component.css | 30 ++++++++++++++----- .../feature-notification.component.html | 29 +++++++----------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.css b/frontend/src/app/components/feature-notification/feature-notification.component.css index 8910b78c6..09e0ff0f6 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.css +++ b/frontend/src/app/components/feature-notification/feature-notification.component.css @@ -74,7 +74,7 @@ } .fn-pulse { - transform-origin: 322px 30px; + transform-origin: 284px 30px; opacity: 0; } @@ -86,6 +86,13 @@ animation: fn-pointer 5.2s ease-in-out infinite; } +.fn-edit-schema-arrow { + opacity: 0; + transform: translateX(-3px); + transition: opacity 150ms ease, transform 150ms ease; + animation: fn-arrow-in 5.2s ease-in-out infinite; +} + .fn-table { opacity: 0; transform-origin: center; @@ -120,12 +127,20 @@ @keyframes fn-pointer { 0%, 8% { opacity: 0; transform: translate(80px, 90px); } 14% { opacity: 1; transform: translate(80px, 90px); } - 25% { opacity: 1; transform: translate(312px, 26px); } - 34% { opacity: 1; transform: translate(312px, 26px) scale(0.85); } - 38% { opacity: 1; transform: translate(312px, 26px) scale(1); } - 60% { opacity: 1; transform: translate(312px, 26px); } - 92% { opacity: 0; transform: translate(312px, 26px); } - 100% { opacity: 0; transform: translate(312px, 26px); } + 25% { opacity: 1; transform: translate(280px, 28px); } + 34% { opacity: 1; transform: translate(280px, 28px) scale(0.85); } + 38% { opacity: 1; transform: translate(280px, 28px) scale(1); } + 60% { opacity: 1; transform: translate(280px, 28px); } + 92% { opacity: 0; transform: translate(280px, 28px); } + 100% { opacity: 0; transform: translate(280px, 28px); } +} + +@keyframes fn-arrow-in { + 0%, 20% { opacity: 0; transform: translateX(-3px); } + 30% { opacity: 1; transform: translateX(0); } + 60% { opacity: 1; transform: translateX(0); } + 72% { opacity: 0; transform: translateX(-3px); } + 100% { opacity: 0; transform: translateX(-3px); } } @keyframes fn-pulse-ring { @@ -182,6 +197,7 @@ .fn-connection-card, .fn-pulse, .fn-pointer, + .fn-edit-schema-arrow, .fn-table, .fn-link, .fn-sparkle { diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.html b/frontend/src/app/components/feature-notification/feature-notification.component.html index 73d0a05d5..048d7725c 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.html +++ b/frontend/src/app/components/feature-notification/feature-notification.component.html @@ -15,7 +15,7 @@ - + @@ -29,24 +29,17 @@ my_database PostgreSQL - + - - - - - - - - - - - - - - - - + + + + + + + Edit schema + + From cd974fc4577783a73c1b26c8e5a289441b23ad48 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Wed, 20 May 2026 15:50:02 +0300 Subject: [PATCH 4/6] feat(feature-notification): three-act animation telling the AI schema story MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the single-card animation with a three-act 10s sequence: Act 1 — empty area + a centered black "+ Create" pill, cursor moves in, ripple-clicks it. Act 2 — a chat surface fades in titled "New database"; the prompt "Bookings for climbing gym" types out character-by-character with a blinking caret, the send button pulses, and the title swaps to "Bookings for climbing gym" when typing finishes. Act 3 — the AI sparkle avatar lifts above the canvas, three larger tables (members / bookings / sessions, lavender / blue / pink) stagger in with right-aligned PK and FK badges, two curved relationship lines draw via stroke-dashoffset, and a green "Schema ready ✓" lands at the bottom. Refreshes the popup copy to "Design schemas with AI" and rewrites the description around the new narrative. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../feature-notification.component.css | 346 ++++++++++++------ .../feature-notification.component.html | 138 ++++--- 2 files changed, 318 insertions(+), 166 deletions(-) diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.css b/frontend/src/app/components/feature-notification/feature-notification.component.css index 09e0ff0f6..60b5ca971 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.css +++ b/frontend/src/app/components/feature-notification/feature-notification.component.css @@ -56,161 +56,289 @@ } /* ──────────────────────────────────────────── */ -/* Single-scene animation */ -/* Cycle: 5.2s */ -/* 0–0.4s: card slides in */ -/* 0.4–1.3s: cursor approaches schema icon */ -/* 1.0–1.5s: pulse rings on icon */ -/* 1.5–2.7s: tables fade in + links draw */ -/* 2.7–3.0s: sparkle */ -/* 3.0–4.8s: hold */ -/* 4.8–5.2s: fade out → loop */ +/* Three-act animation */ +/* Cycle: 7s */ +/* Act 1 (0–1.5s / 0–21%): empty + Add db */ +/* Act 2 (1.5–3.5s / 21–50%): chat + typing */ +/* Act 3 (3.5–5.5s / 50–79%): AI + diagram */ +/* Hold (5.5–7s / 79–100%): linger & loop */ /* ──────────────────────────────────────────── */ -.fn-connection-card { +.fn-placeholder, +.fn-add-button, +.fn-pointer-1, +.fn-click-ripple, +.fn-chat-title, +.fn-chat-divider, +.fn-input, +.fn-caret, +.fn-send-button, +.fn-typing-clip, +.fn-user-message, +.fn-ai-avatar, +.fn-table, +.fn-link, +.fn-schema-ready { opacity: 0; - transform: translateY(-6px); - animation: fn-card-in 5.2s ease-out infinite; } -.fn-pulse { - transform-origin: 284px 30px; - opacity: 0; +/* === ACT 1 elements === */ + +.fn-placeholder, +.fn-act--1 > circle, +.fn-act--1 > path, +.fn-act--1 > text { + animation: fn-fade-act1 10s ease-in-out infinite; } -.fn-pulse--1 { animation: fn-pulse-ring 5.2s ease-out infinite; } -.fn-pulse--2 { animation: fn-pulse-ring 5.2s ease-out infinite -0.45s; } +.fn-add-button { + animation: fn-add-btn 10s ease-in-out infinite; + transform-origin: 180px 93px; +} -.fn-pointer { - opacity: 0; - animation: fn-pointer 5.2s ease-in-out infinite; +.fn-pointer-1 { + animation: fn-pointer-1 10s ease-in-out infinite; } -.fn-edit-schema-arrow { - opacity: 0; - transform: translateX(-3px); - transition: opacity 150ms ease, transform 150ms ease; - animation: fn-arrow-in 5.2s ease-in-out infinite; +.fn-click-ripple { + animation: fn-click-ripple 10s ease-in-out infinite; } -.fn-table { - opacity: 0; - transform-origin: center; - transform: translateY(8px) scale(0.92); +@keyframes fn-fade-act1 { + 0% { opacity: 0; } + 3% { opacity: 1; } + 19% { opacity: 1; } + 22% { opacity: 0; } + 100% { opacity: 0; } } -.fn-table--1 { animation: fn-table-1-in 5.2s ease-out infinite; } -.fn-table--2 { animation: fn-table-2-in 5.2s ease-out infinite; } -.fn-table--3 { animation: fn-table-3-in 5.2s ease-out infinite; } +@keyframes fn-add-btn { + 0% { opacity: 0; transform: scale(0.95); } + 3% { opacity: 1; transform: scale(1); } + 17% { opacity: 1; transform: scale(1); } + 18% { opacity: 1; transform: scale(0.93); } + 19% { opacity: 1; transform: scale(1); } + 22% { opacity: 0; transform: scale(1); } + 100% { opacity: 0; } +} -.fn-link { - stroke-dasharray: 60; - stroke-dashoffset: 60; +@keyframes fn-pointer-1 { + 0%, 3% { opacity: 0; transform: translate(60px, 40px); } + 5% { opacity: 1; transform: translate(60px, 40px); } + 14% { opacity: 1; transform: translate(178px, 86px); } + 17% { opacity: 1; transform: translate(178px, 86px) scale(0.85); } + 19% { opacity: 1; transform: translate(178px, 86px) scale(1); } + 22% { opacity: 0; transform: translate(178px, 86px); } + 100% { opacity: 0; } } -.fn-link--1 { animation: fn-link-1-draw 5.2s ease-in-out infinite; } -.fn-link--2 { animation: fn-link-2-draw 5.2s ease-in-out infinite; } +@keyframes fn-click-ripple { + 0%, 17% { opacity: 0; r: 0; } + 18% { opacity: 0.85; r: 6; } + 21% { opacity: 0; r: 34; } + 100% { opacity: 0; r: 0; } +} -.fn-sparkle { - transform-origin: 120px 70px; - opacity: 0; - animation: fn-sparkle 5.2s ease-in-out infinite; +/* === ACT 2 elements === */ + +.fn-chat-divider { + animation: fn-chat-frame 10s ease-in-out infinite; +} + +.fn-chat-title--initial { + animation: fn-chat-title-initial 10s ease-in-out infinite; } -@keyframes fn-card-in { - 0% { opacity: 0; transform: translateY(-6px); } - 8% { opacity: 1; transform: translateY(0); } - 92% { opacity: 1; transform: translateY(0); } - 100% { opacity: 0; transform: translateY(-6px); } +.fn-chat-title--final { + animation: fn-chat-title-final 10s ease-in-out infinite; } -@keyframes fn-pointer { - 0%, 8% { opacity: 0; transform: translate(80px, 90px); } - 14% { opacity: 1; transform: translate(80px, 90px); } - 25% { opacity: 1; transform: translate(280px, 28px); } - 34% { opacity: 1; transform: translate(280px, 28px) scale(0.85); } - 38% { opacity: 1; transform: translate(280px, 28px) scale(1); } - 60% { opacity: 1; transform: translate(280px, 28px); } - 92% { opacity: 0; transform: translate(280px, 28px); } - 100% { opacity: 0; transform: translate(280px, 28px); } +@keyframes fn-chat-title-initial { + 0%, 21% { opacity: 0; } + 24% { opacity: 1; } + 42% { opacity: 1; } + 45% { opacity: 0; } + 100% { opacity: 0; } } -@keyframes fn-arrow-in { - 0%, 20% { opacity: 0; transform: translateX(-3px); } - 30% { opacity: 1; transform: translateX(0); } - 60% { opacity: 1; transform: translateX(0); } - 72% { opacity: 0; transform: translateX(-3px); } - 100% { opacity: 0; transform: translateX(-3px); } +@keyframes fn-chat-title-final { + 0%, 42% { opacity: 0; } + 45% { opacity: 1; } + 93% { opacity: 1; } + 100% { opacity: 0; } } -@keyframes fn-pulse-ring { - 0%, 18% { opacity: 0; transform: scale(0.7); } - 24% { opacity: 0.8; transform: scale(0.9); } - 38% { opacity: 0; transform: scale(1.8); } - 100% { opacity: 0; transform: scale(1.8); } +.fn-input { + animation: fn-input-in 10s ease-in-out infinite; } -@keyframes fn-table-1-in { - 0%, 30% { opacity: 0; transform: translateY(8px) scale(0.92); } - 38% { opacity: 1; transform: translateY(0) scale(1); } - 92% { opacity: 1; transform: translateY(0) scale(1); } - 100% { opacity: 0; transform: translateY(8px) scale(0.92); } +.fn-typing-clip { + animation: fn-typing 10s linear infinite; } -@keyframes fn-table-2-in { - 0%, 38% { opacity: 0; transform: translateY(8px) scale(0.92); } - 46% { opacity: 1; transform: translateY(0) scale(1); } - 92% { opacity: 1; transform: translateY(0) scale(1); } - 100% { opacity: 0; transform: translateY(8px) scale(0.92); } +.fn-caret { + animation: fn-caret-track 10s linear infinite; } -@keyframes fn-table-3-in { - 0%, 46% { opacity: 0; transform: translateY(8px) scale(0.92); } - 54% { opacity: 1; transform: translateY(0) scale(1); } - 92% { opacity: 1; transform: translateY(0) scale(1); } - 100% { opacity: 0; transform: translateY(8px) scale(0.92); } +.fn-send-button { + animation: fn-send-button 10s ease-in-out infinite; + transform-origin: 326px 151px; } -@keyframes fn-link-1-draw { - 0%, 46% { stroke-dashoffset: 60; } - 56% { stroke-dashoffset: 0; } - 92% { stroke-dashoffset: 0; } - 100% { stroke-dashoffset: 60; } +.fn-user-message { + animation: fn-user-message 10s ease-in-out infinite; } -@keyframes fn-link-2-draw { - 0%, 54% { stroke-dashoffset: 60; } - 62% { stroke-dashoffset: 0; } - 92% { stroke-dashoffset: 0; } - 100% { stroke-dashoffset: 60; } +@keyframes fn-chat-frame { + 0%, 21% { opacity: 0; } + 24% { opacity: 1; } + 93% { opacity: 1; } + 100% { opacity: 0; } } -@keyframes fn-sparkle { - 0%, 60% { opacity: 0; transform: scale(0.4) rotate(0deg); } - 68% { opacity: 1; transform: scale(1.2) rotate(15deg); } - 74% { opacity: 1; transform: scale(1) rotate(0deg); } - 84% { opacity: 0; transform: scale(0.4) rotate(-15deg); } - 100% { opacity: 0; transform: scale(0.4); } +@keyframes fn-input-in { + 0%, 21% { opacity: 0; transform: translateY(8px); } + 24% { opacity: 1; transform: translateY(0); } + 48% { opacity: 1; transform: translateY(0); } + 50% { opacity: 0; transform: translateY(0); } + 100% { opacity: 0; } +} + +@keyframes fn-typing { + 0%, 25% { width: 0; } + 40% { width: 180px; } + 48% { width: 180px; } + 50% { width: 0; } + 100% { width: 0; } +} + +@keyframes fn-caret-track { + 0%, 25% { opacity: 0; transform: translateX(0); } + 27% { opacity: 1; transform: translateX(0); } + 40% { opacity: 1; transform: translateX(180px); } + 42% { opacity: 0; transform: translateX(180px); } + 44% { opacity: 1; transform: translateX(180px); } + 46% { opacity: 0; transform: translateX(180px); } + 48% { opacity: 1; transform: translateX(180px); } + 50% { opacity: 0; transform: translateX(180px); } + 100% { opacity: 0; } +} + +@keyframes fn-send-button { + 0%, 40% { opacity: 0; transform: scale(0.9); } + 43% { opacity: 1; transform: scale(0.9); } + 44% { opacity: 1; transform: scale(1.15); } + 46% { opacity: 1; transform: scale(1); } + 47% { opacity: 1; transform: scale(0.9); } + 48% { opacity: 1; transform: scale(1); } + 50% { opacity: 0; transform: scale(1); } + 100% { opacity: 0; } +} + +@keyframes fn-user-message { + 0%, 47% { opacity: 0; transform: translateY(80px) scale(0.95); } + 50% { opacity: 1; transform: translateY(0) scale(1); } + 93% { opacity: 1; transform: translateY(0) scale(1); } + 100% { opacity: 0; } +} + +/* === ACT 3 elements === */ + +.fn-ai-avatar { + animation: fn-ai-avatar 10s ease-in-out infinite; + transform-origin: 22px 50px; +} + +.fn-table--1 { animation: fn-table-1 10s ease-out infinite; transform-origin: 69px 107px; } +.fn-table--2 { animation: fn-table-2 10s ease-out infinite; transform-origin: 181px 91px; } +.fn-table--3 { animation: fn-table-3 10s ease-out infinite; transform-origin: 293px 109px; } + +.fn-link { + stroke-dasharray: 50; + stroke-dashoffset: 50; +} + +.fn-link--1 { animation: fn-link-1 10s ease-in-out infinite; } +.fn-link--2 { animation: fn-link-2 10s ease-in-out infinite; } + +.fn-schema-ready { animation: fn-schema-ready 10s ease-in-out infinite; } + +@keyframes fn-ai-avatar { + 0%, 53% { opacity: 0; transform: scale(0.7); } + 56% { opacity: 1; transform: scale(1.1); } + 58% { opacity: 1; transform: scale(1); } + 93% { opacity: 1; transform: scale(1); } + 100% { opacity: 0; } +} + +@keyframes fn-table-1 { + 0%, 56% { opacity: 0; transform: translateY(8px) scale(0.92); } + 60% { opacity: 1; transform: translateY(0) scale(1); } + 93% { opacity: 1; transform: translateY(0) scale(1); } + 100% { opacity: 0; } +} + +@keyframes fn-table-2 { + 0%, 60% { opacity: 0; transform: translateY(8px) scale(0.92); } + 64% { opacity: 1; transform: translateY(0) scale(1); } + 93% { opacity: 1; transform: translateY(0) scale(1); } + 100% { opacity: 0; } +} + +@keyframes fn-table-3 { + 0%, 64% { opacity: 0; transform: translateY(8px) scale(0.92); } + 68% { opacity: 1; transform: translateY(0) scale(1); } + 93% { opacity: 1; transform: translateY(0) scale(1); } + 100% { opacity: 0; } +} + +@keyframes fn-link-1 { + 0%, 68% { stroke-dashoffset: 50; opacity: 0; } + 70% { opacity: 1; } + 74% { stroke-dashoffset: 0; opacity: 1; } + 93% { stroke-dashoffset: 0; opacity: 1; } + 100% { stroke-dashoffset: 50; opacity: 0; } +} + +@keyframes fn-link-2 { + 0%, 72% { stroke-dashoffset: 50; opacity: 0; } + 74% { opacity: 1; } + 78% { stroke-dashoffset: 0; opacity: 1; } + 93% { stroke-dashoffset: 0; opacity: 1; } + 100% { stroke-dashoffset: 50; opacity: 0; } +} + +@keyframes fn-schema-ready { + 0%, 76% { opacity: 0; transform: translateY(4px); } + 80% { opacity: 1; transform: translateY(0); } + 93% { opacity: 1; transform: translateY(0); } + 100% { opacity: 0; } } @media (prefers-reduced-motion: reduce) { - .fn-connection-card, - .fn-pulse, - .fn-pointer, - .fn-edit-schema-arrow, + .fn-placeholder, + .fn-add-button, + .fn-pointer-1, + .fn-click-ripple, + .fn-chat-title--final, + .fn-chat-divider, + .fn-input, + .fn-caret, + .fn-send-button, + .fn-user-message, + .fn-ai-avatar, .fn-table, .fn-link, - .fn-sparkle { + .fn-schema-ready { animation: none !important; opacity: 1 !important; transform: none !important; stroke-dashoffset: 0 !important; } -} - -@media (prefers-color-scheme: dark) { - .fn-bg { fill: #2a2540; } - .fn-table rect:first-child, - .fn-connection-card rect:first-child { fill: #1f1e2c; stroke: #3a3b58; } - .fn-table text { fill: #d8dcff; } + .fn-typing-clip { + animation: none !important; + width: 180px !important; + } + .fn-chat-title--initial { display: none !important; } + .fn-act--1 { display: none !important; } } diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.html b/frontend/src/app/components/feature-notification/feature-notification.component.html index 048d7725c..2bc6d992d 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.html +++ b/frontend/src/app/components/feature-notification/feature-notification.component.html @@ -10,89 +10,113 @@ + + + - + - - - - - - - - - + + + + + + + + + Create + - - my_database - PostgreSQL + + - - - - - - - - - Edit schema - - + + + - - - + + + + + + New database + Bookings for climbing gym + + + + + + + Bookings for climbing gym + + + + + + + + + - - - + + + + + + + + + + + - - - users - id - email - name + + + members + id + PK + name + email - - - - posts - id - user_id - title + + + bookings + id + PK + member_id + FK + date - - - - comments - id - post_id + + + sessions + id + PK + starts_at - - - + + + - - - + + + Schema ready ✓
-

New: Create & Edit Schema with AI

+

Design schemas with AI

- Create and edit your database tables — just describe what you need and AI does the rest. + Describe what you need and AI will generate the tables for you.

From 8cc536145b09bf400f08a4913bae298bebcbd325 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Wed, 20 May 2026 15:55:26 +0300 Subject: [PATCH 5/6] chore(feature-notification): bigger typed prompt + clearer description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enlarge the typed prompt to 16px so "Bookings for climbing gym" stretches almost across the full input row and the caret follows it; resize the typing clip and caret-track keyframes to match. Reword the popup description to spell out the dual use: "Generate a new database from scratch or edit your existing one — just describe what you need." Co-Authored-By: Claude Opus 4.7 (1M context) --- .../feature-notification.component.css | 18 +++++++++--------- .../feature-notification.component.html | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.css b/frontend/src/app/components/feature-notification/feature-notification.component.css index 60b5ca971..fbe45a699 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.css +++ b/frontend/src/app/components/feature-notification/feature-notification.component.css @@ -206,8 +206,8 @@ @keyframes fn-typing { 0%, 25% { width: 0; } - 40% { width: 180px; } - 48% { width: 180px; } + 40% { width: 270px; } + 48% { width: 270px; } 50% { width: 0; } 100% { width: 0; } } @@ -215,12 +215,12 @@ @keyframes fn-caret-track { 0%, 25% { opacity: 0; transform: translateX(0); } 27% { opacity: 1; transform: translateX(0); } - 40% { opacity: 1; transform: translateX(180px); } - 42% { opacity: 0; transform: translateX(180px); } - 44% { opacity: 1; transform: translateX(180px); } - 46% { opacity: 0; transform: translateX(180px); } - 48% { opacity: 1; transform: translateX(180px); } - 50% { opacity: 0; transform: translateX(180px); } + 40% { opacity: 1; transform: translateX(255px); } + 42% { opacity: 0; transform: translateX(255px); } + 44% { opacity: 1; transform: translateX(255px); } + 46% { opacity: 0; transform: translateX(255px); } + 48% { opacity: 1; transform: translateX(255px); } + 50% { opacity: 0; transform: translateX(255px); } 100% { opacity: 0; } } @@ -337,7 +337,7 @@ } .fn-typing-clip { animation: none !important; - width: 180px !important; + width: 270px !important; } .fn-chat-title--initial { display: none !important; } .fn-act--1 { display: none !important; } diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.html b/frontend/src/app/components/feature-notification/feature-notification.component.html index 2bc6d992d..3084dcc15 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.html +++ b/frontend/src/app/components/feature-notification/feature-notification.component.html @@ -11,7 +11,7 @@ - + @@ -50,9 +50,9 @@ - Bookings for climbing gym + Bookings for climbing gym - + @@ -116,7 +116,7 @@

Design schemas with AI

- Describe what you need and AI will generate the tables for you. + Generate a new database from scratch or edit your existing one — just describe what you need.

From 2b0fe09f3698dd8770f1125f2b0e0e6b73906cdc Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Wed, 20 May 2026 16:16:55 +0300 Subject: [PATCH 6/6] chore(feature-notification): full-width popup on mobile On screens up to 640px the popup hugs the bottom of the viewport with 12px gutters, becomes auto-width (capped at 480px for tablets), gains a 14px corner radius and a softer layered shadow so it reads as a proper toast card instead of a tiny corner widget. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../feature-notification.component.css | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.css b/frontend/src/app/components/feature-notification/feature-notification.component.css index fbe45a699..a13ff72c3 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.css +++ b/frontend/src/app/components/feature-notification/feature-notification.component.css @@ -18,6 +18,20 @@ } } +@media (max-width: 640px) { + .feature-notification { + left: 12px; + right: 12px; + bottom: 12px; + width: auto; + max-width: 480px; + margin: 0 auto; + padding: 14px; + border-radius: 14px; + box-shadow: 0 12px 28px rgba(0, 0, 0, 0.2), 0 4px 8px rgba(0, 0, 0, 0.12); + } +} + .feature-notification__animation { width: 100%; border-radius: 8px;