Skip to content

feat: Add onboarding flow#199

Open
hhvrc wants to merge 7 commits into
developfrom
feat/onboarding-flow
Open

feat: Add onboarding flow#199
hhvrc wants to merge 7 commits into
developfrom
feat/onboarding-flow

Conversation

@hhvrc
Copy link
Copy Markdown
Contributor

@hhvrc hhvrc commented May 17, 2026

This pull request introduces a new interactive welcome tour for onboarding users.

It also adds the driver.js library for the tour, applies custom theming to match the app's dark UI, and ensures users with old-style hash URLs are redirected to the new route structure.

@hhvrc hhvrc self-assigned this May 17, 2026
Copilot AI review requested due to automatic review settings May 17, 2026 22:51
Comment thread src/lib/utils/legacy-hash-redirect.ts Fixed
…rect'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a first-run onboarding experience to the SvelteKit app, including an interactive welcome modal and an optional guided tour, while also introducing a client-side redirect for legacy hash-based URLs.

Changes:

  • Introduces WelcomeScreen.svelte and wires it into the root layout to display a multi-step onboarding modal.
  • Adds a driver.js-based guided tour (welcome-tour.ts) plus global CSS theming overrides for the popovers.
  • Implements legacy-hash-redirect.ts and hooks it into hooks.client.ts startup to translate old #/... URLs to the new route structure; adds expiring-flags for localStorage-backed one-time/expiry gating.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/routes/WelcomeScreen.svelte New multi-step welcome modal with optional “start tour” action gated by login state.
src/routes/+layout.svelte Mounts the welcome screen at the app root so it can display globally.
src/lib/utils/legacy-hash-redirect.ts Adds mapping + redirect logic for legacy hash routes.
src/lib/tour/welcome-tour.ts Implements the driver.js welcome tour flow and step definitions.
src/lib/state/expiring-flags.ts Adds expiring localStorage-backed flags used to gate onboarding/tour.
src/hooks.client.ts Runs legacy redirect and prunes expired flags during client init.
src/app.css Adds a dark-theme override for driver.js popovers/buttons.
package.json Adds driver.js dependency.
pnpm-lock.yaml Locks driver.js dependency version and integrity metadata.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +95 to +98
function handleKeydown(e: KeyboardEvent) {
if (!open) return;
if (e.key === 'Escape') dismiss();
else if (e.key === 'ArrowRight' || e.key === 'Enter') goNext();
Comment thread src/lib/state/expiring-flags.ts Outdated
Comment on lines +21 to +30
function read(): Store {
if (typeof localStorage === 'undefined') return {};
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return {};
try {
const parsed: unknown = JSON.parse(raw);
if (parsed && typeof parsed === 'object') return parsed as Store;
} catch {
// Corrupt blob; reset.
}
Comment on lines +72 to +77
export function redirectLegacyHashRoute(): void {
const hash = location.hash;
if (!hash || hash.charAt(1) !== '/') return;

const target = mapLegacyHashRoute(hash.slice(1));
if (!target) return;
showProgress: true,
allowClose: true,
progressText: '{{current}} of {{total}}',
nextBtnText: 'Skip',
Comment thread src/lib/state/expiring-flags.ts Outdated
Comment on lines +47 to +57
export const expiringFlags = {
/**
* Returns the stored value for `key`, or `null` if the entry is missing or
* has expired. Expired entries are NOT pruned here (see {@link clearExpired}).
*/
get<T = unknown>(key: string): T | null {
const entry = read()[key];
if (!entry) return null;
if (Date.now() >= entry.e) return null;
return entry.v as T;
},
Comment on lines +47 to +66
/**
* Maps a legacy WebUI hash route (without the leading `#`) to its new path,
* or returns `null` if the path doesn't look like a legacy route.
*/
export function mapLegacyHashRoute(legacyPath: string): string | null {
if (!legacyPath.startsWith('/')) return null;

const qIdx = legacyPath.indexOf('?');
const path = qIdx === -1 ? legacyPath : legacyPath.slice(0, qIdx);
const search = qIdx === -1 ? '' : legacyPath.slice(qIdx);

if (Object.prototype.hasOwnProperty.call(EXACT, path)) return EXACT[path] + search;

for (const { re, to } of PATTERNS) {
if (re.test(path)) return path.replace(re, to) + search;
}

// Unknown legacy route → home so the user isn't dumped on a 404.
return '/home' + search;
}
// Defense-in-depth: only allow same-origin, root-relative redirects.
// Reject protocol-relative (`//...`) and scheme-prefixed (`http:...`, `javascript:...`) values.
const isSafeInternalPath = target.startsWith('/') && !target.startsWith('//') && !/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(target);
location.replace(isSafeInternalPath ? target : '/home');
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 17, 2026

Deploying openshockapp with  Cloudflare Pages  Cloudflare Pages

Latest commit: f037443
Status: ✅  Deploy successful!
Preview URL: https://68707c38.openshockapp.pages.dev
Branch Preview URL: https://feat-onboarding-flow.openshockapp.pages.dev

View logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants