Skip to content

Commit 2938845

Browse files
committed
Refactor guided tours into composable infrastructure under src/lib/tours
1 parent e49e67a commit 2938845

30 files changed

Lines changed: 2240 additions & 1160 deletions

src/app.css

Lines changed: 0 additions & 229 deletions
Original file line numberDiff line numberDiff line change
@@ -718,232 +718,3 @@ body.assembly-pending .svelte-flow__edge {
718718
}
719719
}
720720

721-
/* --- Guided tour (driver.js) — pathview theme ------------------------- */
722-
723-
/* Theme-aware overlay: light scrim on dark, dark scrim on light. Driver.js
724-
* sets fill via inline attribute on the inner path, so we override with
725-
* !important. Vars live on :root so toggling theme updates immediately. */
726-
:root[data-theme='dark'],
727-
:root:not([data-theme]) {
728-
--tour-overlay-color: rgba(255, 255, 255, 0.22);
729-
}
730-
731-
:root[data-theme='light'] {
732-
--tour-overlay-color: rgba(0, 0, 0, 0.55);
733-
}
734-
735-
.driver-overlay,
736-
.driver-overlay path {
737-
fill: var(--tour-overlay-color) !important;
738-
}
739-
740-
.driver-popover {
741-
background-color: var(--surface) !important;
742-
color: var(--text) !important;
743-
border: 1px solid var(--border) !important;
744-
border-radius: var(--radius-lg) !important;
745-
box-shadow: var(--shadow-lg) !important;
746-
max-width: 420px !important;
747-
min-width: 320px !important;
748-
padding: 0 !important;
749-
overflow: hidden !important;
750-
}
751-
752-
.driver-popover,
753-
.driver-popover * {
754-
font-family: var(--font-ui) !important;
755-
}
756-
757-
.driver-popover-title {
758-
display: flex !important;
759-
align-items: center !important;
760-
min-height: var(--header-height) !important;
761-
margin: 0 !important;
762-
padding: 0 var(--space-md) !important;
763-
background: var(--surface-raised) !important;
764-
border-bottom: 1px solid var(--border) !important;
765-
border-radius: var(--radius-lg) var(--radius-lg) 0 0 !important;
766-
font-size: var(--font-base) !important;
767-
font-weight: 500 !important;
768-
line-height: 1.3 !important;
769-
color: var(--text-muted) !important;
770-
text-transform: uppercase !important;
771-
letter-spacing: 0.5px !important;
772-
}
773-
774-
.driver-popover-description {
775-
font-size: 12px !important;
776-
color: var(--text-muted) !important;
777-
line-height: 1.55 !important;
778-
margin: 0 !important;
779-
padding: 14px 16px 0 !important;
780-
}
781-
782-
.driver-popover-description p {
783-
margin: 0 0 8px !important;
784-
}
785-
786-
.driver-popover-description p:last-child {
787-
margin-bottom: 0 !important;
788-
}
789-
790-
.driver-popover-description ul {
791-
margin: 6px 0 10px !important;
792-
padding-left: 18px !important;
793-
}
794-
795-
.driver-popover-description li {
796-
margin: 2px 0 !important;
797-
}
798-
799-
.driver-popover-description table {
800-
width: 100% !important;
801-
border-collapse: collapse !important;
802-
margin: 6px 0 !important;
803-
font-size: 11px !important;
804-
}
805-
806-
.driver-popover-description table td {
807-
padding: 4px 6px !important;
808-
border-bottom: 1px solid var(--border) !important;
809-
vertical-align: middle !important;
810-
}
811-
812-
.driver-popover-description table tr:last-child td {
813-
border-bottom: none !important;
814-
}
815-
816-
.driver-popover-description table td:last-child {
817-
text-align: right !important;
818-
white-space: nowrap !important;
819-
}
820-
821-
.driver-popover-description kbd {
822-
display: inline-block;
823-
padding: 2px 5px;
824-
font-family: inherit !important;
825-
font-size: 10px;
826-
background: var(--surface-raised);
827-
border: 1px solid var(--border);
828-
border-radius: 3px;
829-
color: var(--text-muted);
830-
min-width: 16px;
831-
text-align: center;
832-
}
833-
834-
.driver-popover-description code {
835-
font-family: var(--font-mono) !important;
836-
color: var(--accent);
837-
background: transparent;
838-
padding: 0;
839-
}
840-
841-
.driver-popover-progress-text {
842-
font-size: 10px !important;
843-
color: var(--text-disabled) !important;
844-
}
845-
846-
.driver-popover-footer {
847-
margin-top: 0 !important;
848-
padding: 12px 16px 14px !important;
849-
}
850-
851-
/* Footer buttons — match the modal action-button styles (with border). */
852-
.driver-popover-footer button {
853-
display: inline-flex !important;
854-
align-items: center !important;
855-
justify-content: center !important;
856-
gap: var(--space-sm) !important;
857-
cursor: pointer !important;
858-
border: 1px solid var(--border) !important;
859-
background: var(--surface-raised) !important;
860-
color: var(--text) !important;
861-
padding: var(--space-sm) var(--space-md) !important;
862-
border-radius: var(--radius-md) !important;
863-
font-size: 11px !important;
864-
font-weight: 500 !important;
865-
text-shadow: none !important;
866-
transition: all var(--transition-fast) !important;
867-
}
868-
869-
.driver-popover-footer button:hover {
870-
background: var(--surface-hover) !important;
871-
border-color: var(--border-focus) !important;
872-
}
873-
874-
.driver-popover-footer button:active {
875-
transform: scale(0.98) !important;
876-
}
877-
878-
.driver-popover-footer button:focus {
879-
outline: none !important;
880-
}
881-
882-
/* Back button as ghost (secondary action). */
883-
.driver-popover-prev-btn {
884-
background: transparent !important;
885-
color: var(--text-muted) !important;
886-
}
887-
888-
.driver-popover-prev-btn:hover {
889-
background: var(--surface-hover) !important;
890-
color: var(--text) !important;
891-
border-color: var(--border-focus) !important;
892-
}
893-
894-
895-
/* Close button: matches the .icon-btn pattern used in modal headers.
896-
* The default "×" character is hidden and replaced by a Lucide X icon
897-
* via a pseudo-element mask so theme colours apply on hover. */
898-
.driver-popover-close-btn {
899-
position: absolute !important;
900-
top: 6px !important;
901-
right: 6px !important;
902-
width: 28px !important;
903-
height: 28px !important;
904-
border: none !important;
905-
border-radius: var(--radius-md) !important;
906-
background: transparent !important;
907-
color: transparent !important;
908-
font-size: 0 !important;
909-
cursor: pointer !important;
910-
text-shadow: none !important;
911-
transition: background var(--transition-fast) !important;
912-
z-index: 2 !important;
913-
}
914-
915-
.driver-popover-close-btn::before {
916-
content: '' !important;
917-
position: absolute !important;
918-
inset: 0 !important;
919-
background: var(--text-muted) !important;
920-
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3C/svg%3E") center / 16px no-repeat;
921-
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3C/svg%3E") center / 16px no-repeat;
922-
transition: background var(--transition-fast) !important;
923-
}
924-
925-
.driver-popover-close-btn:hover {
926-
background: var(--surface-hover) !important;
927-
}
928-
929-
.driver-popover-close-btn:hover::before {
930-
background: var(--text) !important;
931-
}
932-
933-
.driver-popover-close-btn:focus {
934-
outline: none !important;
935-
}
936-
937-
/* Arrow tip color matches popover background; only the visible side counts. */
938-
.driver-popover-arrow-side-left {
939-
border-left-color: var(--surface) !important;
940-
}
941-
.driver-popover-arrow-side-right {
942-
border-right-color: var(--surface) !important;
943-
}
944-
.driver-popover-arrow-side-top {
945-
border-top-color: var(--surface) !important;
946-
}
947-
.driver-popover-arrow-side-bottom {
948-
border-bottom-color: var(--surface) !important;
949-
}

