Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
cba49ae
docs(editor): add v0 REPL editor page
johnleider Feb 12, 2026
cb756e8
docs(editor): add v0 theme and UnoCSS runtime to REPL editor
johnleider Feb 12, 2026
1190649
docs(editor): add @vue/repl dependency and fix preview layout
johnleider Feb 12, 2026
f21b6c6
Merge remote-tracking branch 'origin/master' into docs/editor
johnleider Feb 13, 2026
1c7ad34
docs(editor): add file tree sidebar with CRUD, tabs, and breadcrumbs
johnleider Feb 13, 2026
47100f1
docs(editor): improve load performance, fix FOUC, and add tab management
johnleider Feb 13, 2026
5466fed
docs(editor): resizable file panel, tab close improvements, and polish
johnleider Feb 14, 2026
49e83fc
docs(editor): extract composables, lazy-load fflate, and improve UX
johnleider Feb 14, 2026
cb37960
Merge remote-tracking branch 'origin/master' into docs/editor
johnleider Feb 14, 2026
ec3c527
docs(editor): show main.ts under src, rename project to /, tab orderi…
johnleider Feb 14, 2026
596da67
docs(editor): remove old playground composable and update UI references
johnleider Feb 14, 2026
9bf240d
docs(editor): add layout toggle and hide file panel on mobile
johnleider Feb 14, 2026
996d7b7
docs(editor): mobile file panel with stack overlay and global scrim
johnleider Feb 15, 2026
44ef39c
docs(editor): add tutorial system with markdown instructions panel
johnleider Feb 15, 2026
3d83a04
docs(editor): resizable tutorial panels, step preloading, and markdow…
johnleider Feb 15, 2026
ca5c5f2
docs(editor): move tutorials into skillz directory
johnleider Feb 15, 2026
dfc4417
docs(editor): sync preview theme when doc site theme toggles
johnleider Feb 15, 2026
669f58a
docs(editor): wrap vue tutorial code fences in template tags
johnleider Feb 15, 2026
6727c20
docs(editor): tutorial landing pages, level grouping, and card improv…
johnleider Feb 17, 2026
510f12a
docs(editor): editor refactors, tutorial content, and skillz routing
johnleider Feb 17, 2026
8d69aeb
docs(editor): add horizontal drag-scroll carousel for skill cards
johnleider Feb 18, 2026
96a0bba
docs(editor): carousel arrows, keyboard nav, fade masks, and a11y imp…
johnleider Feb 18, 2026
ff72d0d
docs(editor): fix tutorial overflow and completion redirect
johnleider Feb 18, 2026
fb1982c
docs(editor): prevent sandbox iframe recreation on step transitions
johnleider Feb 18, 2026
6967d31
docs(editor): tutorial step progress, routing, and resume support
johnleider Feb 18, 2026
13d1034
docs(editor): playground intro panel, url hash state, and iframe fixes
johnleider Feb 19, 2026
79129f9
docs(playground): update default example to use createSingle with man…
johnleider Feb 19, 2026
ba2cda2
docs(skillz): redesign resume popup with snooze, level badge, progres…
johnleider Feb 19, 2026
9e0dbdc
chore(EditorIntroPanel): update content
johnleider Feb 19, 2026
1a40f9e
docs(skillz): separate local UI prefs from synced progress data
johnleider Feb 19, 2026
1c5ec44
Merge remote-tracking branch 'origin/master' into docs/editor
johnleider Feb 19, 2026
3bc8f6e
docs(skillz): organize tutorials into level subfolders
johnleider Feb 19, 2026
4f8a16b
docs(skillz): rename editor → playground across all files and symbols
johnleider Feb 19, 2026
7113c83
docs(skillz): remove redundant base classes from tutorial App.vue files
johnleider Feb 19, 2026
4873ce5
docs(playground): consolidate dual-repl, add Pinia store with tour API
johnleider Feb 19, 2026
392d644
docs: withDefaults → Vue 3.5 destructuring, toRef, v0 utilities, idle…
johnleider Feb 19, 2026
7f5c451
docs(playground): layout toggle, panel mode, and workspace fixes
johnleider Feb 19, 2026
feed90f
docs(playground): fix stale tour references after editor→playground r…
johnleider Feb 19, 2026
2c8f87a
docs(playground): use Pinia store directly in tour instead of injecte…
johnleider Feb 19, 2026
4da44d8
docs(playground): mobile support, tour fixes, and resize UX polish
johnleider Feb 20, 2026
649283b
docs(playground): fix resize stuttering
johnleider Feb 20, 2026
9a51837
docs(playground): extract PlaygroundWorkspace into focused SFCs
johnleider Feb 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/docs/build/generate-nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ async function generateNav (): Promise<NavItem[]> {
pages.get(key)!.push(pageInfo)
}

