@myko/ui-svelte 4.2.0-canary.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.
Files changed (31) hide show
  1. package/.prettierignore +4 -0
  2. package/.prettierrc +15 -0
  3. package/README.md +58 -0
  4. package/package.json +59 -0
  5. package/src/app.d.ts +13 -0
  6. package/src/app.html +12 -0
  7. package/src/lib/components/ConnectionStats.svelte +83 -0
  8. package/src/lib/components/Logs.svelte +37 -0
  9. package/src/lib/components/Query.svelte +34 -0
  10. package/src/lib/components/Report.svelte +25 -0
  11. package/src/lib/components/Search.svelte +85 -0
  12. package/src/lib/components/ServerView.svelte +95 -0
  13. package/src/lib/components/state/resolutions.ts +137 -0
  14. package/src/lib/components/state/viewstate.svelte.ts +375 -0
  15. package/src/lib/components/state/windback.svelte.ts +88 -0
  16. package/src/lib/components/transactions/EntityHistory.svelte +173 -0
  17. package/src/lib/components/transactions/TimeStrip.svelte +268 -0
  18. package/src/lib/components/transactions/TransactionDetails.svelte +26 -0
  19. package/src/lib/components/transactions/TransactionEvent.svelte +87 -0
  20. package/src/lib/components/transactions/TransactionEventGroup.svelte +56 -0
  21. package/src/lib/components/transactions/Transactions.svelte +111 -0
  22. package/src/lib/components/transactions/TransactonView.svelte +24 -0
  23. package/src/lib/components/windback/WindbackFrame.svelte +65 -0
  24. package/src/lib/components/windback/index.ts +1 -0
  25. package/src/lib/index.ts +26 -0
  26. package/src/lib/services/svelte-client.svelte.ts +863 -0
  27. package/src/routes/+page.svelte +3 -0
  28. package/static/favicon.png +0 -0
  29. package/svelte.config.js +18 -0
  30. package/tsconfig.json +13 -0
  31. package/vite.config.ts +6 -0
