anentrypoint-design 0.0.204 → 0.0.206
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/app-shell.css +81 -37
- package/dist/247420.css +81 -37
- package/dist/247420.js +12 -12
- package/package.json +1 -1
- package/src/components/community.js +8 -1
- package/src/components/content.js +6 -2
- package/src/kits/os/freddie/pages-tools.js +1 -1
- package/src/markdown.js +10 -0
- package/src/markdown-cache-perf-test.js +0 -173
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.206",
|
|
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",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as webjsx from '../../vendor/webjsx/index.js';
|
|
4
4
|
import { Icon } from './shell.js';
|
|
5
|
+
import { sanitizeHtml } from '../markdown.js';
|
|
5
6
|
const h = webjsx.createElement;
|
|
6
7
|
|
|
7
8
|
// Clamp a count to a compact badge string (matches the rail's 99+ convention),
|
|
@@ -391,7 +392,13 @@ export function PageView({ title = '', html = '', isAdmin = false, onEdit } = {}
|
|
|
391
392
|
),
|
|
392
393
|
h('div', {
|
|
393
394
|
class: 'cm-page-body',
|
|
394
|
-
|
|
395
|
+
// Page bodies are host/user-authored HTML, so they pass through the
|
|
396
|
+
// DOMPurify gate before innerHTML — never injected raw (stored-XSS gate).
|
|
397
|
+
ref: (el) => {
|
|
398
|
+
if (!el) return;
|
|
399
|
+
if (!html) { el.innerHTML = '<p class="cm-page-empty">This page is empty.</p>'; return; }
|
|
400
|
+
sanitizeHtml(html).then((clean) => { el.innerHTML = clean; });
|
|
401
|
+
}
|
|
395
402
|
})
|
|
396
403
|
);
|
|
397
404
|
}
|
|
@@ -53,7 +53,11 @@ export function Row({ code, rank, title, sub, meta, active, state = 'default', o
|
|
|
53
53
|
const isLink = kind === 'link' || (href != null && !onClick);
|
|
54
54
|
const isButton = !isLink && !!onClick;
|
|
55
55
|
const stateCls = state === 'disabled' ? ' row-state-disabled' : (state === 'error' ? ' row-state-error' : '');
|
|
56
|
-
|
|
56
|
+
// With no leading/code, the title would otherwise land in the narrow code
|
|
57
|
+
// column and wrap; `row-nocode` collapses that column so the title gets the
|
|
58
|
+
// full width (meta still pinned right).
|
|
59
|
+
const noLead = codeVal == null && leading == null;
|
|
60
|
+
const cls = 'row' + (isActive ? ' active' : '') + stateCls + (cols ? ' row-grid' : '') + (noLead && !cols ? ' row-nocode' : '') + (rail ? ' rail-' + rail : '');
|
|
57
61
|
const isDisabled = state === 'disabled';
|
|
58
62
|
const props = { key, class: cls, style: cols ? `${style ? style + ';' : ''}grid-template-columns:${cols}` : style };
|
|
59
63
|
if (isLink) {
|
|
@@ -168,7 +172,7 @@ export function WorksList({ works = [], openedIndex = -1, onToggle }) {
|
|
|
168
172
|
// Expand affordance: a chevron icon (down when open, right when
|
|
169
173
|
// collapsed) separated from the meta text by a CSS gap, not a
|
|
170
174
|
// literal +/- with a double-space.
|
|
171
|
-
meta: h('span', { class: 'ds-works-meta'
|
|
175
|
+
meta: h('span', { class: 'ds-works-meta' },
|
|
172
176
|
w.meta != null ? h('span', {}, w.meta) : null,
|
|
173
177
|
Icon(isOpen ? 'chevron-down' : 'chevron-right')),
|
|
174
178
|
active: isOpen,
|
|
@@ -139,7 +139,7 @@ export function makeToolsPages(ctx) {
|
|
|
139
139
|
const out = h('div', { id: 'fd-batch-out' });
|
|
140
140
|
const root = ctx.root;
|
|
141
141
|
return [
|
|
142
|
-
Section({ title: '
|
|
142
|
+
Section({ title: 'batch runner', children: [
|
|
143
143
|
Panel({ title: 'run prompts', children: form({
|
|
144
144
|
fields: [{ name: 'prompts', kind: 'textarea', placeholder: 'one prompt per line' }, { name: 'concurrency', type: 'number', value: '4' }],
|
|
145
145
|
submit: 'run',
|
package/src/markdown.js
CHANGED
|
@@ -52,3 +52,13 @@ export async function renderMarkdown(src) {
|
|
|
52
52
|
const raw = _marked.parse(String(src));
|
|
53
53
|
return _purify.sanitize(raw);
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
// Sanitize already-rendered HTML before it touches innerHTML. For any surface
|
|
57
|
+
// that injects host/user-authored HTML (e.g. a wiki page body), this is the
|
|
58
|
+
// single XSS gate — DOMPurify strips scripts/handlers. If the purifier hasn't
|
|
59
|
+
// loaded, we safe-fail by escaping (raw tags show as text, never execute).
|
|
60
|
+
export async function sanitizeHtml(html) {
|
|
61
|
+
const ok = await ensureReady();
|
|
62
|
+
if (!ok) return escapeHtml(html);
|
|
63
|
+
return _purify.sanitize(String(html));
|
|
64
|
+
}
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
// Performance test harness for markdown & Prism cache optimization.
|
|
2
|
-
// Measures load times, render performance, and verifies correctness.
|
|
3
|
-
// Run: node src/markdown-cache-perf-test.js
|
|
4
|
-
|
|
5
|
-
import { renderMarkdownCached, highlightCodeBlockCached, initializeCachesEagerly, getCacheStats, resetCacheState } from './markdown-cache.js';
|
|
6
|
-
|
|
7
|
-
const SAMPLE_MARKDOWN = `
|
|
8
|
-
# Hello World
|
|
9
|
-
|
|
10
|
-
This is **bold** and *italic* text with \`inline code\`.
|
|
11
|
-
|
|
12
|
-
[Link example](https://example.com)
|
|
13
|
-
|
|
14
|
-
## Code Block Example
|
|
15
|
-
|
|
16
|
-
\`\`\`python
|
|
17
|
-
def hello():
|
|
18
|
-
print("World")
|
|
19
|
-
\`\`\`
|
|
20
|
-
|
|
21
|
-
> Blockquote example
|
|
22
|
-
|
|
23
|
-
- List item 1
|
|
24
|
-
- List item 2
|
|
25
|
-
- List item 3
|
|
26
|
-
`;
|
|
27
|
-
|
|
28
|
-
const CODE_SAMPLES = [
|
|
29
|
-
{ lang: 'javascript', code: 'const x = 42; console.log(x);' },
|
|
30
|
-
{ lang: 'python', code: 'def fib(n):\n return n if n < 2 else fib(n-1) + fib(n-2)' },
|
|
31
|
-
{ lang: 'bash', code: 'echo "Hello World" | grep -i hello' },
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
async function measureInitialization() {
|
|
35
|
-
console.log('\n=== Initialization Performance ===');
|
|
36
|
-
resetCacheState();
|
|
37
|
-
|
|
38
|
-
const startTotal = performance.now();
|
|
39
|
-
await initializeCachesEagerly();
|
|
40
|
-
const totalMs = performance.now() - startTotal;
|
|
41
|
-
|
|
42
|
-
const stats = getCacheStats();
|
|
43
|
-
console.log(`Total init time: ${totalMs.toFixed(2)}ms`);
|
|
44
|
-
console.log(` - Markdown init: ${stats.initMs.markdown.toFixed(2)}ms`);
|
|
45
|
-
console.log(` - Prism init: ${stats.initMs.prism.toFixed(2)}ms`);
|
|
46
|
-
console.log(`Status: Markdown=${stats.markdownInitialized}, Prism=${stats.prismInitialized}`);
|
|
47
|
-
|
|
48
|
-
// Second call should be instant (cached)
|
|
49
|
-
const t0 = performance.now();
|
|
50
|
-
await initializeCachesEagerly();
|
|
51
|
-
const cacheHitMs = performance.now() - t0;
|
|
52
|
-
console.log(`Second init (cached): ${cacheHitMs.toFixed(2)}ms`);
|
|
53
|
-
|
|
54
|
-
return stats;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function measureMarkdownRendering() {
|
|
58
|
-
console.log('\n=== Markdown Rendering Performance ===');
|
|
59
|
-
|
|
60
|
-
// Warm up
|
|
61
|
-
await renderMarkdownCached(SAMPLE_MARKDOWN);
|
|
62
|
-
|
|
63
|
-
// Measure 10 renders
|
|
64
|
-
const times = [];
|
|
65
|
-
for (let i = 0; i < 10; i++) {
|
|
66
|
-
const t0 = performance.now();
|
|
67
|
-
await renderMarkdownCached(SAMPLE_MARKDOWN);
|
|
68
|
-
times.push(performance.now() - t0);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const avg = times.reduce((a, b) => a + b, 0) / times.length;
|
|
72
|
-
const min = Math.min(...times);
|
|
73
|
-
const max = Math.max(...times);
|
|
74
|
-
|
|
75
|
-
console.log(`Rendered markdown 10x:`);
|
|
76
|
-
console.log(` - Average: ${avg.toFixed(2)}ms`);
|
|
77
|
-
console.log(` - Min: ${min.toFixed(2)}ms`);
|
|
78
|
-
console.log(` - Max: ${max.toFixed(2)}ms`);
|
|
79
|
-
console.log(`Sample times: [${times.map(t => t.toFixed(1)).join(', ')}]ms`);
|
|
80
|
-
|
|
81
|
-
return { avg, min, max, times };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function measureSyntaxHighlighting() {
|
|
85
|
-
console.log('\n=== Syntax Highlighting Performance ===');
|
|
86
|
-
|
|
87
|
-
const results = [];
|
|
88
|
-
|
|
89
|
-
for (const sample of CODE_SAMPLES) {
|
|
90
|
-
// Create a minimal mock element (note: in browser, this would be real DOM)
|
|
91
|
-
const mockEl = { querySelectorAll: () => [] };
|
|
92
|
-
|
|
93
|
-
// Measure highlight operation
|
|
94
|
-
const t0 = performance.now();
|
|
95
|
-
await highlightCodeBlockCached(mockEl);
|
|
96
|
-
const ms = performance.now() - t0;
|
|
97
|
-
|
|
98
|
-
console.log(`${sample.lang}: ${ms.toFixed(2)}ms`);
|
|
99
|
-
results.push({ lang: sample.lang, ms });
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return results;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async function measureMultipleMessages() {
|
|
106
|
-
console.log('\n=== Simulated Message Stream (10 messages) ===');
|
|
107
|
-
|
|
108
|
-
const times = [];
|
|
109
|
-
let totalMs = 0;
|
|
110
|
-
|
|
111
|
-
for (let i = 0; i < 10; i++) {
|
|
112
|
-
const t0 = performance.now();
|
|
113
|
-
await renderMarkdownCached(SAMPLE_MARKDOWN);
|
|
114
|
-
const ms = performance.now() - t0;
|
|
115
|
-
times.push(ms);
|
|
116
|
-
totalMs += ms;
|
|
117
|
-
process.stdout.write(`Message ${i + 1}: ${ms.toFixed(1)}ms\n`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
console.log(`\nStream Stats:`);
|
|
121
|
-
console.log(` - Total: ${totalMs.toFixed(2)}ms`);
|
|
122
|
-
console.log(` - Average: ${(totalMs / times.length).toFixed(2)}ms`);
|
|
123
|
-
console.log(` - First message overhead: ${times[0].toFixed(2)}ms`);
|
|
124
|
-
console.log(` - Steady state average: ${(times.slice(1).reduce((a, b) => a + b, 0) / (times.length - 1)).toFixed(2)}ms`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async function runAllTests() {
|
|
128
|
-
console.log('[247420] Markdown & Prism Cache Performance Test Suite');
|
|
129
|
-
console.log('='.repeat(50));
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
// Test 1: Initialization
|
|
133
|
-
const initStats = await measureInitialization();
|
|
134
|
-
|
|
135
|
-
// Test 2: Markdown rendering
|
|
136
|
-
const mdStats = await measureMarkdownRendering();
|
|
137
|
-
|
|
138
|
-
// Test 3: Syntax highlighting
|
|
139
|
-
const syntaxStats = await measureSyntaxHighlighting();
|
|
140
|
-
|
|
141
|
-
// Test 4: Multi-message stream
|
|
142
|
-
await measureMultipleMessages();
|
|
143
|
-
|
|
144
|
-
// Final cache stats
|
|
145
|
-
console.log('\n=== Final Cache Statistics ===');
|
|
146
|
-
const finalStats = getCacheStats();
|
|
147
|
-
console.log(`Total messages rendered: ${finalStats.renderStats.count}`);
|
|
148
|
-
console.log(`Average render time: ${finalStats.renderStats.avgTimeMs}ms`);
|
|
149
|
-
console.log(`Min/Max render time: ${finalStats.renderStats.minTimeMs}ms / ${finalStats.renderStats.maxTimeMs}ms`);
|
|
150
|
-
|
|
151
|
-
// Summary
|
|
152
|
-
console.log('\n=== Summary ===');
|
|
153
|
-
console.log(`Library init (one-time): ${initStats.initMs.markdown + initStats.initMs.prism}ms`);
|
|
154
|
-
console.log(`Markdown render: ~${mdStats.avg.toFixed(1)}ms per message (cached)`);
|
|
155
|
-
console.log(`Cache hit on subsequent init: 0ms (verified)`);
|
|
156
|
-
console.log(`Estimated time for 100 messages without cache: ~10000+ms`);
|
|
157
|
-
console.log(`Estimated time for 100 messages with cache: ~${(mdStats.avg * 100 + initStats.initMs.markdown + initStats.initMs.prism).toFixed(0)}ms`);
|
|
158
|
-
const speedup = (10000 + initStats.initMs.markdown + initStats.initMs.prism) / (mdStats.avg * 100 + initStats.initMs.markdown + initStats.initMs.prism);
|
|
159
|
-
console.log(`Performance improvement: ~${speedup.toFixed(1)}x faster`);
|
|
160
|
-
|
|
161
|
-
console.log('\nPASS All tests completed successfully');
|
|
162
|
-
} catch (err) {
|
|
163
|
-
console.error('\nFAIL Test failed:', err.message);
|
|
164
|
-
console.error(err.stack);
|
|
165
|
-
process.exit(1);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Run tests
|
|
170
|
-
runAllTests().then(() => process.exit(0)).catch(err => {
|
|
171
|
-
console.error('Fatal error:', err);
|
|
172
|
-
process.exit(1);
|
|
173
|
-
});
|