@anthusai/papyrus 1.0.0-next.2 → 1.0.0-next.3
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/amplify/data/resource.js +26 -0
- package/amplify/data/resource.ts +30 -0
- package/amplify/data/schema.js +26 -0
- package/amplify/data/schema.ts +30 -0
- package/amplify/functions/content-actions/handler.py +107 -0
- package/amplify/functions/content-actions/requirements.txt +4 -0
- package/amplify/functions/content-actions/resource.js +40 -0
- package/amplify/functions/content-actions/resource.ts +54 -0
- package/amplify/functions/shared/python-bundle.js +17 -2
- package/amplify/functions/shared/python-bundle.ts +23 -2
- package/amplify/site-backend.js +10 -0
- package/amplify/site-backend.ts +15 -0
- package/app/[year]/[month]/[day]/[articleSlug]/page.tsx +2 -2
- package/app/globals.css +1 -4
- package/app/layout.tsx +4 -12
- package/components/news-desk-page.tsx +1 -4
- package/lib/content-actions-client.ts +98 -0
- package/lib/empty-theme.css +1 -0
- package/lib/layout-scenarios.ts +8 -8
- package/lib/site-brand-tenant.ts +3 -22
- package/lib/site-brand.ts +44 -42
- package/middleware.ts +10 -35
- package/package.json +2 -1
- package/renderers/pretext/article-page.tsx +1 -6
- package/renderers/pretext/presentation-shell.tsx +2 -9
- package/routes.manifest.json +1 -1
- package/src/with-papyrus.mjs +15 -2
- package/publications/anth_us/brand.ts +0 -68
- package/publications/anth_us/theme.css +0 -66
- package/publications/pilobol_us/brand.ts +0 -37
- package/publications/pilobol_us/theme.css +0 -140
- package/publications/threat_intelligence/blog-defense/graph.ts +0 -636
- package/publications/threat_intelligence/blog-defense/layout.ts +0 -934
- package/publications/threat_intelligence/blog-defense/page-background.tsx +0 -590
- package/publications/threat_intelligence/brand.ts +0 -40
- package/publications/threat_intelligence/pictograms/art.tsx +0 -608
- package/publications/threat_intelligence/pictograms/figure.tsx +0 -82
- package/publications/threat_intelligence/pictograms/registry.ts +0 -30
- package/publications/threat_intelligence/pictograms/system.tsx +0 -305
- package/publications/threat_intelligence/seed/seed-edition-content.json +0 -629
- package/publications/threat_intelligence/theme.css +0 -1440
- /package/{publications/papyrus/theme.css → app/papyrus-theme.css} +0 -0
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export const PICTOGRAM_CYCLE_MS = 20_000;
|
|
2
|
-
export const PICTOGRAM_NODE_RADIUS = 4.75;
|
|
3
|
-
export const PICTOGRAM_EDGE_WIDTH = 2.5;
|
|
4
|
-
|
|
5
|
-
export const THREAT_INTELLIGENCE_PICTOGRAM_PHASE_OFFSETS = {
|
|
6
|
-
hero: 1_400,
|
|
7
|
-
"the-balance-of-power-is-shifting": 0,
|
|
8
|
-
"how-our-newsroom-learns": 2_600,
|
|
9
|
-
"audit-aws-exposure-before-attackers-do": 5_000,
|
|
10
|
-
"audit-azure-blast-radius-before-attackers-do": 7_500,
|
|
11
|
-
"treat-openai-accounts-like-production-infrastructure": 10_000,
|
|
12
|
-
"how-to-play-games-securely": 12_500,
|
|
13
|
-
} as const;
|
|
14
|
-
|
|
15
|
-
export type ThreatIntelligencePictogramSlug = Exclude<
|
|
16
|
-
keyof typeof THREAT_INTELLIGENCE_PICTOGRAM_PHASE_OFFSETS,
|
|
17
|
-
"hero"
|
|
18
|
-
>;
|
|
19
|
-
|
|
20
|
-
export const THREAT_INTELLIGENCE_PICTOGRAM_SLUGS = Object.keys(THREAT_INTELLIGENCE_PICTOGRAM_PHASE_OFFSETS).filter(
|
|
21
|
-
(key): key is ThreatIntelligencePictogramSlug => key !== "hero",
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
export function isThreatIntelligencePictogramSlug(slug: string): slug is ThreatIntelligencePictogramSlug {
|
|
25
|
-
return THREAT_INTELLIGENCE_PICTOGRAM_SLUGS.includes(slug as ThreatIntelligencePictogramSlug);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function getThreatIntelligencePictogramPhaseOffset(slug: ThreatIntelligencePictogramSlug): number {
|
|
29
|
-
return THREAT_INTELLIGENCE_PICTOGRAM_PHASE_OFFSETS[slug];
|
|
30
|
-
}
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { motion, useReducedMotion } from "motion/react";
|
|
4
|
-
import { useEffect, useMemo, useState, type CSSProperties, type ReactNode } from "react";
|
|
5
|
-
import { PICTOGRAM_CYCLE_MS, PICTOGRAM_EDGE_WIDTH, PICTOGRAM_NODE_RADIUS } from "./registry";
|
|
6
|
-
|
|
7
|
-
export type PictogramPalette = {
|
|
8
|
-
frame: string;
|
|
9
|
-
edge: string;
|
|
10
|
-
node: string;
|
|
11
|
-
muted: string;
|
|
12
|
-
accent: string;
|
|
13
|
-
accentSoft: string;
|
|
14
|
-
accentGlow: string;
|
|
15
|
-
safe: string;
|
|
16
|
-
warning: string;
|
|
17
|
-
barrier: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export const PICTOGRAM_PALETTE: PictogramPalette = {
|
|
21
|
-
frame: "var(--background)",
|
|
22
|
-
edge: "var(--ti-pictogram-edge)",
|
|
23
|
-
node: "var(--ti-pictogram-node)",
|
|
24
|
-
muted: "var(--ti-pictogram-muted)",
|
|
25
|
-
accent: "var(--ti-pictogram-compromised)",
|
|
26
|
-
accentSoft: "var(--ti-pictogram-throb)",
|
|
27
|
-
accentGlow: "var(--ti-pictogram-accent-glow)",
|
|
28
|
-
safe: "var(--grass-8)",
|
|
29
|
-
warning: "var(--amber-8)",
|
|
30
|
-
barrier: "var(--sand-8)",
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export type PictogramMotion = {
|
|
34
|
-
cycleS: number;
|
|
35
|
-
phase: number;
|
|
36
|
-
prefersReducedMotion: boolean;
|
|
37
|
-
delayS: (offsetMs?: number) => number;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export function usePictogramMotion(phaseOffsetMs: number): PictogramMotion {
|
|
41
|
-
const prefersReducedMotion = useReducedMotion() ?? false;
|
|
42
|
-
const [epochMs, setEpochMs] = useState(0);
|
|
43
|
-
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
setEpochMs(Date.now() % PICTOGRAM_CYCLE_MS);
|
|
46
|
-
}, []);
|
|
47
|
-
|
|
48
|
-
return useMemo(() => {
|
|
49
|
-
const cycleS = PICTOGRAM_CYCLE_MS / 1000;
|
|
50
|
-
const phase = ((epochMs + phaseOffsetMs) % PICTOGRAM_CYCLE_MS) / PICTOGRAM_CYCLE_MS;
|
|
51
|
-
return {
|
|
52
|
-
cycleS,
|
|
53
|
-
phase,
|
|
54
|
-
prefersReducedMotion,
|
|
55
|
-
delayS: (offsetMs = 0) =>
|
|
56
|
-
prefersReducedMotion ? 0 : -(((epochMs + phaseOffsetMs + offsetMs) % PICTOGRAM_CYCLE_MS) / 1000),
|
|
57
|
-
};
|
|
58
|
-
}, [epochMs, phaseOffsetMs, prefersReducedMotion]);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function PictogramFrame({
|
|
62
|
-
aspectRatio = 1,
|
|
63
|
-
children,
|
|
64
|
-
}: {
|
|
65
|
-
aspectRatio?: number;
|
|
66
|
-
children: ReactNode;
|
|
67
|
-
}) {
|
|
68
|
-
return (
|
|
69
|
-
<div className="pictogram-figure__frame" style={{ "--pictogram-aspect-ratio": String(aspectRatio) } as CSSProperties}>
|
|
70
|
-
{children}
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function PictogramSvg({
|
|
76
|
-
alt,
|
|
77
|
-
children,
|
|
78
|
-
viewBox = "0 0 320 220",
|
|
79
|
-
}: {
|
|
80
|
-
alt: string;
|
|
81
|
-
children: ReactNode;
|
|
82
|
-
viewBox?: string;
|
|
83
|
-
}) {
|
|
84
|
-
return (
|
|
85
|
-
<svg aria-label={alt} className="pictogram-figure__svg" role="img" viewBox={viewBox}>
|
|
86
|
-
<title>{alt}</title>
|
|
87
|
-
{children}
|
|
88
|
-
</svg>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function Line({
|
|
93
|
-
d,
|
|
94
|
-
palette,
|
|
95
|
-
tone = "edge",
|
|
96
|
-
width = PICTOGRAM_EDGE_WIDTH,
|
|
97
|
-
opacity = 1,
|
|
98
|
-
dashArray,
|
|
99
|
-
dashOffset,
|
|
100
|
-
}: {
|
|
101
|
-
d: string;
|
|
102
|
-
palette: PictogramPalette;
|
|
103
|
-
tone?: "edge" | "muted" | "accent" | "safe" | "warning" | "barrier";
|
|
104
|
-
width?: number;
|
|
105
|
-
opacity?: number;
|
|
106
|
-
dashArray?: string;
|
|
107
|
-
dashOffset?: number;
|
|
108
|
-
}) {
|
|
109
|
-
return (
|
|
110
|
-
<path
|
|
111
|
-
d={d}
|
|
112
|
-
fill="none"
|
|
113
|
-
opacity={opacity}
|
|
114
|
-
stroke={resolveTone(palette, tone)}
|
|
115
|
-
strokeDasharray={dashArray}
|
|
116
|
-
strokeDashoffset={dashOffset}
|
|
117
|
-
strokeLinecap="round"
|
|
118
|
-
strokeLinejoin="round"
|
|
119
|
-
strokeWidth={width}
|
|
120
|
-
/>
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function MotionLine({
|
|
125
|
-
animate,
|
|
126
|
-
d,
|
|
127
|
-
palette,
|
|
128
|
-
tone = "edge",
|
|
129
|
-
transition,
|
|
130
|
-
width = PICTOGRAM_EDGE_WIDTH,
|
|
131
|
-
opacity = 1,
|
|
132
|
-
dashArray,
|
|
133
|
-
}: {
|
|
134
|
-
animate: any;
|
|
135
|
-
d: string;
|
|
136
|
-
palette: PictogramPalette;
|
|
137
|
-
tone?: "edge" | "muted" | "accent" | "safe" | "warning" | "barrier";
|
|
138
|
-
transition: any;
|
|
139
|
-
width?: number;
|
|
140
|
-
opacity?: number;
|
|
141
|
-
dashArray?: string;
|
|
142
|
-
}) {
|
|
143
|
-
return (
|
|
144
|
-
<motion.path
|
|
145
|
-
animate={animate}
|
|
146
|
-
d={d}
|
|
147
|
-
fill="none"
|
|
148
|
-
initial={false}
|
|
149
|
-
opacity={opacity}
|
|
150
|
-
stroke={resolveTone(palette, tone)}
|
|
151
|
-
strokeDasharray={dashArray}
|
|
152
|
-
strokeLinecap="round"
|
|
153
|
-
strokeLinejoin="round"
|
|
154
|
-
strokeWidth={width}
|
|
155
|
-
transition={transition}
|
|
156
|
-
/>
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function Node({
|
|
161
|
-
glow = false,
|
|
162
|
-
palette,
|
|
163
|
-
r = PICTOGRAM_NODE_RADIUS,
|
|
164
|
-
tone = "node",
|
|
165
|
-
x,
|
|
166
|
-
y,
|
|
167
|
-
}: {
|
|
168
|
-
glow?: boolean;
|
|
169
|
-
palette: PictogramPalette;
|
|
170
|
-
r?: number;
|
|
171
|
-
tone?: "node" | "muted" | "accent" | "safe" | "warning";
|
|
172
|
-
x: number;
|
|
173
|
-
y: number;
|
|
174
|
-
}) {
|
|
175
|
-
const fill = resolveNodeTone(palette, tone);
|
|
176
|
-
return (
|
|
177
|
-
<g>
|
|
178
|
-
{glow ? <circle cx={x} cy={y} fill={palette.accentGlow} r={r * 2.4} /> : null}
|
|
179
|
-
<circle cx={x} cy={y} fill={fill} r={r} />
|
|
180
|
-
</g>
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function MotionNode({
|
|
185
|
-
animate,
|
|
186
|
-
glow = false,
|
|
187
|
-
palette,
|
|
188
|
-
r = PICTOGRAM_NODE_RADIUS,
|
|
189
|
-
tone = "node",
|
|
190
|
-
transition,
|
|
191
|
-
x,
|
|
192
|
-
y,
|
|
193
|
-
}: {
|
|
194
|
-
animate: any;
|
|
195
|
-
glow?: boolean;
|
|
196
|
-
palette: PictogramPalette;
|
|
197
|
-
r?: number;
|
|
198
|
-
tone?: "node" | "muted" | "accent" | "safe" | "warning";
|
|
199
|
-
transition: any;
|
|
200
|
-
x: number;
|
|
201
|
-
y: number;
|
|
202
|
-
}) {
|
|
203
|
-
const fill = resolveNodeTone(palette, tone);
|
|
204
|
-
return (
|
|
205
|
-
<g>
|
|
206
|
-
{glow ? (
|
|
207
|
-
<motion.circle animate={animate} cx={x} cy={y} fill={palette.accentGlow} initial={false} r={r * 2.4} transition={transition} />
|
|
208
|
-
) : null}
|
|
209
|
-
<motion.circle animate={animate} cx={x} cy={y} fill={fill} initial={false} r={r} transition={transition} />
|
|
210
|
-
</g>
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export function Box({
|
|
215
|
-
fillOpacity = 0.08,
|
|
216
|
-
height,
|
|
217
|
-
palette,
|
|
218
|
-
strokeTone = "edge",
|
|
219
|
-
width,
|
|
220
|
-
x,
|
|
221
|
-
y,
|
|
222
|
-
}: {
|
|
223
|
-
fillOpacity?: number;
|
|
224
|
-
height: number;
|
|
225
|
-
palette: PictogramPalette;
|
|
226
|
-
strokeTone?: "edge" | "muted" | "accent" | "safe" | "warning" | "barrier";
|
|
227
|
-
width: number;
|
|
228
|
-
x: number;
|
|
229
|
-
y: number;
|
|
230
|
-
}) {
|
|
231
|
-
const stroke = resolveTone(palette, strokeTone);
|
|
232
|
-
return (
|
|
233
|
-
<rect
|
|
234
|
-
fill={stroke}
|
|
235
|
-
fillOpacity={fillOpacity}
|
|
236
|
-
height={height}
|
|
237
|
-
rx={14}
|
|
238
|
-
stroke={stroke}
|
|
239
|
-
strokeWidth={PICTOGRAM_EDGE_WIDTH}
|
|
240
|
-
width={width}
|
|
241
|
-
x={x}
|
|
242
|
-
y={y}
|
|
243
|
-
/>
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export function arrowPath(x1: number, y1: number, x2: number, y2: number, arrowSize = 9): string {
|
|
248
|
-
const dx = x2 - x1;
|
|
249
|
-
const dy = y2 - y1;
|
|
250
|
-
const length = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
251
|
-
const ux = dx / length;
|
|
252
|
-
const uy = dy / length;
|
|
253
|
-
const px = -uy;
|
|
254
|
-
const py = ux;
|
|
255
|
-
const bx = x2 - ux * arrowSize;
|
|
256
|
-
const by = y2 - uy * arrowSize;
|
|
257
|
-
const leftX = bx + px * (arrowSize * 0.55);
|
|
258
|
-
const leftY = by + py * (arrowSize * 0.55);
|
|
259
|
-
const rightX = bx - px * (arrowSize * 0.55);
|
|
260
|
-
const rightY = by - py * (arrowSize * 0.55);
|
|
261
|
-
return `M ${x1} ${y1} L ${x2} ${y2} M ${leftX} ${leftY} L ${x2} ${y2} L ${rightX} ${rightY}`;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export function pointsPath(points: Array<[number, number]>): string {
|
|
265
|
-
return points.map(([x, y], index) => `${index === 0 ? "M" : "L"} ${x} ${y}`).join(" ");
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export function ringNodes(cx: number, cy: number, radius: number, count: number, startAngle = -90): Array<[number, number]> {
|
|
269
|
-
return Array.from({ length: count }, (_, index) => {
|
|
270
|
-
const angle = ((startAngle + (360 / count) * index) * Math.PI) / 180;
|
|
271
|
-
return [cx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius];
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function resolveTone(palette: PictogramPalette, tone: "edge" | "muted" | "accent" | "safe" | "warning" | "barrier"): string {
|
|
276
|
-
switch (tone) {
|
|
277
|
-
case "muted":
|
|
278
|
-
return palette.muted;
|
|
279
|
-
case "accent":
|
|
280
|
-
return palette.accent;
|
|
281
|
-
case "safe":
|
|
282
|
-
return palette.safe;
|
|
283
|
-
case "warning":
|
|
284
|
-
return palette.warning;
|
|
285
|
-
case "barrier":
|
|
286
|
-
return palette.barrier;
|
|
287
|
-
default:
|
|
288
|
-
return palette.edge;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function resolveNodeTone(palette: PictogramPalette, tone: "node" | "muted" | "accent" | "safe" | "warning"): string {
|
|
293
|
-
switch (tone) {
|
|
294
|
-
case "muted":
|
|
295
|
-
return palette.muted;
|
|
296
|
-
case "accent":
|
|
297
|
-
return palette.accent;
|
|
298
|
-
case "safe":
|
|
299
|
-
return palette.safe;
|
|
300
|
-
case "warning":
|
|
301
|
-
return palette.warning;
|
|
302
|
-
default:
|
|
303
|
-
return palette.node;
|
|
304
|
-
}
|
|
305
|
-
}
|