@@ -0,0 +1,268 @@
1
+ <script lang="ts">
2
+ import { DateTime } from 'luxon';
3
+ import { getContext } from 'svelte';
4
+ import {
5
+ FULL_DATE_FORMAT,
6
+ TRANSACTIONS_VIEW_STATE,
7
+ type TransactionsViewState
8
+ } from '../state/viewstate.svelte.js';
9
+ import { windbackState } from '../state/windback.svelte.js';
10
+
11
+ const viewState = getContext(TRANSACTIONS_VIEW_STATE) as TransactionsViewState;
12
+
13
+ const fullDuration = $derived(viewState.now.diff(viewState.timeZero));
14
+
15
+ const viewLeftDuration = $derived(viewState.leftTime.diff(viewState.timeZero));
16
+ const viewDuration = $derived(viewState.rightTime.diff(viewState.leftTime));
17
+
18
+ const viewLeftPx = $derived(
19
+ (viewLeftDuration.as('milliseconds') / fullDuration.as('milliseconds')) * viewState.width
20
+ );
21
+ const viewWidthPx = $derived(
22
+ (viewDuration.as('milliseconds') / fullDuration.as('milliseconds')) * viewState.width
23
+ );
24
+
25
+ let timestampsEl: HTMLElement | null = $state(null);
26
+ </script>
27
+
28
+ <div class="all-time">
29
+ <div class="bounds">
30
+ <div class="start-time">{viewState.timeZero.toFormat(FULL_DATE_FORMAT)}</div>
31
+ <div class="end-time">{viewState.now.toFormat(FULL_DATE_FORMAT)}</div>
32
+ </div>
33
+ <div class="all-times">
34
+ {#each viewState.allEventTimestamps as timestamp}
35
+ <div class="event" style="left: {timestamp}px"></div>
36
+ {/each}
37
+ </div>
38
+
39
+ <div class="cursor-time">
40
+ <div class="cursor" style="left: {viewState.windbackFullX}px; ">
41
+ <div class="line"></div>
42
+ <div class="triangle"></div>
43
+ </div>
44
+ </div>
45
+
46
+ <div
47
+ class="view-region"
48
+ style="left: {viewLeftPx}px; width: {viewWidthPx}px; --width: {viewWidthPx}px;"
49
+ ></div>
50
+ </div>
51
+ <div class="timestamps" bind:this={timestampsEl}>
52
+ {#each viewState.majors as { leftPx, time: majorTime }, i}
53
+ <div class="major timestamp" style="left: {leftPx}px;">
54
+ <div class="tick"></div>
55
+ <span class="timestamp-text"
56
+ >{DateTime.fromMillis(majorTime).toFormat(viewState.majorResolution.majorFormat)}</span
57
+ >
58
+ {#each viewState.minors as { leftPx, time: minorTime }, i}
59
+ <div class="minor timestamp" style="left: {leftPx}px;">
60
+ <div class="tick"></div>
61
+ <span class="timestamp-text"
62
+ >{DateTime.fromMillis(minorTime + majorTime).toFormat(
63
+ viewState.minorResolution.minorFormat
64
+ )}</span
65
+ >
66
+ </div>
67
+ {/each}
68
+ </div>
69
+ {/each}
70
+ <div class="overlay">
71
+ {#if windbackState.cursor}
72
+ <div class="windback-cursor cursor" class:lagging={!viewState.windbackState.caughtUp}>
73
+ <div class="line" style="left: {viewState.windbackX}px">
74
+ <div class="text">
75
+ <p>
76
+ {windbackState.cursor.toFormat(FULL_DATE_FORMAT)}
77
+ </p>
78
+ <p>
79
+ {viewState.now
80
+ .diff(windbackState.cursor)
81
+ .rescale()
82
+ .normalize()
83
+ .toHuman({ compactDisplay: 'short', unitDisplay: 'narrow' })} ago
84
+ </p>
85
+ </div>
86
+ <div class="click-area"></div>
87
+ </div>
88
+ </div>
89
+ {/if}
90
+ <div class="live-cursor cursor">
91
+ <div class="line" style="left: {viewState.mouseX}px">
92
+ <div class="text">
93
+ <p>
94
+ {viewState.mouseTime.toFormat(viewState.majorResolution.majorFormat)}
95
+ </p>
96
+ <p>
97
+ {viewState.mouseTimeRelative.rescale().normalize().toHuman({
98
+ compactDisplay: 'short',
99
+ unitDisplay: 'narrow',
100
+ maximumFractionDigits: 0
101
+ })}
102
+ ago
103
+ </p>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </div>
108
+ </div>
109
+
110
+ <style>
111
+ /* bounds */
112
+
113
+ .all-time {
114
+ position: relative;
115
+ border-bottom: rgba(255, 255, 255, 0.1) 1px solid;
116
+ user-select: none;
117
+ }
118
+
119
+ .bounds {
120
+ display: flex;
121
+ width: 100%;
122
+ padding: 0.1rem 0.5rem;
123
+ justify-content: space-between;
124
+ }
125
+
126
+ /* VIEW REGION (scroll bar) */
127
+
128
+ .all-times {
129
+ position: absolute;
130
+ inset: 0;
131
+ z-index: 999;
132
+ }
133
+ .view-region {
134
+ position: absolute;
135
+ top: 3px;
136
+ bottom: 3px;
137
+ border-radius: 0.2rem;
138
+ background-color: rgba(255, 255, 255, 0.2);
139
+ display: flex;
140
+ justify-content: space-between;
141
+ align-items: center;
142
+ color: white;
143
+ padding: 0 0.5rem;
144
+ box-sizing: border-box;
145
+ user-select: none;
146
+ }
147
+
148
+ /* .text {
149
+ position: absolute;
150
+ white-space: nowrap;
151
+ }
152
+
153
+ .left {
154
+ left: 0.5rem;
155
+ }
156
+
157
+ .right {
158
+ right: 0.5rem;
159
+ }
160
+
161
+ .shuntLeft {
162
+ right: calc(var(--width) + 0.5rem) !important;
163
+ left: unset;
164
+ }
165
+
166
+ .shuntRight {
167
+ right: unset;
168
+ left: calc(var(--width) + 0.5rem) !important;
169
+ }
170
+
171
+ .doubleShuntLeft {
172
+ left: unset;
173
+ right: calc(var(--right-width) + 1rem) !important;
174
+ }
175
+
176
+ .doubleShuntRight {
177
+ right: unset;
178
+ left: calc(var(--left-width) + 1rem) !important;
179
+ } */
180
+
181
+ .event {
182
+ position: absolute;
183
+ height: 100%;
184
+ border-left: 1px solid rgba(255, 255, 255, 0.2);
185
+ }
186
+
187
+ /* timestamps and grid lines */
188
+ .timestamps {
189
+ position: relative;
190
+ height: 2rem;
191
+ }
192
+ .timestamp {
193
+ position: absolute;
194
+ }
195
+
196
+ .timestamp-text {
197
+ left: 3px;
198
+ position: relative;
199
+ }
200
+
201
+ .overlay {
202
+ position: absolute;
203
+ inset: 0;
204
+ bottom: unset;
205
+ height: 100vh;
206
+ pointer-events: none;
207
+ z-index: 999;
208
+ }
209
+
210
+ .tick {
211
+ position: absolute;
212
+ top: 0;
213
+ height: 100vh;
214
+ width: 0px;
215
+ border-left: 1px solid rgba(255, 255, 255, 0.5);
216
+ }
217
+
218
+ .minor .tick {
219
+ opacity: 0.25;
220
+ }
221
+
222
+ .major {
223
+ opacity: 0.5;
224
+ }
225
+
226
+ .cursor .line {
227
+ position: absolute;
228
+ top: 0;
229
+ bottom: 0;
230
+ border-left: 1px solid rgba(255, 255, 255, 0.2);
231
+ }
232
+
233
+ .cursor .text {
234
+ white-space: nowrap;
235
+ background-color: rgba(0, 0, 0, 0.9);
236
+ padding: 0 0.25rem;
237
+ }
238
+
239
+ .windback-cursor {
240
+ border-color: cornflowerblue;
241
+
242
+ color: cornflowerblue;
243
+
244
+ .line {
245
+ border-color: cornflowerblue;
246
+ }
247
+ }
248
+
249
+ .click-area {
250
+ position: absolute;
251
+ inset: 0;
252
+ width: 20px;
253
+ left: -10px;
254
+ z-index: 999;
255
+ background-color: rgba(255, 255, 255, 0.1);
256
+ }
257
+
258
+ .click-area:hover {
259
+ cursor: ew-resize;
260
+ background-color: rgba(255, 255, 255, 0.9);
261
+ }
262
+
263
+ .lagging {
264
+ .line {
265
+ border-color: orangered;
266
+ }
267
+ }
268
+ </style>
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import { myko as client } from '../../services/svelte-client.svelte.js';
3
+ import { EventsForTransaction, type ID } from '@myko/core';
4
+ import { fromISOMemo, FULL_DATE_FORMAT } from '../state/viewstate.svelte.js';
5
+
6
+ const { tx }: { tx: ID } = $props();
7
+ const events = client.watchReport(new EventsForTransaction({ transactionId: tx }));
8
+ </script>
9
+
10
+ {#if $events}
11
+ {#each $events as event (event)}
12
+ <div class="div">
13
+ <p>{event.itemType}</p>
14
+ <p>{event.changeType}</p>
15
+ <p>{fromISOMemo(event.createdAt).diffNow().toHuman()}</p>
16
+ <p>{fromISOMemo(event.createdAt).toFormat(FULL_DATE_FORMAT)}</p>
17
+ </div>
18
+ {/each}
19
+ {/if}
20
+
21
+ <style>
22
+ .div {
23
+ display: flex;
24
+ gap: 1rem;
25
+ }
26
+ </style>
@@ -0,0 +1,87 @@
1
+ <script lang="ts">
2
+ import type { MEvent } from '@myko/core';
3
+ import { getContext } from 'svelte';
4
+ import {
5
+ fromISOMemo,
6
+ FULL_DATE_FORMAT,
7
+ isoToMillisMemo,
8
+ TRANSACTIONS_VIEW_STATE,
9
+ type TransactionsViewState
10
+ } from '../state/viewstate.svelte.js';
11
+
12
+ const { event }: { event: MEvent } = $props();
13
+
14
+ const timeMilis = $derived(isoToMillisMemo(event.createdAt));
15
+
16
+ const viewState = getContext(TRANSACTIONS_VIEW_STATE) as TransactionsViewState;
17
+
18
+ const leftTimeMilis = $derived(timeMilis - viewState.leftTimeMilis);
19
+
20
+ const leftPx = $derived(leftTimeMilis / viewState.durationMilisPerPx);
21
+
22
+ let showDetail = $state(false);
23
+
24
+ const onmouseover = () => {
25
+ showDetail = true;
26
+ };
27
+
28
+ const onmouseleave = () => {
29
+ showDetail = false;
30
+ };
31
+
32
+ const time = $derived(fromISOMemo(event.createdAt));
33
+ </script>
34
+
35
+ <div class="event {event.changeType}" style="left: {leftPx}px;">
36
+ {#if showDetail}
37
+ <div class="hover-data">
38
+ <p>{event.itemType}</p>
39
+ <p>{event.changeType}</p>
40
+ <p>{time.toFormat(FULL_DATE_FORMAT)}</p>
41
+ <pre>{JSON.stringify(event.item, null, 2)}</pre>
42
+ </div>
43
+ {/if}
44
+ <div
45
+ class="icon"
46
+ role="presentation"
47
+ {onmouseover}
48
+ {onmouseleave}
49
+ onfocus={onmouseover}
50
+ onblur={onmouseleave}
51
+ ></div>
52
+ </div>
53
+
54
+ <style>
55
+ .event {
56
+ position: relative;
57
+ width: 0;
58
+ height: 0;
59
+ }
60
+
61
+ .icon {
62
+ --size: 8px;
63
+ width: var(--size);
64
+ height: var(--size);
65
+ /* margin-left: calc(var(--size) / -2); */
66
+ border-left: 2px solid white;
67
+ margin-top: calc(var(--size) / -2);
68
+ /* border-radius: 50%; */
69
+ }
70
+
71
+ .DEL .icon {
72
+ background: linear-gradient(to right, orangered, transparent);
73
+ }
74
+
75
+ .SET .icon {
76
+ background: linear-gradient(to right, lightgreen, transparent);
77
+ }
78
+
79
+ .hover-data {
80
+ position: absolute;
81
+ right: 1rem;
82
+ z-index: 1000;
83
+ background-color: rgba(0, 0, 0, 0.5);
84
+ padding: 0.5rem;
85
+ border-radius: 0.25rem;
86
+ }
87
+ </style>
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ const { firstPx, lastPx, numEvents }: { firstPx: number; lastPx: number; numEvents: number } =
3
+ $props();
4
+
5
+ let showDetail = $state(false);
6
+
7
+ const onmouseover = () => {
8
+ showDetail = true;
9
+ };
10
+
11
+ const onmouseleave = () => {
12
+ showDetail = false;
13
+ };
14
+ </script>
15
+
16
+ <div class="event" style="left: {firstPx}px; width: {lastPx - firstPx}px;">
17
+ {#if showDetail}
18
+ <div class="hover-data">
19
+ <p>{numEvents} Events</p>
20
+ </div>
21
+ {/if}
22
+ <div
23
+ class="icon"
24
+ role="presentation"
25
+ {onmouseover}
26
+ {onmouseleave}
27
+ onfocus={onmouseover}
28
+ onblur={onmouseleave}
29
+ ></div>
30
+ </div>
31
+
32
+ <style>
33
+ .event {
34
+ position: relative;
35
+ width: 0;
36
+ height: 0;
37
+ }
38
+
39
+ .icon {
40
+ --size: 8px;
41
+ height: var(--size);
42
+ margin-left: calc(var(--size) / -2);
43
+ margin-top: calc(var(--size) / -2);
44
+ /* border-radius: 5px; */
45
+ background-color: cornflowerblue;
46
+ }
47
+
48
+ .hover-data {
49
+ position: absolute;
50
+ right: 1rem;
51
+ z-index: 1000;
52
+ background-color: rgba(0, 0, 0, 0.5);
53
+ padding: 0.5rem;
54
+ border-radius: 0.25rem;
55
+ }
56
+ </style>
@@ -0,0 +1,111 @@
1
+ <script lang="ts">
2
+ import { myko as client } from '../../services/svelte-client.svelte.js';
3
+ import { EntitySnapshotDifference, type ID } from '@myko/core';
4
+ import { setContext } from 'svelte';
5
+ import { watchResize } from 'svelte-watch-resize';
6
+ import { TRANSACTIONS_VIEW_STATE, TransactionsViewState } from '../state/viewstate.svelte';
7
+ import { windbackState } from '../state/windback.svelte.js';
8
+ import EntityHistory from './EntityHistory.svelte';
9
+ import TimeStrip from './TimeStrip.svelte';
10
+
11
+ const viewstate = new TransactionsViewState(windbackState);
12
+
13
+ setContext(TRANSACTIONS_VIEW_STATE, viewstate);
14
+
15
+ let isMouseDown = $state(false);
16
+
17
+ const {
18
+ entrypointId,
19
+ entrypointItemType
20
+ }: {
21
+ entrypointId: ID;
22
+ entrypointItemType: string;
23
+ } = $props();
24
+
25
+ const onwheel = (e: WheelEvent) => {
26
+ if (e.ctrlKey || e.metaKey) {
27
+ e.preventDefault();
28
+ viewstate.zoom(e.deltaY);
29
+ }
30
+
31
+ if (e.shiftKey) {
32
+ e.preventDefault();
33
+ viewstate.pan(e.deltaY);
34
+ }
35
+ };
36
+
37
+ const onmousemove = (e: MouseEvent) => {
38
+ viewstate.mouseX = e.clientX;
39
+ };
40
+
41
+ const onkeydown = (e: KeyboardEvent) => {
42
+ if (e.key === 'z') {
43
+ viewstate.zoomAllTheWayOut();
44
+ }
45
+ };
46
+
47
+ const onmousedown = (e: MouseEvent) => {
48
+ e.stopPropagation();
49
+ if (e.button === 0) {
50
+ }
51
+
52
+ viewstate.startDragWindback();
53
+ };
54
+
55
+ const onmouseup = (e: MouseEvent) => {
56
+ e.stopPropagation();
57
+
58
+ viewstate.stopDragWindback();
59
+ };
60
+
61
+ const diff = client.watchReport(
62
+ new EntitySnapshotDifference({
63
+ parentType: entrypointItemType,
64
+ parentId: entrypointId
65
+ })
66
+ );
67
+ </script>
68
+
69
+ <svelte:window {onkeydown} {onmousedown} {onmouseup} />
70
+ <div
71
+ class:windbackCursor={viewstate.isOverWindback}
72
+ class="transactions-frame extra class"
73
+ role="presentation"
74
+ use:watchResize={(e) => {
75
+ console.log('RESIZE', e.clientWidth);
76
+ viewstate.width = e.clientWidth;
77
+ }}
78
+ {onwheel}
79
+ {onmousemove}
80
+ >
81
+ <div class="header pad">
82
+ <TimeStrip></TimeStrip>
83
+ </div>
84
+ <div class="scroll pad">
85
+ <EntityHistory id={entrypointId} itemType={entrypointItemType} />
86
+ </div>
87
+ </div>
88
+
89
+ <style>
90
+ .transactions-frame {
91
+ height: 100%;
92
+ min-height: 100%;
93
+ overflow: hidden;
94
+ display: flex;
95
+ flex-direction: column;
96
+ justify-content: flex-start;
97
+ }
98
+
99
+ .scroll {
100
+ overflow: scroll;
101
+ scrollbar-width: none;
102
+ }
103
+
104
+ .header {
105
+ flex-shrink: 0;
106
+ }
107
+
108
+ .windbackCursor {
109
+ cursor: ew-resize;
110
+ }
111
+ </style>
@@ -0,0 +1,24 @@
1
+ <script lang="ts">
2
+ import type { ID } from '@myko/core';
3
+ import TransactionDetails from './TransactionDetails.svelte';
4
+
5
+ let open = $state(false);
6
+
7
+ const { tx }: { tx: ID } = $props();
8
+ </script>
9
+
10
+ <div
11
+ class="transaction"
12
+ role="presentation"
13
+ onclick={() => {
14
+ open = !open;
15
+ }}
16
+ >
17
+ <span>
18
+ {tx}
19
+ </span>
20
+
21
+ {#if open}
22
+ <TransactionDetails {tx}></TransactionDetails>
23
+ {/if}
24
+ </div>
@@ -0,0 +1,65 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { windbackState } from '../state/windback.svelte.js';
4
+ import Transactions from '../transactions/Transactions.svelte';
5
+
6
+ const { children }: { children: Snippet } = $props();
7
+ </script>
8
+
9
+ <div class="bg"></div>
10
+
11
+ <div class="windback">
12
+ <div class="windback-frame" class:windingBack={windbackState.windingBack}>
13
+ {@render children()}
14
+ </div>
15
+
16
+ <div class="windback-control" class:windingBack={windbackState.windingBack}>
17
+ {#if windbackState.ctx}
18
+ <Transactions
19
+ entrypointId={windbackState.ctx.id}
20
+ entrypointItemType={windbackState.ctx.itemType}
21
+ />
22
+ {/if}
23
+ </div>
24
+ </div>
25
+
26
+ <style>
27
+ .bg {
28
+ position: absolute;
29
+ top: 0;
30
+ left: 0;
31
+ width: 100%;
32
+ height: 100%;
33
+ z-index: -1;
34
+ }
35
+
36
+ .windback {
37
+ height: 100%;
38
+ width: 100%;
39
+ display: flex;
40
+ flex-direction: column;
41
+ }
42
+
43
+ .windback-frame {
44
+ width: 100%;
45
+ height: 100%;
46
+ background-color: black;
47
+ transition: filter 1s ease-in-out;
48
+ transition: height 1s ease-in-out;
49
+ &.windingBack {
50
+ filter: saturate(0.25);
51
+ height: 70%;
52
+ }
53
+ }
54
+
55
+ .windback-control {
56
+ background-color: rgba(0, 0, 0, 0.5);
57
+ z-index: 1000;
58
+ height: 0%;
59
+ transition: height 1s ease-in-out;
60
+
61
+ &.windingBack {
62
+ height: 30%;
63
+ }
64
+ }
65
+ </style>
@@ -0,0 +1 @@
1
+ export { default as WindbackFrame } from './WindbackFrame.svelte';
@@ -0,0 +1,26 @@
1
+ // Components
2
+ export { default as ConnectionStats } from './components/ConnectionStats.svelte';
3
+ export { default as Logs } from './components/Logs.svelte';
4
+ export { default as Query } from './components/Query.svelte';
5
+ export { default as Report } from './components/Report.svelte';
6
+ export { default as Search } from './components/Search.svelte';
7
+ export { default as ServerView } from './components/ServerView.svelte';
8
+ export { default as Transactions } from './components/transactions/Transactions.svelte';
9
+ export * from './components/state/windback.svelte.js';
10
+ export * from './components/windback/index.js';
11
+
12
+ // Svelte-friendly Myko client
13
+ export {
14
+ createMykoClient,
15
+ getMykoClient,
16
+ myko,
17
+ myko as client,
18
+ SvelteMykoClient,
19
+ type CommandError,
20
+ type CommandSuccess,
21
+ type LiveQuery,
22
+ type LiveReport
23
+ } from './services/svelte-client.svelte.js';
24
+
25
+ // Re-export useful types from @myko/core
26
+ export { ConnectionStatus, type ClientStats } from '@myko/core';