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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anentrypoint-design",
3
- "version": "0.0.10",
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) => { navigator.clipboard?.writeText(cmd); state.copied = true; render(); setTimeout(() => { state.copied = false; render(); }, 1200); }
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) => { state.opened = i; render(); },
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' + (side ? '' : ' no-side') },
96
- side || null,
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:12px' }, e.ver),
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', { style: 'padding:14px 20px 18px 86px;background:var(--panel-2);color:var(--panel-text);font-size:13px;line-height:1.6' },
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 = () => webjsx.applyDiff(rootEl, viewFn(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
+ }