fragment-tools 0.1.20 → 0.2.1

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 (105) hide show
  1. package/bin/index.js +2 -2
  2. package/package.json +5 -6
  3. package/src/cli/build.js +1 -1
  4. package/src/cli/plugins/check-dependencies.js +19 -17
  5. package/src/cli/run.js +1 -1
  6. package/src/cli/templates/three-fragment/index.js +6 -6
  7. package/src/cli/templates/three-orthographic/index.js +3 -3
  8. package/src/cli/templates/three-perspective/index.js +3 -3
  9. package/src/client/app/actions/resize.js +14 -0
  10. package/src/client/app/components/HintLoading.svelte +94 -0
  11. package/src/client/app/components/HintPaused.svelte +88 -0
  12. package/src/client/app/components/HintRecord.svelte +62 -0
  13. package/src/client/app/components/IconLocked.svelte +51 -0
  14. package/src/client/app/components/IconTriggers.svelte +48 -0
  15. package/src/client/app/components/Init.svelte +14 -27
  16. package/src/client/app/components/KeyBinding.svelte +3 -6
  17. package/src/client/app/helpers.js +4 -40
  18. package/src/client/app/hooks.js +41 -17
  19. package/src/client/app/inputs/MIDI.js +2 -1
  20. package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +6 -1
  21. package/src/client/app/lib/gl/Renderer.js +1 -0
  22. package/src/client/app/lib/svelte-json-tree/ErrorNode.svelte +28 -0
  23. package/src/client/app/lib/svelte-json-tree/ErrorStack.svelte +31 -0
  24. package/src/client/app/lib/svelte-json-tree/Expandable.svelte +25 -0
  25. package/src/client/app/lib/svelte-json-tree/JSONArrayNode.svelte +38 -0
  26. package/src/client/app/lib/svelte-json-tree/JSONArrow.svelte +47 -0
  27. package/src/client/app/lib/svelte-json-tree/JSONFunctionNode.svelte +114 -0
  28. package/src/client/app/lib/svelte-json-tree/JSONIterableArrayNode.svelte +60 -0
  29. package/src/client/app/lib/svelte-json-tree/JSONIterableMapNode.svelte +87 -0
  30. package/src/client/app/lib/svelte-json-tree/JSONNested.svelte +94 -0
  31. package/src/client/app/lib/svelte-json-tree/JSONNode.svelte +91 -0
  32. package/src/client/app/lib/svelte-json-tree/JSONObjectNode.svelte +40 -0
  33. package/src/client/app/lib/svelte-json-tree/JSONStringNode.svelte +31 -0
  34. package/src/client/app/lib/svelte-json-tree/JSONValueNode.svelte +31 -0
  35. package/src/client/app/lib/svelte-json-tree/PreviewList.svelte +38 -0
  36. package/src/client/app/lib/svelte-json-tree/RegExpNode.svelte +42 -0
  37. package/src/client/app/lib/svelte-json-tree/Root.svelte +75 -0
  38. package/src/client/app/lib/svelte-json-tree/Summary.svelte +9 -0
  39. package/src/client/app/lib/svelte-json-tree/TypedArrayNode.svelte +56 -0
  40. package/src/client/app/lib/svelte-json-tree/index.js +1 -0
  41. package/src/client/app/lib/svelte-json-tree/utils.js +57 -0
  42. package/src/client/app/modules/Console/ConsoleLine.svelte +12 -11
  43. package/src/client/app/modules/Console.svelte +82 -17
  44. package/src/client/app/modules/Exports.svelte +48 -48
  45. package/src/client/app/modules/MidiPanel.svelte +12 -19
  46. package/src/client/app/modules/Monitor.svelte +147 -55
  47. package/src/client/app/modules/Params.svelte +127 -80
  48. package/src/client/app/renderers/2DRenderer.js +1 -0
  49. package/src/client/app/renderers/FragmentRenderer.js +1 -1
  50. package/src/client/app/renderers/P5GLRenderer.js +11 -5
  51. package/src/client/app/renderers/P5Renderer.js +7 -3
  52. package/src/client/app/renderers/THREERenderer.js +42 -79
  53. package/src/client/app/state/Sketch.svelte.js +538 -0
  54. package/src/client/app/state/errors.svelte.js +17 -0
  55. package/src/client/app/state/exports.svelte.js +152 -0
  56. package/src/client/app/state/layout.svelte.js +205 -0
  57. package/src/client/app/state/monitors.svelte.js +36 -0
  58. package/src/client/app/state/renderers.svelte.js +77 -0
  59. package/src/client/app/state/rendering.svelte.js +697 -0
  60. package/src/client/app/state/sketches.svelte.js +73 -0
  61. package/src/client/app/state/utils.svelte.js +65 -0
  62. package/src/client/app/ui/Build.svelte +53 -60
  63. package/src/client/app/ui/ErrorOverlay.svelte +2 -2
  64. package/src/client/app/ui/Field.svelte +63 -189
  65. package/src/client/app/ui/FieldGroup.svelte +4 -5
  66. package/src/client/app/ui/FieldSection.svelte +14 -9
  67. package/src/client/app/ui/FieldSpace.svelte +1 -1
  68. package/src/client/app/ui/FieldTrigger.svelte +86 -84
  69. package/src/client/app/ui/FieldTriggers.svelte +25 -24
  70. package/src/client/app/ui/FloatingParams.svelte +50 -12
  71. package/src/client/app/ui/Layout.svelte +24 -13
  72. package/src/client/app/ui/LayoutColumn.svelte +2 -2
  73. package/src/client/app/ui/LayoutComponent.svelte +86 -195
  74. package/src/client/app/ui/LayoutResizer.svelte +25 -37
  75. package/src/client/app/ui/LayoutRoot.svelte +3 -5
  76. package/src/client/app/ui/LayoutRow.svelte +2 -2
  77. package/src/client/app/ui/LayoutToolbar.svelte +17 -76
  78. package/src/client/app/ui/Module.svelte +31 -35
  79. package/src/client/app/ui/ModuleHeaderAction.svelte +23 -16
  80. package/src/client/app/ui/ModuleHeaderButton.svelte +3 -3
  81. package/src/client/app/ui/ModuleHeaderSelect.svelte +4 -12
  82. package/src/client/app/ui/ModuleRenderer.svelte +84 -22
  83. package/src/client/app/ui/ParamsOutput.svelte +61 -77
  84. package/src/client/app/ui/Preview.svelte +15 -4
  85. package/src/client/app/ui/SelectChevrons.svelte +1 -2
  86. package/src/client/app/ui/SketchRenderer.svelte +89 -701
  87. package/src/client/app/ui/SketchSelect.svelte +14 -49
  88. package/src/client/app/ui/fields/ButtonInput.svelte +14 -11
  89. package/src/client/app/ui/fields/CheckboxInput.svelte +5 -12
  90. package/src/client/app/ui/fields/ColorInput.svelte +46 -121
  91. package/src/client/app/ui/fields/FieldInputRow.svelte +5 -1
  92. package/src/client/app/ui/fields/ImageInput.svelte +14 -14
  93. package/src/client/app/ui/fields/Input.svelte +19 -25
  94. package/src/client/app/ui/fields/IntervalInput.svelte +22 -22
  95. package/src/client/app/ui/fields/NumberInput.svelte +32 -38
  96. package/src/client/app/ui/fields/ProgressInput.svelte +14 -13
  97. package/src/client/app/ui/fields/Select.svelte +34 -45
  98. package/src/client/app/ui/fields/TextInput.svelte +10 -6
  99. package/src/client/app/ui/fields/VectorInput.svelte +25 -30
  100. package/src/client/app/utils/canvas.utils.js +8 -8
  101. package/src/client/app/utils/color.utils.js +46 -13
  102. package/src/client/app/utils/fields.utils.js +1 -1
  103. package/src/client/app/utils/glsl.utils.js +1 -1
  104. package/src/client/app/utils/glslErrors.js +1 -1
  105. package/src/client/main.js +2 -2
