create-atom.io 0.0.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.
Files changed (64) hide show
  1. package/bin/create-atom.bin.js +2 -0
  2. package/dist/create-atom-BV6WVRvY.js +102 -0
  3. package/dist/create-atom-BV6WVRvY.js.map +1 -0
  4. package/dist/create-atom.d.ts +14 -0
  5. package/dist/create-atom.d.ts.map +1 -0
  6. package/dist/create-atom.js +3 -0
  7. package/dist/create-atom.x.js +51 -0
  8. package/dist/create-atom.x.js.map +1 -0
  9. package/package.json +56 -0
  10. package/src/create-atom.ts +191 -0
  11. package/src/create-atom.x.ts +59 -0
  12. package/templates/base/_gitignore +24 -0
  13. package/templates/base/eslint.config.ts +178 -0
  14. package/templates/base/eslint.d.ts +31 -0
  15. package/templates/base/index.html +14 -0
  16. package/templates/base/node_modules/.bin/browserslist +21 -0
  17. package/templates/base/node_modules/.bin/eslint +21 -0
  18. package/templates/base/node_modules/.bin/jiti +21 -0
  19. package/templates/base/node_modules/.bin/sass +21 -0
  20. package/templates/base/node_modules/.bin/terser +21 -0
  21. package/templates/base/node_modules/.bin/tsc +21 -0
  22. package/templates/base/node_modules/.bin/tsserver +21 -0
  23. package/templates/base/node_modules/.bin/vite +21 -0
  24. package/templates/base/node_modules/.bin/yaml +21 -0
  25. package/templates/base/node_modules/.vite/deps/_metadata.json +82 -0
  26. package/templates/base/node_modules/.vite/deps/atom__io.js +72 -0
  27. package/templates/base/node_modules/.vite/deps/atom__io.js.map +7 -0
  28. package/templates/base/node_modules/.vite/deps/atom__io_react.js +170 -0
  29. package/templates/base/node_modules/.vite/deps/atom__io_react.js.map +7 -0
  30. package/templates/base/node_modules/.vite/deps/chunk-2PJG54YB.js +364 -0
  31. package/templates/base/node_modules/.vite/deps/chunk-2PJG54YB.js.map +7 -0
  32. package/templates/base/node_modules/.vite/deps/chunk-6VZTUEOV.js +3777 -0
  33. package/templates/base/node_modules/.vite/deps/chunk-6VZTUEOV.js.map +7 -0
  34. package/templates/base/node_modules/.vite/deps/chunk-IHAFLL3M.js +204 -0
  35. package/templates/base/node_modules/.vite/deps/chunk-IHAFLL3M.js.map +7 -0
  36. package/templates/base/node_modules/.vite/deps/chunk-LZJKCPXG.js +84 -0
  37. package/templates/base/node_modules/.vite/deps/chunk-LZJKCPXG.js.map +7 -0
  38. package/templates/base/node_modules/.vite/deps/chunk-PJQAIOAR.js +311 -0
  39. package/templates/base/node_modules/.vite/deps/chunk-PJQAIOAR.js.map +7 -0
  40. package/templates/base/node_modules/.vite/deps/chunk-XRJUZPUF.js +17 -0
  41. package/templates/base/node_modules/.vite/deps/chunk-XRJUZPUF.js.map +7 -0
  42. package/templates/base/node_modules/.vite/deps/package.json +3 -0
  43. package/templates/base/node_modules/.vite/deps/preact.js +27 -0
  44. package/templates/base/node_modules/.vite/deps/preact.js.map +7 -0
  45. package/templates/base/node_modules/.vite/deps/preact_compat.js +100 -0
  46. package/templates/base/node_modules/.vite/deps/preact_compat.js.map +7 -0
  47. package/templates/base/node_modules/.vite/deps/preact_debug.js +240 -0
  48. package/templates/base/node_modules/.vite/deps/preact_debug.js.map +7 -0
  49. package/templates/base/node_modules/.vite/deps/preact_devtools.js +7 -0
  50. package/templates/base/node_modules/.vite/deps/preact_devtools.js.map +7 -0
  51. package/templates/base/node_modules/.vite/deps/preact_hooks.js +29 -0
  52. package/templates/base/node_modules/.vite/deps/preact_hooks.js.map +7 -0
  53. package/templates/base/node_modules/.vite/deps/preact_jsx-dev-runtime.js +18 -0
  54. package/templates/base/node_modules/.vite/deps/preact_jsx-dev-runtime.js.map +7 -0
  55. package/templates/base/node_modules/.vite/deps/preact_jsx-runtime.js +18 -0
  56. package/templates/base/node_modules/.vite/deps/preact_jsx-runtime.js.map +7 -0
  57. package/templates/base/package.json +24 -0
  58. package/templates/base/public/preact.svg +6 -0
  59. package/templates/base/src/BezierPlayground.tsx +448 -0
  60. package/templates/base/src/index.tsx +48 -0
  61. package/templates/base/src/msg.md +105 -0
  62. package/templates/base/src/style.css +126 -0
  63. package/templates/base/tsconfig.json +21 -0
  64. package/templates/base/vite.config.ts +9 -0
