anentrypoint-design 0.0.10 → 0.0.12
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/247420.app.js +15 -1
- package/dist/247420.css +152 -133
- package/dist/247420.js +21 -7
- package/package.json +1 -1
- package/src/app.js +42 -17
- package/src/components.js +16 -4
- package/src/index.js +10 -3
- package/src/motion.js +122 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "247420 design system SDK — webjsx + modified ripple-ui, single-file ESM bundle for reproducible use of the AnEntrypoint design.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/247420.js",
|
package/src/app.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
// Unified 247420 single-page app. Replaces the separate homepage,
|
|
2
2
|
// project_page, writing, and manifesto entry points with one WebJSX page
|
|
3
3
|
// that uses the design-system components and hash routing.
|
|
4
|
-
import { h, mount, installStyles, components as C } from './index.js';
|
|
4
|
+
import { h, mount, installStyles, components as C, motion } from './index.js';
|
|
5
5
|
|
|
6
6
|
const data = {
|
|
7
|
-
nav: [['works', '#/works'], ['writing', '#/writing'], ['manifesto', '#/manifesto'], ['source ↗', 'https://github.com/AnEntrypoint']],
|
|
7
|
+
nav: [['works', '#/works'], ['project', '#/project'], ['writing', '#/writing'], ['manifesto', '#/manifesto'], ['source ↗', 'https://github.com/AnEntrypoint']],
|
|
8
8
|
works: [
|
|
9
|
-
{ code: '001', title: 'gm', sub: 'state machine for coding agents', meta: '2025 · 3k★', body: 'a tiny deterministic state machine that lets llms code without losing their minds. it thinks so you don\'t have to (as much).' },
|
|
10
|
-
{ code: '002', title: 'zellous', sub: 'production push-to-talk', meta: '2024 · live', body: 'hold the button. talk. someone on the other side hears you. opus codec, dynamic rooms, 50-message replay.' },
|
|
11
|
-
{ code: '003', title: 'spoint', sub: 'spawnpoint', meta: '2024 · live', body: 'the directory for "where should we start?" one url, one room, everyone lands in the same place.' },
|
|
12
|
-
{ code: '004', title: 'flatspace', sub: 'flat-file cms', meta: 'wip', body: 'still figuring out what to say about this one. come back tuesday.' },
|
|
13
|
-
{ code: '005', title: 'thebird', sub: '—', meta: 'wip', body: 'yes, the name is a reference. no, we won\'t tell you to what.' },
|
|
14
|
-
{ code: '006', title: 'mcp-repl', sub: 'repl for mcp', meta: '2024 · live', body: 'executenodejs, executedeno, executebash, astgrep_search.' },
|
|
15
|
-
{ code: '007', title: 'mutagen', sub: 'adaptogen server', meta: '2024 · live', body: 'everything to do with a dapp deg3n. read the source.' },
|
|
16
|
-
{ code: '008', title: 'techshaman', sub: 'member site', meta: 'ongoing', body: 'the official website for the techshaman.' }
|
|
9
|
+
{ code: '001', title: 'gm', sub: 'state machine for coding agents', meta: '2025 · 3k★', body: 'a tiny deterministic state machine that lets llms code without losing their minds. it thinks so you don\'t have to (as much).', href: '#/project', source: 'https://github.com/AnEntrypoint/gm' },
|
|
10
|
+
{ code: '002', title: 'zellous', sub: 'production push-to-talk', meta: '2024 · live', body: 'hold the button. talk. someone on the other side hears you. opus codec, dynamic rooms, 50-message replay.', href: 'https://zellous.com', source: 'https://github.com/AnEntrypoint/zellous' },
|
|
11
|
+
{ code: '003', title: 'spoint', sub: 'spawnpoint', meta: '2024 · live', body: 'the directory for "where should we start?" one url, one room, everyone lands in the same place.', href: 'https://spoint.world', source: 'https://github.com/AnEntrypoint/spoint' },
|
|
12
|
+
{ code: '004', title: 'flatspace', sub: 'flat-file cms', meta: 'wip', body: 'still figuring out what to say about this one. come back tuesday.', href: 'https://github.com/AnEntrypoint/flatspace', source: 'https://github.com/AnEntrypoint/flatspace' },
|
|
13
|
+
{ code: '005', title: 'thebird', sub: '—', meta: 'wip', body: 'yes, the name is a reference. no, we won\'t tell you to what.', href: 'https://github.com/AnEntrypoint/thebird', source: 'https://github.com/AnEntrypoint/thebird' },
|
|
14
|
+
{ code: '006', title: 'mcp-repl', sub: 'repl for mcp', meta: '2024 · live', body: 'executenodejs, executedeno, executebash, astgrep_search.', href: 'https://github.com/AnEntrypoint/mcp-repl', source: 'https://github.com/AnEntrypoint/mcp-repl' },
|
|
15
|
+
{ code: '007', title: 'mutagen', sub: 'adaptogen server', meta: '2024 · live', body: 'everything to do with a dapp deg3n. read the source.', href: 'https://github.com/AnEntrypoint/mutagen', source: 'https://github.com/AnEntrypoint/mutagen' },
|
|
16
|
+
{ code: '008', title: 'techshaman', sub: 'member site', meta: 'ongoing', body: 'the official website for the techshaman.', href: 'https://github.com/AnEntrypoint/techshaman', source: 'https://github.com/AnEntrypoint/techshaman' }
|
|
17
17
|
],
|
|
18
18
|
posts: [
|
|
19
|
-
{ date: '2026.04.14', title: 'we were here first', tag: 'lore' },
|
|
20
|
-
{ date: '2026.03.22', title: 'gm v0.4 postmortem, or: why state machines', tag: 'gm' },
|
|
21
|
-
{ date: '2026.02.09', title: 'push-to-talk is a protocol, not a feature', tag: 'zellous' },
|
|
22
|
-
{ date: '2025.12.11', title: 'against the vibe-coded interface', tag: 'manifesto' },
|
|
23
|
-
{ date: '2025.10.03', title: 'notes on shipping weird', tag: 'notes' }
|
|
19
|
+
{ date: '2026.04.14', title: 'we were here first', tag: 'lore', href: 'https://247420.xyz' },
|
|
20
|
+
{ date: '2026.03.22', title: 'gm v0.4 postmortem, or: why state machines', tag: 'gm', href: 'https://github.com/AnEntrypoint/gm' },
|
|
21
|
+
{ date: '2026.02.09', title: 'push-to-talk is a protocol, not a feature', tag: 'zellous', href: 'https://github.com/AnEntrypoint/zellous' },
|
|
22
|
+
{ date: '2025.12.11', title: 'against the vibe-coded interface', tag: 'manifesto', href: '#/manifesto' },
|
|
23
|
+
{ date: '2025.10.03', title: 'notes on shipping weird', tag: 'notes', href: 'https://github.com/AnEntrypoint' }
|
|
24
24
|
],
|
|
25
25
|
manifesto: [
|
|
26
26
|
{ text: 'we are the creative department of the internet. always open (24/7). always a little bit high on possibility (420).' },
|
|
@@ -59,6 +59,9 @@ function parseRoute() {
|
|
|
59
59
|
window.addEventListener('hashchange', () => {
|
|
60
60
|
state.route = parseRoute();
|
|
61
61
|
render();
|
|
62
|
+
requestAnimationFrame(() => {
|
|
63
|
+
motion.animateSelector('.app-main', 'fadeIn', { duration: 'var(--motion-base)' });
|
|
64
|
+
});
|
|
62
65
|
});
|
|
63
66
|
|
|
64
67
|
let render;
|
|
@@ -99,7 +102,18 @@ function pageMain() {
|
|
|
99
102
|
return C.ProjectView({
|
|
100
103
|
project: data.project,
|
|
101
104
|
copied: state.copied,
|
|
102
|
-
onCopy: (cmd) => {
|
|
105
|
+
onCopy: (cmd) => {
|
|
106
|
+
navigator.clipboard?.writeText(cmd);
|
|
107
|
+
state.copied = true;
|
|
108
|
+
render();
|
|
109
|
+
requestAnimationFrame(() => {
|
|
110
|
+
motion.animateSelector('.cli .copy', 'pulse', { duration: 'var(--motion-fast)' });
|
|
111
|
+
});
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
state.copied = false;
|
|
114
|
+
render();
|
|
115
|
+
}, 1200);
|
|
116
|
+
}
|
|
103
117
|
});
|
|
104
118
|
}
|
|
105
119
|
if (r === 'writing') {
|
|
@@ -110,7 +124,15 @@ function pageMain() {
|
|
|
110
124
|
}
|
|
111
125
|
return C.HomeView({
|
|
112
126
|
state, onNav: navigate,
|
|
113
|
-
onToggleWork: (i) => {
|
|
127
|
+
onToggleWork: (i) => {
|
|
128
|
+
state.opened = i;
|
|
129
|
+
render();
|
|
130
|
+
if (i >= 0) {
|
|
131
|
+
requestAnimationFrame(() => {
|
|
132
|
+
motion.animateSelector(`[data-work-index="${i}"]`, 'fadeInUp', { duration: 'var(--motion-fast)' });
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
},
|
|
114
136
|
works: data.works, posts: data.posts, manifesto: data.manifesto,
|
|
115
137
|
currentlyShipping: data.currentlyShipping
|
|
116
138
|
});
|
|
@@ -129,3 +151,6 @@ function App() {
|
|
|
129
151
|
await installStyles();
|
|
130
152
|
const root = document.getElementById('root');
|
|
131
153
|
render = mount(root, App);
|
|
154
|
+
requestAnimationFrame(() => {
|
|
155
|
+
motion.animateSelector('.app-main', 'fadeIn', { duration: 'var(--motion-base)' });
|
|
156
|
+
});
|
package/src/components.js
CHANGED
|
@@ -89,11 +89,19 @@ export function Status({ left = [], right = [] } = {}) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
export function AppShell({ topbar, crumb, side, main, status, narrow } = {}) {
|
|
92
|
+
const hasSide = Boolean(side);
|
|
93
|
+
const sideMotionClass = hasSide
|
|
94
|
+
? ' animate__animated animate__fadeInLeft'
|
|
95
|
+
: ' animate__animated animate__fadeOutLeft';
|
|
96
|
+
const sideNode = hasSide
|
|
97
|
+
? side
|
|
98
|
+
: h('aside', { class: 'app-side', 'aria-hidden': 'true' });
|
|
99
|
+
|
|
92
100
|
return h('div', { class: 'app' },
|
|
93
101
|
topbar || null,
|
|
94
102
|
crumb || null,
|
|
95
|
-
h('div', { class: 'app-body' + (
|
|
96
|
-
side
|
|
103
|
+
h('div', { class: 'app-body' + (hasSide ? '' : ' no-side') },
|
|
104
|
+
h('div', { class: 'app-side-shell' + sideMotionClass }, sideNode),
|
|
97
105
|
h('main', { class: 'app-main' + (narrow ? ' narrow' : '') }, ...(Array.isArray(main) ? main : [main]))
|
|
98
106
|
),
|
|
99
107
|
status || null
|
|
@@ -176,7 +184,7 @@ export function Changelog({ entries = [] }) {
|
|
|
176
184
|
children: entries.map((e, i) =>
|
|
177
185
|
h('div', { key: i, class: 'row', style: 'grid-template-columns:100px 70px 1fr' },
|
|
178
186
|
h('span', { class: 'code' }, e.date),
|
|
179
|
-
h('span', { style: 'color:var(--panel-accent);font-family:var(--ff-mono);font-size:
|
|
187
|
+
h('span', { style: 'color:var(--panel-accent);font-family:var(--ff-mono);font-size:14px' }, e.ver),
|
|
180
188
|
h('span', { class: 'title' }, e.msg)
|
|
181
189
|
)
|
|
182
190
|
)
|
|
@@ -196,7 +204,11 @@ export function WorksList({ works = [], openedIndex = -1, onToggle }) {
|
|
|
196
204
|
active: isOpen,
|
|
197
205
|
onClick: () => onToggle && onToggle(isOpen ? -1 : i)
|
|
198
206
|
}),
|
|
199
|
-
isOpen ? h('div', {
|
|
207
|
+
isOpen ? h('div', {
|
|
208
|
+
class: 'work-detail',
|
|
209
|
+
'data-work-index': String(i),
|
|
210
|
+
style: 'padding:14px 20px 18px 86px;background:var(--panel-2);color:var(--panel-text);font-size:13px;line-height:1.6'
|
|
211
|
+
},
|
|
200
212
|
h('p', { style: 'margin:0 0 12px 0;max-width:64ch' }, w.body),
|
|
201
213
|
h('div', { style: 'display:flex;gap:8px' },
|
|
202
214
|
Btn({ primary: true, href: w.href || '#', children: 'open ↗' }),
|
package/src/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as webjsx from '../vendor/webjsx/index.js';
|
|
|
2
2
|
import { loadCss, scope } from './styles.js';
|
|
3
3
|
import { registerDeckStage, getDeckStage } from './deck-stage.js';
|
|
4
4
|
import * as components from './components.js';
|
|
5
|
+
import * as motion from './motion.js';
|
|
5
6
|
|
|
6
7
|
let _installed = false;
|
|
7
8
|
export async function installStyles(target) {
|
|
@@ -12,6 +13,7 @@ export async function installStyles(target) {
|
|
|
12
13
|
tag.setAttribute('data-247420', '');
|
|
13
14
|
tag.textContent = css;
|
|
14
15
|
root.appendChild(tag);
|
|
16
|
+
if (!target) motion.installMotion();
|
|
15
17
|
if (!target) _installed = true;
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -19,12 +21,17 @@ export function mount(rootEl, viewFn, { autoScope = true } = {}) {
|
|
|
19
21
|
if (autoScope && rootEl && rootEl.classList && !rootEl.classList.contains(scope.slice(1))) {
|
|
20
22
|
rootEl.classList.add(scope.slice(1));
|
|
21
23
|
}
|
|
22
|
-
const render = () =>
|
|
24
|
+
const render = () => {
|
|
25
|
+
webjsx.applyDiff(rootEl, viewFn(render));
|
|
26
|
+
requestAnimationFrame(() => {
|
|
27
|
+
motion.animateTree(rootEl);
|
|
28
|
+
});
|
|
29
|
+
};
|
|
23
30
|
render();
|
|
24
31
|
return render;
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
export { webjsx, loadCss, scope, registerDeckStage, getDeckStage, components };
|
|
34
|
+
export { webjsx, loadCss, scope, registerDeckStage, getDeckStage, components, motion };
|
|
28
35
|
export const h = webjsx.createElement;
|
|
29
36
|
export const applyDiff = webjsx.applyDiff;
|
|
30
|
-
export default { webjsx, loadCss, scope, installStyles, mount, h, applyDiff, registerDeckStage, getDeckStage, components };
|
|
37
|
+
export default { webjsx, loadCss, scope, installStyles, mount, h, applyDiff, registerDeckStage, getDeckStage, components, motion };
|
package/src/motion.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const ANIMATE_CSS_ID = 'animate-style-cdn';
|
|
2
|
+
const ANIMATE_CSS_HREF = 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css';
|
|
3
|
+
const MOTION_STYLE_ID = 'ds-247420-motion';
|
|
4
|
+
|
|
5
|
+
function hasDom() {
|
|
6
|
+
return typeof document !== 'undefined';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function shouldReduceMotion() {
|
|
10
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;
|
|
11
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function installMotion() {
|
|
15
|
+
if (!hasDom()) return;
|
|
16
|
+
|
|
17
|
+
if (!document.getElementById(ANIMATE_CSS_ID)) {
|
|
18
|
+
const link = document.createElement('link');
|
|
19
|
+
link.id = ANIMATE_CSS_ID;
|
|
20
|
+
link.rel = 'stylesheet';
|
|
21
|
+
link.href = ANIMATE_CSS_HREF;
|
|
22
|
+
document.head.appendChild(link);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!document.getElementById(MOTION_STYLE_ID)) {
|
|
26
|
+
const style = document.createElement('style');
|
|
27
|
+
style.id = MOTION_STYLE_ID;
|
|
28
|
+
style.textContent = `
|
|
29
|
+
:root {
|
|
30
|
+
--motion-fast: 220ms;
|
|
31
|
+
--motion-base: 420ms;
|
|
32
|
+
--motion-slow: 720ms;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@media (prefers-reduced-motion: reduce) {
|
|
36
|
+
.animate__animated {
|
|
37
|
+
animation-duration: 1ms !important;
|
|
38
|
+
animation-iteration-count: 1 !important;
|
|
39
|
+
transition-duration: 1ms !important;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
`.trim();
|
|
43
|
+
document.head.appendChild(style);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function animateElement(el, name, {
|
|
48
|
+
duration = null,
|
|
49
|
+
delay = null,
|
|
50
|
+
speedClass = '',
|
|
51
|
+
repeat = null,
|
|
52
|
+
cleanup = true
|
|
53
|
+
} = {}) {
|
|
54
|
+
if (!el || !name || shouldReduceMotion()) return Promise.resolve(false);
|
|
55
|
+
|
|
56
|
+
const classes = ['animate__animated', `animate__${name}`];
|
|
57
|
+
if (speedClass) classes.push(`animate__${speedClass}`);
|
|
58
|
+
|
|
59
|
+
el.classList.remove(...classes);
|
|
60
|
+
void el.offsetWidth;
|
|
61
|
+
el.classList.add(...classes);
|
|
62
|
+
|
|
63
|
+
if (duration) el.style.setProperty('--animate-duration', duration);
|
|
64
|
+
if (delay) el.style.setProperty('--animate-delay', delay);
|
|
65
|
+
if (repeat != null) el.style.setProperty('--animate-repeat', String(repeat));
|
|
66
|
+
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
const onEnd = () => {
|
|
69
|
+
if (cleanup) {
|
|
70
|
+
el.classList.remove(...classes);
|
|
71
|
+
}
|
|
72
|
+
if (duration) el.style.removeProperty('--animate-duration');
|
|
73
|
+
if (delay) el.style.removeProperty('--animate-delay');
|
|
74
|
+
if (repeat != null) el.style.removeProperty('--animate-repeat');
|
|
75
|
+
resolve(true);
|
|
76
|
+
};
|
|
77
|
+
el.addEventListener('animationend', onEnd, { once: true });
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function animateSelector(selector, name, opts = {}) {
|
|
82
|
+
if (!hasDom()) return Promise.resolve(false);
|
|
83
|
+
const el = document.querySelector(selector);
|
|
84
|
+
return animateElement(el, name, opts);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function defaultEffectFor(el) {
|
|
88
|
+
const tag = String(el.tagName || '').toLowerCase();
|
|
89
|
+
if (tag === 'aside') return 'fadeInLeft';
|
|
90
|
+
if (tag === 'header') return 'fadeInDown';
|
|
91
|
+
if (tag === 'footer') return 'fadeInUp';
|
|
92
|
+
if (tag === 'main' || tag === 'section' || tag === 'article') return 'fadeIn';
|
|
93
|
+
if (tag === 'a' || tag === 'button' || tag === 'input' || tag === 'textarea' || tag === 'select') return 'fadeInUp';
|
|
94
|
+
return 'fadeIn';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function animateTree(root, {
|
|
98
|
+
selector = '*',
|
|
99
|
+
baseDelayMs = 14,
|
|
100
|
+
duration = 'var(--motion-base)'
|
|
101
|
+
} = {}) {
|
|
102
|
+
if (!hasDom() || !root || shouldReduceMotion()) return 0;
|
|
103
|
+
|
|
104
|
+
const nodes = Array.from(root.querySelectorAll(selector));
|
|
105
|
+
let animated = 0;
|
|
106
|
+
|
|
107
|
+
nodes.forEach((el) => {
|
|
108
|
+
if (!el || !el.classList) return;
|
|
109
|
+
if (el.matches('script,style,link,meta,title')) return;
|
|
110
|
+
if (el.hasAttribute('data-no-motion')) return;
|
|
111
|
+
if (el.dataset.motionApplied === '1') return;
|
|
112
|
+
|
|
113
|
+
const effect = el.dataset.motionEffect || defaultEffectFor(el);
|
|
114
|
+
el.classList.add('animate__animated', `animate__${effect}`);
|
|
115
|
+
el.style.setProperty('--animate-duration', duration);
|
|
116
|
+
el.style.setProperty('--animate-delay', `${animated * baseDelayMs}ms`);
|
|
117
|
+
el.dataset.motionApplied = '1';
|
|
118
|
+
animated += 1;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return animated;
|
|
122
|
+
}
|