@@ -1,42 +1,6 @@
1
- import { props } from './stores/props';
2
-
3
- const propHandler = {
4
- set: function (target, key, value, receiver) {
5
- Reflect.set(target, key, value, receiver);
6
-
7
- if (key === 'value') {
8
- props.update((currentProps) => currentProps);
9
- }
10
-
11
- return true;
12
- },
13
- };
14
-
15
- const propsHandler = {
16
- get: (target, key) => {
17
- if (typeof target[key] === 'object' && target[key] !== null) {
18
- return new Proxy(target[key], propHandler);
19
- }
20
- },
21
- set: (target, key, value) => {
22
- console.log('new set', target, key, value);
23
-
24
- target[key] = value;
25
-
26
- props.update((currentProps) => currentProps);
27
-
28
- return true;
29
- },
30
- deleteProperty: (target, prop) => {
31
- if (prop in target) {
32
- delete target[prop];
33
- props.update((currentProps) => currentProps);
34
-
35
- return true;
36
- }
37
- },
38
- };
39
-
40
1
  export function reactiveProps(props = {}) {
41
- return new Proxy(props, propsHandler);
2
+ console.warn(
3
+ `reactiveProps has been deprecated. Props are now reactive by default.`,
4
+ );
5
+ return props;
42
6
  }