@@ -0,0 +1,448 @@
1
+ import {
2
+ atom,
3
+ atomFamily,
4
+ getState,
5
+ Loadable,
6
+ resetState,
7
+ selectorFamily,
8
+ setState,
9
+ } from "atom.io"
10
+ import { type RegularAtomToken } from "atom.io"
11
+ import { useO } from "atom.io/react"
12
+ import { PointerEventHandler, TargetedPointerEvent } from "preact"
13
+ import { useRef, useCallback, useEffect, MutableRef } from "preact/hooks"
14
+
15
+ type PointXY = { x: number; y: number }
16
+ type EdgeXY = { c?: PointXY; s: PointXY }
17
+
18
+ const pathKeysAtom = atom<string[]>({
19
+ key: "pathKeys",
20
+ default: [],
21
+ })
22
+ const subpathKeysAtoms = atomFamily<string[], string>({
23
+ key: "subpathKeys",
24
+ default: [],
25
+ })
26
+ const nodeAtoms = atomFamily<PointXY | null, string>({
27
+ key: "nodeAtoms",
28
+ default: null,
29
+ })
30
+ const edgeAtoms = atomFamily<boolean | EdgeXY, string>({
31
+ key: "edgeAtoms",
32
+ default: true,
33
+ })
34
+ const pathDrawSelectors = selectorFamily<string, string>({
35
+ key: "pathDrawSelectors",
36
+ get:
37
+ (pathKey) =>
38
+ ({ get }) => {
39
+ const subpathKeys = get(subpathKeysAtoms, pathKey)
40
+ return subpathKeys
41
+ .map((subpathKey, idx) => {
42
+ const node = get(nodeAtoms, subpathKey)
43
+ const edge = get(edgeAtoms, subpathKey)
44
+
45
+ if (node === null) {
46
+ return "Z"
47
+ }
48
+ if (idx === 0) {
49
+ return `M ${node.x} ${node.y}`
50
+ }
51
+ if (edge === false) {
52
+ return `M ${node.x} ${node.y}`
53
+ }
54
+ if (edge === true) {
55
+ return `L ${node.x} ${node.y}`
56
+ }
57
+ if (`c` in edge) {
58
+ return `C ${edge.c.x} ${edge.c.y} ${edge.s.x} ${edge.s.y} ${node.x} ${node.y}`
59
+ }
60
+ return `S ${edge.s.x} ${edge.s.y} ${node.x} ${node.y}`
61
+ })
62
+ .join(" ")
63
+ },
64
+ })
65
+
66
+ export function useAtomicRef<T>(
67
+ token: RegularAtomToken<T | null>,
68
+ ): MutableRef<T | null> {
69
+ const ref = useRef<T | null>(null)
70
+ useEffect(() => {
71
+ setState(token, ref.current)
72
+ }, [token])
73
+ return ref
74
+ }
75
+
76
+ function clamp(n: number, min: number, max: number) {
77
+ return Math.max(min, Math.min(max, n))
78
+ }
79
+
80
+ function Bezier({
81
+ at,
82
+ subpathKey,
83
+ prevSubpathKey,
84
+ node: maybeNode,
85
+ }: {
86
+ at: PointXY
87
+ subpathKey: string
88
+ prevSubpathKey: string
89
+ node?: PointXY
90
+ }) {
91
+ let node: PointXY | null
92
+ switch (typeof maybeNode) {
93
+ case "undefined":
94
+ node = useO(nodeAtoms, prevSubpathKey)
95
+ break
96
+ default:
97
+ node = maybeNode
98
+ }
99
+ return node === null ? null : (
100
+ <>
101
+ <line
102
+ class="bezier"
103
+ x1={node.x}
104
+ y1={node.y}
105
+ x2={at.x}
106
+ y2={at.y}
107
+ stroke="#777"
108
+ stroke-width={1}
109
+ />
110
+ <circle
111
+ class="bezier"
112
+ fill="#777"
113
+ stroke="#aaa"
114
+ stroke-width={1}
115
+ cx={at.x}
116
+ cy={at.y}
117
+ r={2}
118
+ />
119
+ <circle
120
+ class="bezier-draggable"
121
+ fill="transparent"
122
+ cx={at.x}
123
+ cy={at.y}
124
+ r={6}
125
+ onPointerDown={(evt) => {
126
+ evt.currentTarget.setPointerCapture(evt.pointerId)
127
+ setState(currentlyDraggingAtom, {
128
+ key: subpathKey,
129
+ by: !maybeNode ? `c` : `s`,
130
+ })
131
+ }}
132
+ />
133
+ </>
134
+ )
135
+ }
136
+
137
+ function Node({
138
+ subpathKey,
139
+ nextSubpathKey,
140
+ }: {
141
+ subpathKey: string
142
+ nextSubpathKey: string
143
+ }) {
144
+ const node = useO(nodeAtoms, subpathKey)
145
+ const edge = useO(edgeAtoms, subpathKey)
146
+ return node === null ? null : (
147
+ <>
148
+ {typeof edge === "boolean" ? (
149
+ <rect class="node" x={node.x - 3} y={node.y - 3} width={6} height={6} />
150
+ ) : (
151
+ <>
152
+ <Bezier
153
+ at={edge.s}
154
+ node={node}
155
+ subpathKey={subpathKey}
156
+ prevSubpathKey={nextSubpathKey}
157
+ />
158
+ {edge.c ? (
159
+ <Bezier
160
+ at={edge.c}
161
+ subpathKey={subpathKey}
162
+ prevSubpathKey={nextSubpathKey}
163
+ />
164
+ ) : null}
165
+ <circle class="node" cx={node.x} cy={node.y} r={3} />
166
+ </>
167
+ )}
168
+ <circle
169
+ class="node-draggable"
170
+ fill="transparent"
171
+ cx={node.x}
172
+ cy={node.y}
173
+ r={10}
174
+ onPointerDown={(evt) => {
175
+ evt.currentTarget.setPointerCapture(evt.pointerId)
176
+ setState(currentlyDraggingAtom, { key: subpathKey })
177
+ }}
178
+ />
179
+ </>
180
+ )
181
+ }
182
+
183
+ function RenderedPath({ pathKey }: { pathKey: string }) {
184
+ const draw = useO(pathDrawSelectors, pathKey)
185
+ return <path d={`${draw} Z`} class="path" style={{ pointerEvents: "none" }} />
186
+ }
187
+
188
+ function Path({ pathKey }: { pathKey: string }) {
189
+ const subpathKeys = useO(subpathKeysAtoms, pathKey)
190
+ console.log(console.log(`render path`, subpathKeys))
191
+ return (
192
+ <>
193
+ <RenderedPath pathKey={pathKey} />
194
+ {subpathKeys.toReversed().map((spk, idx, arr) => (
195
+ <Node subpathKey={spk} nextSubpathKey={arr[idx + 1] ?? arr[0]} />
196
+ ))}
197
+ </>
198
+ )
199
+ }
200
+
201
+ function PathsDemo() {
202
+ const pathKeys = useO(pathKeysAtom)
203
+ return (
204
+ <>
205
+ {pathKeys.map((pathKey) => {
206
+ return <Path pathKey={pathKey} />
207
+ })}
208
+ </>
209
+ )
210
+ }
211
+
212
+ const svgRefAtom = atom<SVGSVGElement | null>({
213
+ key: "svgRef",
214
+ default: null,
215
+ })
216
+ const currentlyDraggingAtom = atom<null | { key: string; by?: `c` | `s` }>({
217
+ key: "currentlyDragging",
218
+ default: null,
219
+ })
220
+
221
+ function onPointerMove(evt: TargetedPointerEvent<SVGSVGElement>): void {
222
+ evt.preventDefault()
223
+ const { key: currentlyDragging, by: draggingBy } =
224
+ getState(currentlyDraggingAtom) ?? {}
225
+ const svg = getState(svgRefAtom)
226
+ if (!svg || !currentlyDragging) {
227
+ return
228
+ }
229
+ const pt = svg.createSVGPoint()
230
+ pt.x = evt.clientX
231
+ pt.y = evt.clientY
232
+ const ctm = svg.getScreenCTM()?.inverse()
233
+ const { x, y } = pt.matrixTransform(ctm)
234
+
235
+ switch (draggingBy) {
236
+ case undefined:
237
+ setState(nodeAtoms, currentlyDragging, {
238
+ x: clamp(x, -185, WIDTH + 185),
239
+ y: clamp(y, -10, HEIGHT + 10),
240
+ })
241
+ break
242
+ case `s`:
243
+ setState(edgeAtoms, currentlyDragging, (prev) => ({
244
+ ...(prev as EdgeXY),
245
+ s: { x: clamp(x, -185, WIDTH + 185), y: clamp(y, -10, HEIGHT + 10) },
246
+ }))
247
+ break
248
+ case `c`:
249
+ setState(edgeAtoms, currentlyDragging, (prev) => ({
250
+ ...(prev as EdgeXY),
251
+ c: { x: clamp(x, -185, WIDTH + 185), y: clamp(y, -10, HEIGHT + 10) },
252
+ }))
253
+ break
254
+ }
255
+ }
256
+
257
+ const CODES = [`m`, `M`, `l`, `L`, `c`, `C`, `v`, `V`, `z`, `Z`] as const
258
+
259
+ const preactLogoAtom = atom<Loadable<string>>({
260
+ key: "preactLogo",
261
+ default: () => fetch("preact.svg").then((res) => res.text()),
262
+ })
263
+
264
+ async function reset() {
265
+ const logo = await getState(preactLogoAtom)
266
+ for (const pathKey of getState(pathKeysAtom)) {
267
+ resetState(subpathKeysAtoms, pathKey)
268
+ }
269
+ resetState(pathKeysAtom)
270
+
271
+ const shapes = logo
272
+ .split("\n")
273
+ .filter((l) => l.startsWith("\t<path"))
274
+ .map((logo) => {
275
+ const raw = logo.split(`d="`)[1].slice(0, -9)
276
+
277
+ type Letter = (typeof CODES)[number]
278
+ let letter: Letter | undefined
279
+ let number = ``
280
+ let numbers: number[] = []
281
+
282
+ const instructions: { letter: Letter; numbers: number[] }[] = []
283
+ for (let i = 0; i < raw.length; i++) {
284
+ const c = raw[i]
285
+ if (CODES.includes(c as Letter)) {
286
+ if (number) {
287
+ numbers.push(Number.parseFloat(number))
288
+ number = ``
289
+ }
290
+ if (letter) {
291
+ instructions.push({ letter, numbers })
292
+ }
293
+ letter = c as Letter
294
+ numbers = []
295
+ continue
296
+ }
297
+ if (c === ` `) {
298
+ numbers.push(Number.parseFloat(number))
299
+ number = ``
300
+ continue
301
+ }
302
+ if (c === `-` && number) {
303
+ numbers.push(Number.parseFloat(number))
304
+ number = `-`
305
+ continue
306
+ }
307
+
308
+ number += c
309
+ }
310
+
311
+ let prev: PointXY = { x: 0, y: 0 }
312
+ const edgeNodes = instructions.map<{
313
+ node: null | PointXY
314
+ edge: boolean | { c?: PointXY; s: PointXY }
315
+ }>(({ letter, numbers }) => {
316
+ let node: null | PointXY
317
+ let edge: boolean | { c?: PointXY; s: PointXY }
318
+ switch (letter) {
319
+ case `m`:
320
+ node = { x: prev.x + numbers[0], y: prev.y + numbers[1] }
321
+ edge = false
322
+ break
323
+ case `M`:
324
+ node = { x: numbers[0], y: numbers[1] }
325
+ edge = false
326
+ break
327
+ case `l`:
328
+ node = { x: prev.x + numbers[0], y: prev.y + numbers[1] }
329
+ edge = true
330
+ break
331
+ case `L`:
332
+ node = { x: numbers[0], y: numbers[1] }
333
+ edge = true
334
+ break
335
+ case `c`:
336
+ node = { x: prev.x + numbers[4], y: prev.y + numbers[5] }
337
+ edge = {
338
+ c: { x: prev.x + numbers[0], y: prev.y + numbers[1] },
339
+ s: { x: prev.x + numbers[2], y: prev.y + numbers[3] },
340
+ }
341
+ break
342
+ case `C`:
343
+ node = { x: numbers[4], y: numbers[5] }
344
+ edge = {
345
+ c: { x: numbers[0], y: numbers[1] },
346
+ s: { x: numbers[2], y: numbers[3] },
347
+ }
348
+ break
349
+ case `v`:
350
+ node = { x: prev.x, y: prev.y + numbers[0] }
351
+ edge = true
352
+ break
353
+ case `V`:
354
+ node = { x: prev.x, y: numbers[0] }
355
+ edge = true
356
+ break
357
+ case `z`:
358
+ case `Z`:
359
+ node = null
360
+ edge = true
361
+ }
362
+ if (node) {
363
+ prev = node
364
+ }
365
+
366
+ return { node, edge }
367
+ })
368
+
369
+ return edgeNodes
370
+ })
371
+
372
+ let i = 0
373
+ let j = 0
374
+ for (const shape of shapes) {
375
+ const jj = j
376
+ for (const { node, edge } of shape) {
377
+ setState(edgeAtoms, `subpath${j}`, edge)
378
+ setState(nodeAtoms, `subpath${j}`, node)
379
+ j++
380
+ }
381
+ const numberOfNodes = j - jj
382
+ setState(
383
+ subpathKeysAtoms,
384
+ `path${i}`,
385
+ Array.from(
386
+ { length: numberOfNodes },
387
+ (_, nodeNum) => `subpath${jj + nodeNum}`,
388
+ ),
389
+ )
390
+ setState(pathKeysAtom, (prev) => [...prev, `path${i}`])
391
+ i++
392
+ }
393
+ }
394
+
395
+ const WIDTH = 256
396
+ const HEIGHT = 296
397
+ export default function BezierPlayground() {
398
+ const svgRef = useAtomicRef(svgRefAtom)
399
+ const onPointerUp: PointerEventHandler<SVGSVGElement> = useCallback((evt) => {
400
+ evt.currentTarget.releasePointerCapture(evt.pointerId)
401
+ setState(currentlyDraggingAtom, null)
402
+ }, [])
403
+
404
+ useEffect(() => void reset(), [])
405
+
406
+ return (
407
+ <div
408
+ style={{
409
+ display: "flex",
410
+ flexFlow: "column",
411
+ position: "relative",
412
+ overflow: "hidden",
413
+ maxWidth: "1280px",
414
+ width: "100vw",
415
+ alignItems: "center",
416
+ }}
417
+ >
418
+ <svg
419
+ ref={svgRef}
420
+ viewBox={`-185 -15 ${WIDTH + 370} ${HEIGHT + 30}`}
421
+ width={1000}
422
+ height={500}
423
+ onPointerMove={onPointerMove}
424
+ onPointerUp={onPointerUp}
425
+ onPointerCancel={onPointerUp}
426
+ >
427
+ <title>Bezier Playground</title>
428
+ <defs>
429
+ <pattern id="grid" width="5" height="5" patternUnits="userSpaceOnUse">
430
+ <rect x="0" y="0" width=".5" height=".5" fill="none" stroke="#aaa" />
431
+ </pattern>
432
+ </defs>
433
+ <rect x={0} y={0} width={WIDTH} height={HEIGHT} fill="#aaa3" />
434
+ <rect
435
+ x={-185}
436
+ y={-10}
437
+ width={WIDTH + 370}
438
+ height={HEIGHT + 20}
439
+ fill="url(#grid)"
440
+ />
441
+ <PathsDemo />
442
+ </svg>
443
+ <button type="button" class="flat" onClick={reset}>
444
+ Reset
445
+ </button>
446
+ </div>
447
+ )
448
+ }
@@ -0,0 +1,48 @@
1
+ import { render } from "preact"
2
+
3
+ import "./style.css"
4
+ import BezierPlayground from "./BezierPlayground.tsx"
5
+
6
+ export function App() {
7
+ return (
8
+ <>
9
+ <h1>Atom.io in Preact on Vite</h1>
10
+ <main>
11
+ <BezierPlayground />
12
+ <article>
13
+ <Resource
14
+ title="Learn Atom.io"
15
+ description="Atom.io is where data lives."
16
+ href="https://atom.io.fyi/docs/getting-started"
17
+ />
18
+ <Resource
19
+ title="Learn Preact"
20
+ description="If you're new to Preact, try the interactive tutorial to learn important concepts"
21
+ href="https://preactjs.com/tutorial"
22
+ />
23
+ <Resource
24
+ title="Learn Vite"
25
+ description="To learn more about Vite and how you can customize it to fit your needs, take a look at their excellent documentation"
26
+ href="https://vitejs.dev"
27
+ />
28
+ </article>
29
+ </main>
30
+ </>
31
+ )
32
+ }
33
+
34
+ type ResourceProps = {
35
+ title: string
36
+ description: string
37
+ href: string
38
+ }
39
+ function Resource(props: ResourceProps) {
40
+ return (
41
+ <a href={props.href} target="_blank" class="resource">
42
+ <h2>{props.title}</h2>
43
+ <p>{props.description}</p>
44
+ </a>
45
+ )
46
+ }
47
+
48
+ render(<App />, document.getElementById("app")!)
@@ -0,0 +1,105 @@
1
+ Great question — the `d` attribute in an SVG `<path>` element is one of the most powerful but also most cryptic parts of SVG. It defines the shape of the path by listing a series of **commands** and **parameters**. Let’s break it down step by step so you can confidently read (and write) it.
2
+
3
+ ---
4
+
5
+ ## đź§­ 1. What the `d` Attribute Is
6
+
7
+ The `d` attribute contains **a mini language** consisting of:
8
+
9
+ - Single-letter commands (e.g. `M`, `L`, `C`, `Z`)
10
+ - A series of numbers following those commands (coordinates or parameters)
11
+ - Optional spaces and commas
12
+
13
+ For example:
14
+
15
+ ```svg
16
+ <path d="M10 10 L50 50 L90 10 Z" />
17
+ ```
18
+
19
+ This draws a triangle:
20
+
21
+ 1. Move to `(10,10)`
22
+ 2. Draw a line to `(50,50)`
23
+ 3. Draw a line to `(90,10)`
24
+ 4. Close the shape
25
+
26
+ ---
27
+
28
+ ## ✍️ 2. Absolute vs Relative Commands
29
+
30
+ Each command can be:
31
+
32
+ - **Uppercase** → Absolute coordinates
33
+ - **Lowercase** → Relative coordinates (relative to the current point)
34
+
35
+ Example:
36
+
37
+ ```svg
38
+ M 100 100 // Move to (100,100)
39
+ l 50 0 // Draw line relative +50 in x, +0 in y → ends at (150,100)
40
+ ```
41
+
42
+ ---
43
+
44
+ ## đź§± 3. Common Path Commands
45
+
46
+ | Command | Name | Parameters | What it does |
47
+ | --------- | ------------------- | --------------------------------------------------- | -------------------------------------------------------- |
48
+ | `M` / `m` | moveto | x y | Moves the “pen” to a new position without drawing |
49
+ | `L` / `l` | lineto | x y | Draws a straight line from current point to new point |
50
+ | `H` / `h` | horizontal lineto | x | Line horizontally (only x changes) |
51
+ | `V` / `v` | vertical lineto | y | Line vertically (only y changes) |
52
+ | `C` / `c` | cubic Bézier curve | x1 y1 x2 y2 x y | Curve with 2 control points and an endpoint |
53
+ | `S` / `s` | smooth cubic Bézier | x2 y2 x y | Like `C` but assumes first control point is a reflection |
54
+ | `Q` / `q` | quadratic Bézier | x1 y1 x y | Curve with 1 control point and an endpoint |
55
+ | `T` / `t` | smooth quadratic | x y | Like `Q` but auto-control point |
56
+ | `A` / `a` | elliptical arc | rx ry x-axis-rotation large-arc-flag sweep-flag x y | Draws an arc |
57
+ | `Z` / `z` | closepath | _(none)_ | Closes the path (line back to start) |
58
+
59
+ ---
60
+
61
+ ## đź§® 4. How to Read a Complex Example
62
+
63
+ Here’s a more complex example:
64
+
65
+ ```svg
66
+ <path d="M 100 100 C 100 50, 200 50, 200 100 S 300 150, 300 100 Z" />
67
+ ```
68
+
69
+ Let’s break it down:
70
+
71
+ 1. `M 100 100` — move to `(100,100)`
72
+ 2. `C 100 50, 200 50, 200 100` — draw cubic Bézier curve with control points `(100,50)` and `(200,50)`, endpoint `(200,100)`
73
+ 3. `S 300 150, 300 100` — smooth curve, auto control point from previous, second control `(300,150)`, endpoint `(300,100)`
74
+ 4. `Z` — close the path (draw a line back to `(100,100)`)
75
+
76
+ ---
77
+
78
+ ## đź§  5. Tips for Reading
79
+
80
+ - Think of the “pen” moving around.
81
+ - Break the path at each command letter.
82
+ - Translate relative commands (`l`, `c`, etc.) into absolute positions step by step if needed.
83
+ - Visualization tools like [SVG Path Editor](https://yqnn.github.io/svg-path-editor/) can help you see what’s happening interactively.
84
+
85
+ ---
86
+
87
+ ## đź§° 6. Handy Shortcuts
88
+
89
+ - Repeating commands can omit the command letter.
90
+
91
+ ```svg
92
+ M10 10 L20 20 30 10
93
+ ```
94
+
95
+ is equivalent to:
96
+
97
+ ```svg
98
+ M10 10 L20 20 L30 10
99
+ ```
100
+
101
+ - For curves (`C`, `S`, `Q`, `T`), the number of coordinates tells you how many segments there are.
102
+
103
+ ---
104
+
105
+ Would you like me to **deconstruct a specific `d` string** you have? (e.g. paste one and I’ll walk through it step by step.)
@@ -0,0 +1,126 @@
1
+ :root {
2
+ --fg: #ccc;
3
+ --bg-shd1: #0e0e0e;
4
+ --bg: #1a1a1a;
5
+ --bg-tnt1: #262626;
6
+ --bg-tnt2: #444;
7
+
8
+ font-family: Charter, serif;
9
+ line-height: 1.5;
10
+ font-weight: 400;
11
+
12
+ color: var(--fg);
13
+ background: var(--bg);
14
+
15
+ font-synthesis: none;
16
+ text-rendering: optimizeLegibility;
17
+ -webkit-font-smoothing: antialiased;
18
+ -moz-osx-font-smoothing: grayscale;
19
+ -webkit-text-size-adjust: 100%;
20
+ }
21
+
22
+ @media (prefers-color-scheme: light) {
23
+ :root {
24
+ --fg: #222;
25
+ --bg-shd1: #e0e0e0;
26
+ --bg: #eee;
27
+ --bg-tnt1: #f3f3f3;
28
+ --bg-tnt2: #f6f6f6;
29
+ }
30
+ }
31
+
32
+ body {
33
+ margin: 0;
34
+ display: flex;
35
+ flex-flow: column;
36
+ align-items: center;
37
+ justify-content: center;
38
+ min-height: 100vh;
39
+ width: 100vw;
40
+ }
41
+
42
+ #app {
43
+ text-align: center;
44
+ display: flex;
45
+ flex-flow: column;
46
+ align-items: center;
47
+ }
48
+
49
+ img {
50
+ margin-bottom: 1.5rem;
51
+ }
52
+
53
+ img:hover {
54
+ filter: drop-shadow(0 0 2em #673ab8aa);
55
+ }
56
+
57
+ main {
58
+ display: flex;
59
+ flex-flow: column;
60
+ align-items: center;
61
+ }
62
+
63
+ article {
64
+ margin-top: 5rem;
65
+ padding: 0 2rem;
66
+ display: flex;
67
+ gap: 10px;
68
+ width: 100vw;
69
+ justify-content: center;
70
+ align-items: stretch;
71
+ max-width: 1280px;
72
+ }
73
+
74
+ .resource {
75
+ padding: 0.75rem 1.5rem;
76
+ border-radius: 0.5rem;
77
+ text-align: left;
78
+ text-decoration: none;
79
+ color: var(--fg);
80
+ background: var(--bg-tnt1);
81
+ border: 1px solid transparent;
82
+ flex: 1;
83
+ &:hover {
84
+ background: var(--bg-tnt2);
85
+ border: 1px solid var(--fg);
86
+ box-shadow: 0 25px 50px -12px #673ab888;
87
+ }
88
+ }
89
+
90
+ .node {
91
+ fill: #673ab8;
92
+ stroke: #aaa;
93
+ }
94
+ .path {
95
+ fill: #aaa1;
96
+ stroke: #aaa;
97
+ }
98
+
99
+ @media (max-width: 639px) {
100
+ article {
101
+ flex-flow: column;
102
+ }
103
+ }
104
+
105
+ button {
106
+ font-family: Verdana, sans-serif;
107
+ }
108
+
109
+ button.flat {
110
+ color: var(--fg);
111
+ background: var(--bg);
112
+ border: 1px solid var(--fg);
113
+ height: 40px;
114
+ font-size: 20px;
115
+ position: absolute;
116
+ bottom: 10px;
117
+ right: 10px;
118
+ padding: 2px 10px 4px;
119
+ &:hover {
120
+ background: var(--bg-tnt1);
121
+ }
122
+ &:active {
123
+ background: var(--bg-shd1);
124
+ transform: scale(0.98);
125
+ }
126
+ }