src/lib/components/FlowCanvas.svelte

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import '@xyflow/svelte/dist/style.css';
1515
1616
import { isInputFocused } from '$lib/utils/focus';
17+
import { isTourActive } from '$lib/tours/inputMode';
1718
import BaseNode from './nodes/BaseNode.svelte';
1819
import EventNode from './nodes/EventNode.svelte';
1920
import AnnotationNode from './nodes/AnnotationNode.svelte';
@@ -31,6 +32,7 @@
3132
import { dropTargetBridge } from '$lib/stores/dropTargetBridge';
3233
import { contextMenuStore } from '$lib/stores/contextMenu';
3334
import { nodeUpdatesStore } from '$lib/stores/nodeUpdates';
35+
import { assemblyAnimationTrigger } from '$lib/animation/assemblyAnimation';
3436
import { nodeRegistry } from '$lib/nodes';
3537
import { NODE_TYPES } from '$lib/constants/nodeTypes';
3638
import { GRID_SIZE, SNAP_GRID, BACKGROUND_GAP } from '$lib/constants/grid';
@@ -74,6 +76,8 @@
7476
7577
// Keyboard shortcuts for node manipulation
7678
function handleKeydown(event: KeyboardEvent) {
79+
// While a guided tour runs, driver.js owns the keyboard.
80+
if (isTourActive()) return;
7781
if (isInputFocused(event)) return;
7882
7983
// Handle Delete key (SvelteFlow's deleteKeyCode doesn't work reliably for 'Delete')
@@ -444,6 +448,19 @@
444448
}
445449
});
446450
451+
// On model load, the initial routing pass runs with fallback 80×40
452+
// dimensions because nodes haven't been measured yet, and the per-node
453+
// $effect above silently records first measurements without rerouting.
454+
// Trigger a full reroute once measurements have settled.
455+
let lastAssemblyTrigger = 0;
456+
const unsubAssembly = assemblyAnimationTrigger.subscribe((v) => {
457+
if (v > lastAssemblyTrigger) {
458+
lastAssemblyTrigger = v;
459+
setTimeout(() => updateRoutingContext(), 400);
460+
}
461+
});
462+
onDestroy(() => unsubAssembly());
463+
447464
// Track if we're currently syncing to prevent loops
448465
let isSyncing = false;
449466