@@ -1,20 +1,44 @@
1
- import {
2
- removeBeforeCaptureFrom,
3
- removeAfterCaptureFrom,
4
- removeBeforeRecordFrom,
5
- removeAfterRecordFrom,
6
- } from './stores/exports';
1
+ import { rendering } from './state/rendering.svelte';
2
+ import { sketchesManager } from './state/sketches.svelte';
3
+ import { getContext } from './triggers/shared';
7
4
 
8
- export {
9
- onBeforeCapture,
10
- onAfterCapture,
11
- onBeforeRecord,
12
- onAfterRecord,
13
- } from './stores/exports';
5
+ export let onBeforeCapture = (listener, context = getContext()) => {
6
+ sketchesManager.sketches[context]?.onBeforeCapture(listener);
7
+ };
14
8
 
15
- export function removeHooksFrom(context) {
16
- removeBeforeCaptureFrom(context);
17
- removeAfterCaptureFrom(context);
18
- removeBeforeRecordFrom(context);
19
- removeAfterRecordFrom(context);
9
+ export let onAfterCapture = (listener, context = getContext()) => {
10
+ sketchesManager.sketches[context]?.onAfterCapture(listener);
11
+ };
12
+
13
+ export let onBeforeRecord = (listener, context = getContext()) => {
14
+ sketchesManager.sketches[context]?.onBeforeRecord(listener);
15
+ };
16
+
17
+ export let onAfterRecord = (listener, context = getContext()) => {
18
+ sketchesManager.sketches[context]?.onAfterRecord(listener);
19
+ };
20
+
21
+ /**
22
+ * Screenshot the sketch it is called from
23
+ * @param {object} options
24
+ * @param {string} [options.filename]
25
+ * @param {function} [options.pattern]
26
+ * @param {exportDir} [options.pattern ]
27
+ */
28
+ export async function screenshot({ filename, pattern, exportDir } = {}) {
29
+ const context = getContext();
30
+
31
+ const renders = rendering.renders.filter(
32
+ (render) => render.sketch.key === context,
33
+ );
34
+
35
+ if (renders.length > 0) {
36
+ for (let i = 0; i < renders.length; i++) {
37
+ await renders[i].screenshot({
38
+ filename,
39
+ pattern,
40
+ exportDir,
41
+ });
42
+ }
43
+ }
20
44
  }
@@ -8,7 +8,7 @@ const commands = {
8
8
 
9
9
  const notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
10
10
 
11
- const LOCAL_STORAGE_KEY = 'midi.requested';
11
+ const LOCAL_STORAGE_KEY = 'fragment.midi.requested';
12
12
 
13
13
  class MIDI extends Input {
14
14
  constructor() {
@@ -125,6 +125,7 @@ class MIDI extends Input {
125
125
  }
126
126
 
127
127
  async request() {
128
+ console.log('MIDI :: request!');
128
129
  try {
129
130
  if (!this.requesting && !this.access) {
130
131
  localStorage.setItem(LOCAL_STORAGE_KEY, true);
@@ -29,11 +29,14 @@ class CanvasRecorder {
29
29
  : Infinity;
30
30
  this.started = false;
31
31
  this.stopped = false;
32
+
33
+ this.startTime = 0;
32
34
  }
33
35
 
34
36
  async load() {}
35
37
 
36
38
  async start() {
39
+ this.startTime = performance.now();
37
40
  this.onStart();
38
41
 
39
42
  await this.load();
@@ -96,7 +99,9 @@ class CanvasRecorder {
96
99
  tick() {}
97
100
 
98
101
  end() {
99
- console.log(`CanvasRecorder - compiled ${this.frameCount + 1} frames`);
102
+ console.log(
103
+ `CanvasRecorder - compiled ${this.frameCount + 1} frames in ${(performance.now() - this.startTime) / 1000}s`,
104
+ );
100
105
  this.onComplete(this.result);
101
106
  }
102
107
 
@@ -20,6 +20,7 @@ class Renderer {
20
20
  };
21
21
 
22
22
  this.canvas = canvas;
23
+ this.canvas.setAttribute('data-engine', `fragment-gl`);
23
24
 
24
25
  if (webgl === 2) gl = canvas.getContext('webgl2', attributes);
25
26
  if (!gl) {
@@ -0,0 +1,28 @@
1
+ <script>
2
+ import ErrorStack from './ErrorStack.svelte';
3
+ import JSONNested from './JSONNested.svelte';
4
+ import JSONNode from './JSONNode.svelte';
5
+
6
+ let { value } = $props();
7
+
8
+ let stack = $derived(value.stack.split('\n'));
9
+ </script>
10
+
11
+ <JSONNested keys={['message', 'stack']}>
12
+ {#snippet summary()}
13
+ <span class="label">Error: {String(value.message)}</span>
14
+ {/snippet}
15
+ {#snippet preview()}
16
+ <span class="label">Error: {String(value.message)}</span>
17
+ {/snippet}
18
+ {#snippet itemKey(key)}
19
+ <span class="property">{key}</span>
20
+ {/snippet}
21
+ {#snippet itemValue(key)}
22
+ {#if key === 'stack'}
23
+ <ErrorStack {stack} />
24
+ {:else}
25
+ <JSONNode value={value[key]} />
26
+ {/if}
27
+ {/snippet}
28
+ </JSONNested>
@@ -0,0 +1,31 @@
1
+ <script>
2
+ import JsonNode from './JSONNode.svelte';
3
+ import { useState } from './utils.js';
4
+
5
+ let { stack } = $props();
6
+
7
+ const { expanded, expandable, toggleExpand } = useState();
8
+ //expandable = true;
9
+ </script>
10
+
11
+ <!-- svelte-ignore a11y_click_events_have_key_events a11y-no-static-element-interactions -->
12
+ <span onclick={(e) => toggleExpand(e)} role="button">
13
+ {#if expanded}
14
+ {#each stack as line, index}
15
+ {@const appendNewLine = index < stack.length - 1}
16
+ <span class:indent={index > 0}
17
+ ><JsonNode value={line + (appendNewLine ? '\\n' : '')} /><span
18
+ class="operator">{appendNewLine ? ' +' : ''}</span
19
+ ></span
20
+ ><br />
21
+ {/each}
22
+ {:else}
23
+ <span><JsonNode value={stack[0] + '…'} /></span>
24
+ {/if}
25
+ </span>
26
+
27
+ <style>
28
+ .indent {
29
+ padding-left: var(--li-identation);
30
+ }
31
+ </style>
@@ -0,0 +1,25 @@
1
+ <script>
2
+ import { useState } from './utils.js';
3
+
4
+ let { key, children } = $props();
5
+
6
+ // const expandable = $state(false);
7
+ // setContext('expandable', false);
8
+ // useState(({ keyPath, level }) => {
9
+ // if (key !== '[[Entries]]') {
10
+ // keyPath = [...keyPath, key];
11
+ // level = level + 1;
12
+ // }
13
+ // return {
14
+ // keyPath,
15
+ // level,
16
+ // expanded,
17
+ // expandable,
18
+ // toggle: () => {
19
+ // expanded = !expanded;
20
+ // },
21
+ // };
22
+ // });
23
+ </script>
24
+
25
+ {@render children?.()}
@@ -0,0 +1,38 @@
1
+ <script>
2
+ import JSONArrow from './JSONArrow.svelte';
3
+ import JSONNested from './JSONNested.svelte';
4
+ import JSONNode from './JSONNode.svelte';
5
+ import PreviewList from './PreviewList.svelte';
6
+
7
+ let { value } = $props();
8
+
9
+ let keys = $derived(Object.getOwnPropertyNames(value));
10
+ let previewList = $derived(value.slice(0, 5));
11
+ </script>
12
+
13
+ <JSONNested {keys}>
14
+ {#snippet summary()}
15
+ <span class="label">Array({value.length})</span>
16
+ {/snippet}
17
+
18
+ {#snippet preview(root)}
19
+ <PreviewList
20
+ list={previewList}
21
+ hasMore={previewList.length < value.length}
22
+ label="({value.length}) "
23
+ prefix="["
24
+ postfix="]"
25
+ {root}
26
+ >
27
+ {#snippet item(node)}
28
+ <JSONNode value={node} />
29
+ {/snippet}
30
+ </PreviewList>
31
+ {/snippet}
32
+ {#snippet itemKey(key)}
33
+ <span class="property">{String(key)}</span>
34
+ {/snippet}
35
+ {#snippet itemValue(key)}
36
+ <JSONNode value={value[key]} />
37
+ {/snippet}
38
+ </JSONNested>
@@ -0,0 +1,47 @@
1
+ <script>
2
+ import { useState } from './utils';
3
+
4
+ let {
5
+ expandable: _expandable,
6
+ expanded: _expanded,
7
+ toggleExpand,
8
+ } = useState();
9
+ let { expanded = _expanded, expandable = _expandable } = $props();
10
+ </script>
11
+
12
+ {#if expandable}
13
+ <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
14
+ <span class="json-arrow" class:expanded>
15
+ <span class="char">{'\u25B6'}</span>
16
+ </span>
17
+ {/if}
18
+
19
+ <style>
20
+ .json-arrow {
21
+ display: inline-flex;
22
+ transition: 150ms;
23
+ line-height: 1.1em;
24
+
25
+ transform-origin: 2px 50%;
26
+ }
27
+
28
+ .json-arrow.expanded {
29
+ transform: rotateZ(90deg);
30
+ }
31
+
32
+ .char {
33
+ transform-origin: 25% 50%;
34
+ position: relative;
35
+ font-size: 0.75em;
36
+ margin-left: 0;
37
+ color: var(--arrow-color);
38
+ user-select: none;
39
+ font-family: 'Courier New', Courier, monospace;
40
+ display: block;
41
+ margin-top: 1px;
42
+ }
43
+
44
+ /* .expanded {
45
+ transform: rotateZ(90deg) translateX(-3px);
46
+ } */
47
+ </style>
@@ -0,0 +1,114 @@
1
+ <script>
2
+ import JSONNested from './JSONNested.svelte';
3
+ import JSONNode from './JSONNode.svelte';
4
+ import JsonObjectNode from './JSONObjectNode.svelte';
5
+
6
+ let { value = () => {} } = $props();
7
+
8
+ let str = $derived(toString(value));
9
+ let ctx = $derived(parseFunction(str));
10
+
11
+ function parseFunction(str) {
12
+ const match = str.match(
13
+ /^(?:(async)\s+)?(?:function)?(\*)?\s*([^(]+)?(\([^)]*\))\s*(=>)?/,
14
+ );
15
+ const isAsync = match?.[1];
16
+ const isGenerator = match?.[2];
17
+ const fnName = match?.[3];
18
+ const args = match?.[4];
19
+ const isArrow = match?.[5];
20
+ const classMatch = str.match(/^class\s+([^\s]+)/);
21
+ const isClass = classMatch?.[1];
22
+
23
+ return {
24
+ args,
25
+ isAsync,
26
+ isGenerator,
27
+ fnName,
28
+ isArrow,
29
+ isClass,
30
+ };
31
+ }
32
+
33
+ function getPreview1({ isGenerator, isAsync, isClass }) {
34
+ if (isClass) return `class ${isClass}`;
35
+ return (isAsync ? 'async ' : '') + 'ƒ' + (isGenerator ? '*' : '');
36
+ }
37
+
38
+ function getPreview2({ isAsync, isArrow, fnName, args }) {
39
+ return (
40
+ (isArrow && isAsync ? 'async' : '') +
41
+ ' ' +
42
+ (fnName ?? '') +
43
+ args +
44
+ (isArrow ? ' => …' : '')
45
+ );
46
+ }
47
+
48
+ const FUNCTION = '[[Function]]';
49
+ const PROTO = '[[Prototype]]';
50
+
51
+ function getValue(key) {
52
+ if (key === PROTO) return value.__proto__;
53
+ return value[key];
54
+ }
55
+
56
+ function filterKeys(key) {
57
+ if (key === FUNCTION) return true;
58
+ return getValue(key);
59
+ }
60
+
61
+ function toString(value = () => {}) {
62
+ try {
63
+ return value.toString();
64
+ } catch {
65
+ switch (value.constructor.name) {
66
+ case 'AsyncFunction':
67
+ return 'async function () {}';
68
+ case 'AsyncGeneratorFunction':
69
+ return 'async function * () {}';
70
+ case 'GeneratorFunction:':
71
+ return 'function * () {}';
72
+ default:
73
+ return 'function () {}';
74
+ }
75
+ }
76
+ }
77
+
78
+ let keys = $derived(
79
+ ['length', 'name', 'prototype', FUNCTION, PROTO].filter(filterKeys),
80
+ );
81
+ </script>
82
+
83
+ <JSONNested {keys}>
84
+ {#snippet summary()}
85
+ <span class="i">ƒ</span>
86
+ {/snippet}
87
+ {#snippet preview()}
88
+ {#if !ctx.isArrow}<span class="fn i">{getPreview1(ctx)}</span
89
+ >{/if}{#if !ctx.isClass}<span class="i">{getPreview2(ctx)}</span
90
+ >{/if}
91
+ {/snippet}
92
+ {#snippet itemKey(key)}
93
+ <span
94
+ class={key === FUNCTION || key === PROTO ? 'internal' : 'property'}
95
+ >{key}</span
96
+ >
97
+ {/snippet}
98
+ {#snippet itemValue(key)}
99
+ {#if key === FUNCTION}<span class="i">{str}</span
100
+ >{:else if key === 'prototype'}<JsonObjectNode
101
+ value={getValue(key)}
102
+ />{:else}<JSONNode value={getValue(key)} />{/if}
103
+ {/snippet}
104
+ </JSONNested>
105
+
106
+ <style>
107
+ .i {
108
+ font-style: italic;
109
+ }
110
+ .fn,
111
+ .i {
112
+ color: var(--function-color);
113
+ }
114
+ </style>
@@ -0,0 +1,60 @@
1
+ <script>
2
+ import JSONNested from './JSONNested.svelte';
3
+ import JSONNode from './JSONNode.svelte';
4
+ import PreviewList from './PreviewList.svelte';
5
+
6
+ let { value, nodeType } = $props();
7
+
8
+ let [indexes, items] = $derived.by(() => {
9
+ let _indexes = [];
10
+ let _items = [];
11
+ let i = 0;
12
+ for (const entry of value) {
13
+ _indexes.push(i++);
14
+ _items.push(entry);
15
+ }
16
+
17
+ return [_indexes, _items];
18
+ });
19
+
20
+ let previewItems = $derived(items.slice(0, 5));
21
+
22
+ const ENTRIES = '[[Entries]]';
23
+ </script>
24
+
25
+ <JSONNested keys={[ENTRIES, 'size']} shouldShowColon={(key) => key !== ENTRIES}>
26
+ {#snippet summary()}
27
+ <span class="label">{nodeType}({indexes.length})</span>
28
+ {/snippet}
29
+ {#snippet preview(root)}
30
+ <PreviewList
31
+ list={previewItems}
32
+ hasMore={previewItems.length < items.length}
33
+ label={`${nodeType}(${indexes.length}) `}
34
+ prefix={'{'}
35
+ postfix="}"
36
+ {root}
37
+ >
38
+ {#snippet item(value)}
39
+ <JSONNode {value} />
40
+ {/snippet}
41
+ </PreviewList>
42
+ {/snippet}
43
+ {#snippet itemKey(key)}
44
+ <span class={key === ENTRIES ? 'internal' : 'property'}>{key}</span>
45
+ {/snippet}
46
+ {#snippet itemValue(key)}
47
+ {#if key === ENTRIES}
48
+ <JSONNested keys={indexes} defaultExpanded>
49
+ {#snippet itemKey(index)}
50
+ <span class="property">{index}</span>
51
+ {/snippet}
52
+ {#snippet itemValue(index)}
53
+ <JSONNode value={items[index]} />
54
+ {/snippet}
55
+ </JSONNested>
56
+ {:else}
57
+ <JSONNode value={value[key]} />
58
+ {/if}
59
+ {/snippet}
60
+ </JSONNested>
@@ -0,0 +1,87 @@
1
+ <script>
2
+ import { useState } from './utils.js';
3
+ import JSONNested from './JSONNested.svelte';
4
+ import JSONNode from './JSONNode.svelte';
5
+ import PreviewList from './PreviewList.svelte';
6
+
7
+ let { value } = $props();
8
+ useState();
9
+
10
+ let [indexes, keys, values] = $derived.by(() => {
11
+ let _indexes = [];
12
+ let _keys = [];
13
+ let _values = [];
14
+ let i = 0;
15
+ for (const entry of value) {
16
+ _indexes.push(i++);
17
+ _keys.push(entry[0]);
18
+ _values.push(entry[1]);
19
+ }
20
+
21
+ return [_indexes, _keys, _values];
22
+ });
23
+
24
+ let previewKeys = $derived(Array.from(value.keys()).slice(0, 5));
25
+
26
+ const ENTRIES = '[[Entries]]';
27
+ </script>
28
+
29
+ <JSONNested keys={[ENTRIES, 'size']} shouldShowColon={(key) => key !== ENTRIES}>
30
+ {#snippet summary()}
31
+ <span color="label">Map({keys.length})</span>
32
+ {/snippet}
33
+ {#snippet preview(root)}
34
+ <PreviewList
35
+ list={previewKeys}
36
+ hasMore={previewKeys.length < value.size}
37
+ label={`Map(${keys.length}) `}
38
+ prefix={`{`}
39
+ postfix="}"
40
+ {root}
41
+ >
42
+ {#snippet item(key)}
43
+ <JSONNode value={key} /><span class="operator">{' => '}</span
44
+ ><JSONNode value={value.get(key)} />
45
+ {/snippet}
46
+ </PreviewList>
47
+ {/snippet}
48
+ {#snippet itemKey(key)}
49
+ <span class={key === ENTRIES ? 'internal' : 'property'}>{key}</span>
50
+ {/snippet}
51
+ {#snippet itemValue(key)}
52
+ {#if key === ENTRIES}
53
+ <JSONNested
54
+ keys={indexes}
55
+ expandKey={(index) => keys[index]}
56
+ defaultExpanded
57
+ >
58
+ {#snippet itemKey(index)}
59
+ <span class="property">{index}</span>
60
+ {/snippet}
61
+ {#snippet itemValue(index)}
62
+ <JSONNested keys={['key', 'value']}>
63
+ {#snippet preview()}
64
+ <span class="operator">{'{ '}</span><JSONNode
65
+ value={keys[index]}
66
+ /><span class="operator">{' => '}</span><JSONNode
67
+ value={values[index]}
68
+ /><span class="operator">{' }'}</span>
69
+ {/snippet}
70
+ {#snippet itemKey(name)}
71
+ <span class="property">{name}</span>
72
+ {/snippet}
73
+ {#snippet itemValue(name)}
74
+ <JSONNode
75
+ value={name === 'key'
76
+ ? keys[index]
77
+ : values[index]}
78
+ />
79
+ {/snippet}
80
+ </JSONNested>
81
+ {/snippet}
82
+ </JSONNested>
83
+ {:else}
84
+ <JSONNode value={value[key]} />
85
+ {/if}
86
+ {/snippet}
87
+ </JSONNested>
@@ -0,0 +1,94 @@
1
+ <script>
2
+ /* eslint-disable @typescript-eslint/no-empty-function */
3
+ import { getContext, onMount, setContext } from 'svelte';
4
+ import JSONArrow from './JSONArrow.svelte';
5
+ import { useState } from './utils.js';
6
+ import Summary from './Summary.svelte';
7
+ import Expandable from './Expandable.svelte';
8
+
9
+ let {
10
+ keys,
11
+ shouldShowColon = undefined,
12
+ expandKey = (key) => key,
13
+ defaultExpanded = false,
14
+ summary,
15
+ preview,
16
+ itemKey,
17
+ itemValue,
18
+ } = $props();
19
+
20
+ let expanded = $state(defaultExpanded);
21
+ let expandable = $state(false);
22
+ let displayMode = getContext('displayMode');
23
+ let root = getContext('root');
24
+ let toggleExpand = (e) => {
25
+ e?.preventDefault();
26
+ e.stopPropagation();
27
+
28
+ expanded = !expanded;
29
+ };
30
+
31
+ $effect(() => {
32
+ setContext('expandable', expandable);
33
+ setContext('expanded', expanded);
34
+ });
35
+
36
+ setContext('root', false);
37
+ setContext('toggleExpand', toggleExpand);
38
+ </script>
39
+
40
+ {#if displayMode === 'summary'}
41
+ {@render summary?.()}
42
+ {:else}
43
+ <!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions -->
44
+ <span class="root jsonnested" onclick={(e) => toggleExpand(e)}>
45
+ <!-- {#if root}
46
+ <JSONArrow {expanded} {expandable} />
47
+ {/if} -->
48
+ <Summary>
49
+ {@render preview?.(root)}
50
+ </Summary>
51
+ </span>
52
+
53
+ {#if expanded}
54
+ <!-- svelte-ignore a11y-click-events-have-key-events a11y-no-noninteractive-element-interactions -->
55
+ <ul
56
+ onclick={(e) => {
57
+ e.stopPropagation();
58
+ toggleExpand(e);
59
+ }}
60
+ >
61
+ {#each keys as key, index}
62
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
63
+ <li class:indent={expanded}>
64
+ <Expandable key={expandKey(key)}>
65
+ <span class="label">
66
+ <!-- <JSONArrow /> -->
67
+ {@render itemKey?.(key)}
68
+ {#if !shouldShowColon || shouldShowColon(key)}
69
+ <span class="operator">{': '}</span>
70
+ {/if}
71
+ </span>
72
+ {@render itemValue?.(key)}
73
+ </Expandable>
74
+ </li>
75
+ {/each}
76
+ </ul>
77
+ {/if}
78
+ {/if}
79
+
80
+ <style>
81
+ .root {
82
+ display: inline-flex;
83
+ position: relative;
84
+ flex-wrap: wrap;
85
+ white-space: pre-wrap;
86
+ }
87
+ .indent {
88
+ padding-left: var(--li-identation);
89
+ }
90
+ .label {
91
+ position: relative;
92
+ display: inline-flex;
93
+ }
94
+ </style>