@tutorialkit-rb/astro 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/default/components/ActivateOverlay.tsx +39 -0
- package/dist/default/components/MainContainer.astro +3 -2
- package/dist/default/components/webcontainer.ts +4 -1
- package/dist/default/layouts/Layout.astro +9 -0
- package/dist/default/pages/[...slug].astro +18 -7
- package/dist/default/stores/deferred-store.ts +32 -0
- package/dist/default/styles/base.css +9 -0
- package/package.json +6 -6
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useStore } from '@nanostores/react';
|
|
2
|
+
import { isDeferred, isActivated, activateText, activate } from '../stores/deferred-store.js';
|
|
3
|
+
|
|
4
|
+
export default function ActivateOverlay() {
|
|
5
|
+
const activated = useStore(isActivated);
|
|
6
|
+
|
|
7
|
+
if (!isDeferred || activated) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const displayText = (activateText ?? 'Start Tutorial').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="fixed inset-0 z-50 flex flex-col justify-center items-center backdrop-blur-md" style={{ backgroundColor: 'rgba(255, 255, 255, 0.1)' }}>
|
|
15
|
+
<button
|
|
16
|
+
onClick={activate}
|
|
17
|
+
className="inline-flex items-center font-500 text-sm rounded-md cursor-pointer transition-all duration-150"
|
|
18
|
+
style={{
|
|
19
|
+
padding: '0.5rem 1rem',
|
|
20
|
+
backgroundColor: '#4f46e5',
|
|
21
|
+
borderColor: '#4f46e5',
|
|
22
|
+
border: '1px solid #4f46e5',
|
|
23
|
+
color: '#ffffff',
|
|
24
|
+
borderRadius: '0.375rem',
|
|
25
|
+
}}
|
|
26
|
+
onMouseEnter={(e) => {
|
|
27
|
+
e.currentTarget.style.backgroundColor = '#4338ca';
|
|
28
|
+
e.currentTarget.style.borderColor = '#4338ca';
|
|
29
|
+
}}
|
|
30
|
+
onMouseLeave={(e) => {
|
|
31
|
+
e.currentTarget.style.backgroundColor = '#4f46e5';
|
|
32
|
+
e.currentTarget.style.borderColor = '#4f46e5';
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
{displayText}
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -12,9 +12,10 @@ import { hasWorkspace } from '../utils/workspace';
|
|
|
12
12
|
interface Props {
|
|
13
13
|
lesson: Lesson<AstroComponentFactory>;
|
|
14
14
|
navList: NavList;
|
|
15
|
+
showNav?: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const { lesson, navList } = Astro.props;
|
|
18
|
+
const { lesson, navList, showNav = true } = Astro.props;
|
|
18
19
|
|
|
19
20
|
const showWorkspacePanel = hasWorkspace(lesson);
|
|
20
21
|
---
|
|
@@ -31,7 +32,7 @@ const showWorkspacePanel = hasWorkspace(lesson);
|
|
|
31
32
|
class="h-full flex flex-col transition-theme bg-tk-elements-app-backgroundColor text-tk-elements-app-textColor"
|
|
32
33
|
slot="a"
|
|
33
34
|
>
|
|
34
|
-
<Nav client:load lesson={lesson} navList={navList} />
|
|
35
|
+
{showNav && <Nav client:load lesson={lesson} navList={navList} />}
|
|
35
36
|
<TutorialContent lesson={lesson} />
|
|
36
37
|
</div>
|
|
37
38
|
<div
|
|
@@ -4,6 +4,7 @@ import { useAuth } from './setup.js';
|
|
|
4
4
|
import { safeBoot, TutorialStore } from '@tutorialkit-rb/runtime';
|
|
5
5
|
import { auth, WebContainer } from '@webcontainer/api';
|
|
6
6
|
import { joinPaths } from '../utils/url.js';
|
|
7
|
+
import { activationPromise } from '../stores/deferred-store.js';
|
|
7
8
|
|
|
8
9
|
interface WebContainerContext {
|
|
9
10
|
readonly useAuth: boolean;
|
|
@@ -16,7 +17,9 @@ export let webcontainer: Promise<WebContainer> = new Promise(() => {
|
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
if (!import.meta.env.SSR) {
|
|
19
|
-
webcontainer =
|
|
20
|
+
webcontainer = activationPromise
|
|
21
|
+
.then(() => (useAuth ? auth.loggedIn() : null))
|
|
22
|
+
.then(() => safeBoot({ workdirName: 'tutorial' }));
|
|
20
23
|
|
|
21
24
|
webcontainer.then(() => {
|
|
22
25
|
webcontainerContext.loaded = true;
|
|
@@ -37,6 +37,7 @@ const canonicalUrl = Astro.site ? new URL(Astro.url.pathname, Astro.site).toStri
|
|
|
37
37
|
<ViewTransitions />
|
|
38
38
|
<script is:inline>
|
|
39
39
|
setTutorialKitTheme();
|
|
40
|
+
setTutorialKitEmbed();
|
|
40
41
|
|
|
41
42
|
function setTutorialKitTheme() {
|
|
42
43
|
let theme = localStorage.getItem('tk_theme');
|
|
@@ -47,12 +48,19 @@ const canonicalUrl = Astro.site ? new URL(Astro.url.pathname, Astro.site).toStri
|
|
|
47
48
|
|
|
48
49
|
document.querySelector('html')?.setAttribute('data-theme', theme);
|
|
49
50
|
}
|
|
51
|
+
|
|
52
|
+
function setTutorialKitEmbed() {
|
|
53
|
+
if (new URLSearchParams(window.location.search).get('embed') === 'true') {
|
|
54
|
+
document.querySelector('html')?.setAttribute('data-embed', 'true');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
50
57
|
</script>
|
|
51
58
|
<script>
|
|
52
59
|
import { swapFunctions as builtInSwap } from 'astro:transitions/client';
|
|
53
60
|
|
|
54
61
|
declare global {
|
|
55
62
|
function setTutorialKitTheme(): void;
|
|
63
|
+
function setTutorialKitEmbed(): void;
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
document.addEventListener('astro:before-swap', (event) => {
|
|
@@ -63,6 +71,7 @@ const canonicalUrl = Astro.site ? new URL(Astro.url.pathname, Astro.site).toStri
|
|
|
63
71
|
builtInSwap.swapRootAttributes(newDocument);
|
|
64
72
|
|
|
65
73
|
setTutorialKitTheme();
|
|
74
|
+
setTutorialKitEmbed();
|
|
66
75
|
|
|
67
76
|
/**
|
|
68
77
|
* Keep the dynamically injected style sheet from Codemirror on all transitions.
|
|
@@ -3,6 +3,7 @@ import type { InferGetStaticPropsType } from 'astro';
|
|
|
3
3
|
import TopBarWrapper from '../components/TopBarWrapper.astro';
|
|
4
4
|
import MainContainer from '../components/MainContainer.astro';
|
|
5
5
|
import PageLoadingIndicator from '../components/PageLoadingIndicator.astro';
|
|
6
|
+
import ActivateOverlay from '../components/ActivateOverlay.tsx';
|
|
6
7
|
import Layout from '../layouts/Layout.astro';
|
|
7
8
|
import '../styles/base.css';
|
|
8
9
|
import '@tutorialkit/custom.css';
|
|
@@ -15,6 +16,12 @@ export async function getStaticPaths() {
|
|
|
15
16
|
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
|
16
17
|
|
|
17
18
|
const { lesson, logoLink, navList, title } = Astro.props as Props;
|
|
19
|
+
|
|
20
|
+
// Embed mode configuration
|
|
21
|
+
const embedMode = Astro.url.searchParams.get('embed') === 'true';
|
|
22
|
+
const showTopBar = !embedMode;
|
|
23
|
+
const showNav = !embedMode;
|
|
24
|
+
|
|
18
25
|
const meta = lesson.data?.meta ?? {};
|
|
19
26
|
|
|
20
27
|
// use lesson's default title and a default description for SEO metadata
|
|
@@ -28,12 +35,16 @@ meta.description ??= 'A TutorialKit interactive lesson';
|
|
|
28
35
|
<div id="previews-container" style="display: none;"></div>
|
|
29
36
|
|
|
30
37
|
<main class="max-w-full flex flex-col h-full overflow-hidden" data-swap-root>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
{showTopBar && (
|
|
39
|
+
<TopBarWrapper
|
|
40
|
+
logoLink={logoLink ?? '/'}
|
|
41
|
+
openInStackBlitz={lesson.data.openInStackBlitz}
|
|
42
|
+
downloadAsZip={lesson.data.downloadAsZip}
|
|
43
|
+
/>
|
|
44
|
+
)}
|
|
45
|
+
|
|
46
|
+
<MainContainer lesson={lesson} navList={navList} showNav={showNav} />
|
|
38
47
|
</main>
|
|
48
|
+
|
|
49
|
+
<ActivateOverlay client:load />
|
|
39
50
|
</Layout>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { atom } from 'nanostores';
|
|
2
|
+
|
|
3
|
+
function getActivateText(): string | null {
|
|
4
|
+
if (typeof window === 'undefined') {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const value = new URLSearchParams(window.location.search).get('activate');
|
|
9
|
+
|
|
10
|
+
if (value === null || value === '') {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const activateText = getActivateText();
|
|
18
|
+
export const isDeferred = activateText !== null;
|
|
19
|
+
export const isActivated = atom<boolean>(!isDeferred);
|
|
20
|
+
|
|
21
|
+
let _resolveActivation: (() => void) | undefined;
|
|
22
|
+
|
|
23
|
+
export const activationPromise: Promise<void> = isDeferred
|
|
24
|
+
? new Promise<void>((resolve) => {
|
|
25
|
+
_resolveActivation = resolve;
|
|
26
|
+
})
|
|
27
|
+
: Promise.resolve();
|
|
28
|
+
|
|
29
|
+
export function activate() {
|
|
30
|
+
isActivated.set(true);
|
|
31
|
+
_resolveActivation?.();
|
|
32
|
+
}
|
|
@@ -9,3 +9,12 @@ body {
|
|
|
9
9
|
font-style: normal;
|
|
10
10
|
font-variation-settings: 'slnt' 0;
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
/* Embed mode: hide top bar and navigation */
|
|
14
|
+
html[data-embed='true'] main[data-swap-root] > nav {
|
|
15
|
+
display: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
html[data-embed='true'] main[data-swap-root] header {
|
|
19
|
+
display: none;
|
|
20
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tutorialkit-rb/astro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "TutorialKit integration for Astro (https://astro.build)",
|
|
5
5
|
"author": "StackBlitz Inc.",
|
|
6
6
|
"type": "module",
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
"unist-util-visit": "^5.0.0",
|
|
55
55
|
"unocss": "^0.59.4",
|
|
56
56
|
"zod": "3.23.8",
|
|
57
|
-
"@tutorialkit-rb/
|
|
58
|
-
"@tutorialkit-rb/
|
|
59
|
-
"@tutorialkit-rb/
|
|
60
|
-
"@tutorialkit-rb/types": "0.
|
|
57
|
+
"@tutorialkit-rb/runtime": "0.2.0",
|
|
58
|
+
"@tutorialkit-rb/theme": "0.2.0",
|
|
59
|
+
"@tutorialkit-rb/react": "0.2.0",
|
|
60
|
+
"@tutorialkit-rb/types": "0.2.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@types/mdast": "^4.0.4",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"typescript": "^5.4.5",
|
|
68
68
|
"vite-plugin-inspect": "0.8.4",
|
|
69
69
|
"vitest": "^3.0.5",
|
|
70
|
-
"@tutorialkit-rb/types": "0.
|
|
70
|
+
"@tutorialkit-rb/types": "0.2.0"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"astro": "^4.15.0"
|