src/lib/components/ResizablePanel.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@
180180
class="resizable-panel glass-panel {position}"
181181
class:resizing={isResizing}
182182
data-panel={title ?? ''}
183+
data-tour={title ? `panel-${title.toLowerCase()}` : ''}
183184
style="
184185
{position === 'left' || position === 'right' ? `width: ${getWidth()}px;` : ''}
185186
{(position === 'bottom-left' || position === 'bottom-right') && controlledWidth !== undefined ? `width: ${getWidth()}px;` : ''}

src/lib/components/SubsystemBreadcrumb.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
</script>
2828

2929
{#if !isAtRoot}
30-
<nav class="breadcrumb glass-panel" transition:fly={{ y: -10, duration: 150 }}>
30+
<nav class="breadcrumb glass-panel" data-tour="breadcrumb" transition:fly={{ y: -10, duration: 150 }}>
3131
{#each breadcrumbs as crumb, i}
3232
<button
3333
class="crumb-pill"

src/lib/components/WelcomeModal.svelte

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { cubicOut } from 'svelte/easing';
66
import Icon from '$lib/components/icons/Icon.svelte';
77
import { PATHVIEW_VERSION, EXTRACTED_VERSIONS } from '$lib/constants/dependencies';
8-
import { startGuidedTour, type TourId } from '$lib/utils/guidedTour';
8+
import { startGuidedTour, type TourId } from '$lib/tours';
99
1010
interface Example {
1111
name: string;
@@ -145,18 +145,18 @@
145145
<span>Step-by-step walkthroughs that highlight the editor's main areas in turn so you know what each does.</span>
146146
</div>
147147
<div class="tour-buttons">
148-
<button class="action-card" onclick={() => handleStartTour('ui')}>
149-
<Icon name="arrow-right" size={20} />
148+
<button class="action-card" onclick={() => handleStartTour('start')}>
149+
<Icon name="compass" size={20} />
150150
<span class="action-label">Start</span>
151151
</button>
152+
<button class="action-card" onclick={() => handleStartTour('modeling')}>
153+
<Icon name="shapes" size={20} />
154+
<span class="action-label">Modeling</span>
155+
</button>
152156
<button class="action-card" onclick={() => handleStartTour('simulation')}>
153-
<Icon name="arrow-right" size={20} />
157+
<Icon name="play-circle" size={20} />
154158
<span class="action-label">Simulation</span>
155159
</button>
156-
<button class="action-card" onclick={() => handleStartTour('customization')}>
157-
<Icon name="arrow-right" size={20} />
158-
<span class="action-label">Modeling</span>
159-
</button>
160160
</div>
161161
</div>
162162

src/lib/components/dialogs/BlockPropertiesDialog.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@
249249

250250
{#if nodeId && node && typeDef}
251251
<div class="dialog-backdrop" onclick={handleBackdropClick} transition:fade={{ duration: 150 }} role="presentation">
252-
<div class="properties-dialog glass-panel" style="--node-color: {currentColor}" transition:scale={{ start: 0.95, duration: 150, easing: cubicOut }} role="dialog" tabindex="-1" aria-labelledby="dialog-title">
252+
<div class="properties-dialog glass-panel" data-tour="dialog-properties" style="--node-color: {currentColor}" transition:scale={{ start: 0.95, duration: 150, easing: cubicOut }} role="dialog" tabindex="-1" aria-labelledby="dialog-title">
253253
<div class="dialog-header">
254254
{#if showCode}
255255
<span id="dialog-title">Python Code</span>
@@ -258,6 +258,7 @@
258258
<input
259259
id="dialog-title"
260260
class="node-name-input"
261+
data-tour="block-name-input"
261262
type="text"
262263
value={node.name}
263264
oninput={(e) => handleNameChange(e.currentTarget.value)}
@@ -377,6 +378,7 @@
377378
<button
378379
class="pin-btn"
379380
class:pinned
381+
data-tour="block-pin-btn"
380382
onclick={() => togglePinParam(param.name)}
381383
use:tooltip={pinned ? "Unpin from node" : "Pin to node"}
382384
aria-label={pinned ? "Unpin from node" : "Pin to node"}

src/lib/components/dialogs/ExportDialog.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135

136136
{#if open}
137137
<div class="dialog-backdrop" transition:fade={{ duration: 150 }} onclick={handleBackdropClick} role="presentation">
138-
<div class="dialog glass-panel" transition:scale={{ start: 0.95, duration: 150, easing: cubicOut }} role="dialog" tabindex="-1" aria-labelledby="dialog-title">
138+
<div class="dialog glass-panel" data-tour="dialog-python-export" transition:scale={{ start: 0.95, duration: 150, easing: cubicOut }} role="dialog" tabindex="-1" aria-labelledby="dialog-title">
139139
<div class="dialog-header">
140140
<span id="dialog-title">Export Python Code</span>
141141
<div class="header-actions">

src/lib/components/dialogs/KeyboardShortcutsDialog.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_click_events_have_key_events -->
9696
<div
9797
class="dialog glass-panel"
98+
data-tour="dialog-shortcuts"
9899
transition:scale={{ start: 0.95, duration: 150, easing: cubicOut }}
99100
onclick={(e) => e.stopPropagation()}
100101
role="dialog"

src/lib/components/dialogs/SearchDialog.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_click_events_have_key_events -->
140140
<div
141141
class="search-dialog glass-panel"
142+
data-tour="dialog-search"
142143
transition:scale={{ start: 0.95, duration: 150, easing: cubicOut }}
143144
onclick={(e) => e.stopPropagation()}
144145
role="dialog"

src/lib/components/dialogs/ToolboxManagerDialog.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@
349349
<div class="dialog-backdrop" transition:fade={{ duration: 150 }} onclick={handleBackdrop} role="presentation">
350350
<div
351351
class="dialog glass-panel manager-modal"
352+
data-tour="dialog-toolbox-manager"
352353
transition:scale={{ start: 0.95, duration: 150, easing: cubicOut }}
353354
role="dialog"
354355
aria-modal="true"

0 commit comments

Comments
 (0)