@motion-proto/live-tokens 0.80.0 → 0.82.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/CHANGELOG.md +35 -0
- package/package.json +17 -1
- package/src/demo/Demo.svelte +97 -0
- package/src/demo/Demo.svelte.d.ts +7 -0
- package/src/demo/DemoBar.svelte +75 -0
- package/src/demo/FloatingTokenTags.css +284 -0
- package/src/demo/FloatingTokenTags.svelte +570 -0
- package/src/demo/LabeledSelect.svelte +243 -0
- package/src/demo/Section.svelte +46 -0
- package/src/demo/SketchSelect.svelte +58 -0
- package/src/demo/SourceLinks.svelte +156 -0
- package/src/demo/TestingLoops.svelte +876 -0
- package/src/demo/TestingLoops.svelte.d.ts +4 -0
- package/src/demo/ThemeSelect.svelte +52 -0
- package/src/demo/sections/SectionClaude.svelte +192 -0
- package/src/demo/sections/SectionFooter.svelte +67 -0
- package/src/demo/sections/SectionGetStarted.svelte +100 -0
- package/src/demo/sections/SectionHero.svelte +201 -0
- package/src/demo/sections/SectionKit.svelte +410 -0
- package/src/demo/sections/SectionLayers.svelte +54 -0
- package/src/demo/sections/SectionOffering.svelte +135 -0
- package/src/demo/sketchCarry.ts +91 -0
- package/src/editor/skill-atlas/SkillAtlas.svelte +42 -19
- package/src/editor/skill-atlas/atlasLink.ts +43 -0
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { tick } from 'svelte';
|
|
2
|
+
import { onMount, tick } from 'svelte';
|
|
3
3
|
import { navigate } from '../core/routing/router';
|
|
4
4
|
import Button from '../../system/components/Button.svelte';
|
|
5
5
|
import TabBar from '../../system/components/TabBar.svelte';
|
|
6
6
|
import SourcePane from './SourcePane.svelte';
|
|
7
7
|
import TreeCanvas from './TreeCanvas.svelte';
|
|
8
|
+
import { linkHash, linkTargets, resolveLink } from './atlasLink';
|
|
8
9
|
import { SKILL_DOC, skillDocs } from './skillSources';
|
|
9
10
|
import { skillTrees } from './skillTrees';
|
|
10
11
|
import type { LineRange, Selection } from './types';
|
|
11
12
|
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
const linked = window.location.hash
|
|
15
|
-
let active = $state(linked
|
|
16
|
-
let selection: Selection | null = $state(null);
|
|
13
|
+
// `#set-type` opens that skill and `#set-type/write-the-font-pairing/voice`
|
|
14
|
+
// also selects that block, so a link can hand someone one step of one tree.
|
|
15
|
+
const linked = resolveLink(window.location.hash, skillTrees);
|
|
16
|
+
let active = $state(linked?.skill ?? Object.keys(skillTrees)[0]);
|
|
17
|
+
let selection: Selection | null = $state(linked?.target ?? null);
|
|
17
18
|
/** Which of the skill's documents the source pane shows. */
|
|
18
19
|
let doc: string = $state(SKILL_DOC);
|
|
19
20
|
|
|
@@ -43,27 +44,21 @@
|
|
|
43
44
|
|
|
44
45
|
function selectTarget(key: string, label: string, range: LineRange) {
|
|
45
46
|
selection = { key, label, lines: range };
|
|
47
|
+
shareSelection();
|
|
46
48
|
// Every range cites SKILL.md, so a step selected while a reference is open
|
|
47
49
|
// would otherwise highlight nothing.
|
|
48
50
|
doc = SKILL_DOC;
|
|
49
51
|
scrollSourceTo(range[0]);
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
...(node.lines ? [{ key: node.id, label: node.title, lines: node.lines }] : []),
|
|
56
|
-
...(node.chips ?? []).map((chip, i) => ({
|
|
57
|
-
key: `${node.id}:${i}`,
|
|
58
|
-
label: chip.label,
|
|
59
|
-
lines: chip.lines,
|
|
60
|
-
})),
|
|
61
|
-
]);
|
|
54
|
+
function shareSelection() {
|
|
55
|
+
const target = linkTargets(tree).find((t) => t.key === selection?.key) ?? null;
|
|
56
|
+
history.replaceState(null, '', `${window.location.pathname}${linkHash(active, target)}`);
|
|
62
57
|
}
|
|
63
58
|
|
|
64
59
|
/** Reverse lookup: the narrowest target whose range covers this line wins. */
|
|
65
60
|
function selectFromLine(lineNumber: number) {
|
|
66
|
-
const covering =
|
|
61
|
+
const covering = linkTargets(tree)
|
|
67
62
|
.filter(({ lines: [from, to] }) => lineNumber >= from && lineNumber <= to)
|
|
68
63
|
.sort((a, b) => a.lines[1] - a.lines[0] - (b.lines[1] - b.lines[0]));
|
|
69
64
|
|
|
@@ -71,8 +66,13 @@
|
|
|
71
66
|
if (!best) return;
|
|
72
67
|
|
|
73
68
|
selection = best;
|
|
69
|
+
shareSelection();
|
|
70
|
+
scrollTreeTo(best.key);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function scrollTreeTo(key: string) {
|
|
74
74
|
treePane
|
|
75
|
-
?.querySelector(`[data-node="${
|
|
75
|
+
?.querySelector(`[data-node="${key.split(':')[0]}"]`)
|
|
76
76
|
?.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -88,11 +88,34 @@
|
|
|
88
88
|
active = id;
|
|
89
89
|
selection = null;
|
|
90
90
|
doc = SKILL_DOC;
|
|
91
|
-
|
|
91
|
+
shareSelection();
|
|
92
92
|
treePane?.scrollTo({ top: 0 });
|
|
93
93
|
sourcePane?.scrollTo({ top: 0 });
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
function openLink(hash: string) {
|
|
97
|
+
const link = resolveLink(hash, skillTrees);
|
|
98
|
+
if (!link) return;
|
|
99
|
+
active = link.skill;
|
|
100
|
+
selection = link.target;
|
|
101
|
+
doc = SKILL_DOC;
|
|
102
|
+
if (!link.target) return;
|
|
103
|
+
const { key, lines: [from] } = link.target;
|
|
104
|
+
// The cards and source rows of a newly opened tab exist only after render.
|
|
105
|
+
tick().then(() => {
|
|
106
|
+
scrollTreeTo(key);
|
|
107
|
+
scrollSourceTo(from);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
onMount(() => {
|
|
112
|
+
openLink(window.location.hash);
|
|
113
|
+
// A link pasted into this tab changes only the hash, so the page never reloads.
|
|
114
|
+
const onHash = () => openLink(window.location.hash);
|
|
115
|
+
window.addEventListener('hashchange', onHash);
|
|
116
|
+
return () => window.removeEventListener('hashchange', onHash);
|
|
117
|
+
});
|
|
118
|
+
|
|
96
119
|
let treePane: HTMLElement | undefined = $state();
|
|
97
120
|
let sourcePane: HTMLElement | undefined = $state();
|
|
98
121
|
</script>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Selection, SkillTree } from './types';
|
|
2
|
+
|
|
3
|
+
/** A selectable target with the path segment its shared link names it by. */
|
|
4
|
+
export interface LinkedTarget extends Selection {
|
|
5
|
+
path: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function slug(text: string): string {
|
|
9
|
+
return text
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
12
|
+
.replace(/^-|-$/g, '');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Every target in the tree, node and chip alike. A chip's path nests under its card's. */
|
|
16
|
+
export function linkTargets(tree: SkillTree): LinkedTarget[] {
|
|
17
|
+
return tree.nodes.flatMap((node) => [
|
|
18
|
+
...(node.lines
|
|
19
|
+
? [{ key: node.id, label: node.title, lines: node.lines, path: slug(node.title) }]
|
|
20
|
+
: []),
|
|
21
|
+
...(node.chips ?? []).map((chip, i) => ({
|
|
22
|
+
key: `${node.id}:${i}`,
|
|
23
|
+
label: chip.label,
|
|
24
|
+
lines: chip.lines,
|
|
25
|
+
path: `${slug(node.title)}/${slug(chip.label)}`,
|
|
26
|
+
})),
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function linkHash(skill: string, target: LinkedTarget | null): string {
|
|
31
|
+
return target ? `#${skill}/${target.path}` : `#${skill}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Reads `#set-type/write-the-font-pairing/voice`. An unknown block still opens its skill. */
|
|
35
|
+
export function resolveLink(
|
|
36
|
+
hash: string,
|
|
37
|
+
trees: Record<string, SkillTree>,
|
|
38
|
+
): { skill: string; target: LinkedTarget | null } | null {
|
|
39
|
+
const [skill, ...rest] = decodeURIComponent(hash.replace(/^#/, '')).split('/');
|
|
40
|
+
if (!(skill in trees)) return null;
|
|
41
|
+
const path = rest.join('/');
|
|
42
|
+
return { skill, target: linkTargets(trees[skill]).find((t) => t.path === path) ?? null };
|
|
43
|
+
}
|