// Inject non-markdown standalone routes (e.g. .vue pages with no backing .md file)
standalonePages.push({ item: { name: 'Playground', to: '/playground' }, order: 1.15 })

const nav: NavItem[] = []
const sectionEntries = Object.entries(SECTIONS).toSorted((a, b) => a[1].order - b[1].order)
const standalonesSorted = standalonePages.toSorted((a, b) => a.order - b.order)
Expand Down
6 changes: 3 additions & 3 deletions apps/docs/build/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ export default async function MarkdownPlugin () {
// Look ahead: blockquote_open -> paragraph_open -> inline
const inlineToken = tokens[index + 2]
if (inlineToken?.type === 'inline' && inlineToken.content) {
const match = inlineToken.content.match(/^\[!(TIP|INFO|WARNING|ERROR|ASKAI|DISCORD|TOUR)\]\s*(.*)/)
const match = inlineToken.content.match(/^\[!(TIP|INFO|WARNING|ERROR|ASKAI|DISCORD|TOUR|TRY)\]\s*(.*)/)
if (match) {
const type = match[1].toLowerCase()
env._calloutType = type
Expand Down Expand Up @@ -292,8 +292,8 @@ export default async function MarkdownPlugin () {
if (inlineToken.children?.length) {
const firstChild = inlineToken.children[0]
if (firstChild?.type === 'text') {
// Only TIP|INFO|WARNING|ERROR reach here - ASKAI, DISCORD, TOUR return early with cleared content
firstChild.content = firstChild.content.replace(/^\[!(TIP|INFO|WARNING|ERROR)\]\s*/, '')
// Only TIP|INFO|WARNING|ERROR|TRY reach here - ASKAI, DISCORD, TOUR return early with cleared content
firstChild.content = firstChild.content.replace(/^\[!(TIP|INFO|WARNING|ERROR|TRY)\]\s*/, '')
}
}

Expand Down
1 change: 1 addition & 0 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@mdi/js": "catalog:",
"@octokit/core": "catalog:",
"@unhead/addons": "catalog:",
"@vue/repl": "catalog:",
"@vuetify/auth": "catalog:",
"@vuetify/paper": "workspace:*",
"@vuetify/v0": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<template>
<div v-if="showMesh" aria-hidden="true" class="mesh-bg mesh-bg-top" />
<div v-if="showMesh" aria-hidden="true" class="mesh-bg mesh-bg-bottom" :class="{ visible: showBottomMesh }" />
<main class="min-h-screen pt-[72px] text-on-background" :class="{ 'dot-grid': settings.showDotGrid.value }">
<main class="min-h-screen text-on-background" :class="{ 'dot-grid': settings.showDotGrid.value }">
<router-view />
</main>

Expand Down
22 changes: 22 additions & 0 deletions apps/docs/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ declare module 'vue' {
export interface GlobalComponents {
AppAccount: typeof import('./components/app/AppAccount.vue')['default']
AppAskInline: typeof import('./components/app/AppAskInline.vue')['default']
AppBackground: typeof import('./components/app/AppBackground.vue')['default']
AppBanner: typeof import('./components/app/AppBanner.vue')['default']
AppBar: typeof import('./components/app/AppBar.vue')['default']
AppBreadcrumbs: typeof import('./components/app/AppBreadcrumbs.vue')['default']
Expand Down Expand Up @@ -122,6 +123,13 @@ declare module 'vue' {
DocsSkeleton: typeof import('./components/docs/DocsSkeleton.vue')['default']
DocsSkillToggle: typeof import('./components/docs/meta/DocsSkillToggle.vue')['default']
DocsToc: typeof import('./components/docs/DocsToc.vue')['default']
EditorBreadcrumbs: typeof import('./components/editor/EditorBreadcrumbs.vue')['default']
EditorExamples: typeof import('./components/editor/EditorExamples.vue')['default']
EditorFileTree: typeof import('./components/editor/EditorFileTree.vue')['default']
EditorIntroPanel: typeof import('./components/editor/EditorIntroPanel.vue')['default']
EditorMarkdownPanel: typeof import('./components/editor/EditorMarkdownPanel.vue')['default']
EditorTabs: typeof import('./components/editor/EditorTabs.vue')['default']
EditorWorkspace: typeof import('./components/editor/EditorWorkspace.vue')['default']
HomeAiFirst: typeof import('./components/home/HomeAiFirst.vue')['default']
HomeArchitecture: typeof import('./components/home/HomeArchitecture.vue')['default']
HomeAskDialog: typeof import('./components/home/HomeAskDialog.vue')['default']
Expand All @@ -132,6 +140,18 @@ declare module 'vue' {
HomeEcosystem: typeof import('./components/home/HomeEcosystem.vue')['default']
HomeFoundation: typeof import('./components/home/HomeFoundation.vue')['default']
HomeHero: typeof import('./components/home/HomeHero.vue')['default']
PlaygroundBreadcrumbs: typeof import('./components/playground/PlaygroundBreadcrumbs.vue')['default']
PlaygroundEditor: typeof import('./components/playground/PlaygroundEditor.vue')['default']
PlaygroundExamples: typeof import('./components/playground/PlaygroundExamples.vue')['default']
PlaygroundFileTree: typeof import('./components/playground/PlaygroundFileTree.vue')['default']
PlaygroundIntroPanel: typeof import('./components/playground/PlaygroundIntroPanel.vue')['default']
PlaygroundMarkdownPanel: typeof import('./components/playground/PlaygroundMarkdownPanel.vue')['default']
PlaygroundMobileSidebar: typeof import('./components/playground/PlaygroundMobileSidebar.vue')['default']
PlaygroundPreview: typeof import('./components/playground/PlaygroundPreview.vue')['default']
PlaygroundResizeHandle: typeof import('./components/playground/PlaygroundResizeHandle.vue')['default']
PlaygroundTabBar: typeof import('./components/playground/PlaygroundTabBar.vue')['default']
PlaygroundTabs: typeof import('./components/playground/PlaygroundTabs.vue')['default']
PlaygroundWorkspace: typeof import('./components/playground/PlaygroundWorkspace.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SkillCard: typeof import('./components/skillz/SkillCard.vue')['default']
Expand All @@ -143,6 +163,8 @@ declare module 'vue' {
SkillMetadata: typeof import('./components/skillz/SkillMetadata.vue')['default']
SkillModeBadge: typeof import('./components/skillz/SkillModeBadge.vue')['default']
SkillPrerequisites: typeof import('./components/skillz/SkillPrerequisites.vue')['default']
SkillzBadge: typeof import('./components/skillz/SkillzBadge.vue')['default']
SkillzComplete: typeof import('./components/skillz/SkillzComplete.vue')['default']
SkillzResume: typeof import('./components/skillz/SkillzResume.vue')['default']
SkillzTour: typeof import('./components/skillz/SkillzTour.vue')['default']
}
Expand Down
6 changes: 2 additions & 4 deletions apps/docs/src/components/app/AppBrowserIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@
size?: number
}

const props = withDefaults(defineProps<AppBrowserIconProps>(), {
size: 18,
})
const { browser, size = 18 } = defineProps<AppBrowserIconProps>()

const config = computed(() => browsers[props.browser])
const config = computed(() => browsers[browser])
</script>

<template>
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/src/components/discovery/DiscoveryContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@

const breakpoints = useBreakpoints()
const activePlacement = toRef(() => {
// Last step always uses center placement
if (discovery.isLast.value) return 'center'
// First and last steps always use center placement
if (discovery.isFirst.value || discovery.isLast.value) return 'center'
if (!isNullOrUndefined(placementMobile) && breakpoints.smAndDown.value) return placementMobile
return placement
})
Expand Down
18 changes: 6 additions & 12 deletions apps/docs/src/components/docs/DocsBadge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import { computed } from 'vue'

const props = withDefaults(defineProps<{
const { label, icon, color, backgroundOpacity = 15, showIcon = true, showLabel = true, iconSize = 14, shape = 'rounded', title } = defineProps<{
/** Badge text label */
label: string
/** Icon name (optional) */
Expand All @@ -25,23 +25,17 @@
shape?: 'rounded' | 'pill'
/** Tooltip text */
title?: string
}>(), {
backgroundOpacity: 15,
showIcon: true,
showLabel: true,
iconSize: 14,
shape: 'rounded',
})
}>()

const badgeStyle = computed(() => {
if (!props.color) return undefined
if (!color) return undefined
return {
background: `color-mix(in srgb, ${props.color} ${props.backgroundOpacity}%, transparent)`,
color: props.color,
background: `color-mix(in srgb, ${color} ${backgroundOpacity}%, transparent)`,
color,
}
})

const shapeClass = computed(() => props.shape === 'pill' ? 'rounded-full' : 'rounded')
const shapeClass = computed(() => shape === 'pill' ? 'rounded-full' : 'rounded')
</script>

<template>
Expand Down
26 changes: 12 additions & 14 deletions apps/docs/src/components/docs/DocsBrowserSupport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,32 @@
anchor?: string
}

const props = withDefaults(defineProps<DocsBrowserSupportProps>(), {
anchor: undefined,
})
const { feature, versions, anchor } = defineProps<DocsBrowserSupportProps>()

const browsers = computed(() => {
const list: { browser: BrowserName, name: string, version: string }[] = []

if (props.versions.chrome) {
list.push({ browser: 'chrome', name: 'Chrome', version: props.versions.chrome })
if (versions.chrome) {
list.push({ browser: 'chrome', name: 'Chrome', version: versions.chrome })
}
if (props.versions.edge) {
list.push({ browser: 'edge', name: 'Edge', version: props.versions.edge })
if (versions.edge) {
list.push({ browser: 'edge', name: 'Edge', version: versions.edge })
}
if (props.versions.firefox) {
list.push({ browser: 'firefox', name: 'Firefox', version: props.versions.firefox })
if (versions.firefox) {
list.push({ browser: 'firefox', name: 'Firefox', version: versions.firefox })
}
if (props.versions.safari) {
list.push({ browser: 'safari', name: 'Safari', version: props.versions.safari })
if (versions.safari) {
list.push({ browser: 'safari', name: 'Safari', version: versions.safari })
}
if (props.versions.opera) {
list.push({ browser: 'opera', name: 'Opera', version: props.versions.opera })
if (versions.opera) {
list.push({ browser: 'opera', name: 'Opera', version: versions.opera })
}

return list
})

const hasLimitedSupport = computed(() => {
return !props.versions.safari || props.versions.safari === '—'
return !versions.safari || versions.safari === '—'
})
</script>

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/src/components/docs/DocsCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
class="p-4 border border-divider rounded-lg bg-surface no-underline text-inherit transition-[border-color,box-shadow] duration-200"
:class="{
'opacity-60 cursor-not-allowed': disabled,
'hover:border-primary hover:shadow-md cursor-pointer': isInteractive && !disabled,
'hover:border-primary hover:shadow-md cursor-pointer focus-visible:outline-2 focus-visible:outline-primary focus-visible:-outline-offset-2': isInteractive && !disabled,
}"
v-bind="linkProps"
>
Expand Down
18 changes: 12 additions & 6 deletions apps/docs/src/components/docs/DocsCodeActions.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<script setup lang="ts">
// Composables
import { getBinUrl } from '@/composables/bin'
import { usePlayground } from '@/composables/playground'
import { useClipboard } from '@/composables/useClipboard'
import { usePlaygroundLink } from '@/composables/usePlaygroundLink'

const props = defineProps<{
code: string
language?: string
fileName?: string
title?: string
binTitle?: string
playground?: boolean
Expand All @@ -28,8 +29,13 @@
window.open(url, '_blank')
}

function openInPlayground () {
const url = usePlayground(props.code)
async function openInEditor () {
let name = props.fileName
// Ensure the file has a proper extension (e.g. "Anatomy" → "Anatomy.vue")
if (name && !/\.\w+$/.test(name)) {
name = `${name}.${props.language || 'vue'}`
}
const url = await usePlaygroundLink(props.code, name)
window.open(url, '_blank')
}
</script>
Expand All @@ -38,11 +44,11 @@
<div class="flex gap-1">
<AppIconButton
v-if="playground"
aria-label="Open in Vuetify Play"
aria-label="Open in Editor"
icon="vuetify-play"
title="Open in Vuetify Play"
title="Open in Editor"
type="button"
@click="openInPlayground"
@click="openInEditor"
/>

<AppIconButton
Expand Down
26 changes: 3 additions & 23 deletions apps/docs/src/components/docs/DocsDiscoveryStep.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,14 @@
// Components
import AppBurst from '@/components/app/AppBurst.vue'
import { Discovery } from '@/components/discovery'
import SkillzBadge from '@/components/skillz/SkillzBadge.vue'

// Composables
import { useDiscovery } from '@/composables/useDiscovery'

// Utilities
import { computed, onBeforeUnmount, useAttrs, useTemplateRef, watch } from 'vue'

// Types
import { SKILL_LEVEL_META } from '@/types/skill'

defineOptions({ name: 'DocsDiscoveryStep', inheritAttrs: false })

const props = defineProps<DocsDiscoveryStepProps>()
Expand All @@ -51,11 +49,7 @@
const attrs = useAttrs()
const discovery = useDiscovery()

const levelMeta = computed(() => {
// const tour = discovery.tours.selected.value
// if (!tour) return null
return SKILL_LEVEL_META[discovery.tours.selectedItem.value?.level || '']
})
const tourLevel = computed(() => discovery.tours.selectedItem.value?.level)

const isLastStep = computed(() => {
const index = discovery.steps.selectedIndex.value
Expand Down Expand Up @@ -96,14 +90,7 @@
>
<!-- Header -->
<div v-if="!isLastStep" class="flex justify-between items-center mb-4">
<span
class="skillz-badge inline-flex items-center gap-1 text-xs font-bold uppercase tracking-wide px-2 py-1 rounded"
:style="levelMeta ? { '--level-color': levelMeta.color } : undefined"
:title="levelMeta ? `${levelMeta.label} level` : undefined"
>
SKILLZ
<AppIcon v-if="levelMeta" :icon="levelMeta.icon" :size="14" />
</span>
<SkillzBadge :level="tourLevel" />

<Discovery.Progress class="text-xs text-on-surface-variant" />
</div>
Expand Down Expand Up @@ -162,10 +149,3 @@
</Discovery.Content>
</Discovery.Root>
</template>

<style scoped>
.skillz-badge {
background: color-mix(in srgb, var(--level-color, var(--v0-primary)) 15%, transparent);
color: var(--level-color, var(--v0-primary));
}
</style>
Loading
Loading