@xfey/tutti 0.1.30 → 0.1.32

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.
@@ -293,6 +293,7 @@ export async function startLaunchProject(options) {
293
293
  relayProjectRef: registered.summary.relay_project_ref,
294
294
  hostConnectionRef: registered.summary.host_connection_ref,
295
295
  hostConnectionToken: registered.hostConnectionToken,
296
+ heartbeatIntervalMs: registered.heartbeatIntervalMs,
296
297
  handleRequest: createHostProjectApiTunnelRequestHandler({
297
298
  app: host.app,
298
299
  trustedMetadataStore: host.trusted_relay_metadata_store,
@@ -18,6 +18,7 @@ export type RelayHostTunnelConnector = (options: ConnectRelayHostTunnelOptions)
18
18
  export type RegisteredRelayHostConnection = {
19
19
  summary: HostLaunchRelaySummary;
20
20
  hostConnectionToken: string;
21
+ heartbeatIntervalMs: number;
21
22
  };
22
23
  export type HostRelayRegistrationPreparation = Pick<LaunchPreparationResult, "tutti_home" | "project_id" | "workspace_root" | "relay_url">;
23
24
  export declare function relayErrorToLaunchError(error: unknown, relayUrl: string): LaunchError;
@@ -60,6 +60,7 @@ export async function registerHostWithRelay(options) {
60
60
  connected_at: result.host_connection.connected_at,
61
61
  },
62
62
  hostConnectionToken: result.host_connection_token,
63
+ heartbeatIntervalMs: result.host_connection.heartbeat_interval_ms,
63
64
  };
64
65
  }
65
66
  //# sourceMappingURL=relay-registration.js.map
@@ -34,11 +34,13 @@ export type RelayHostTunnelRequestHandler = (request: RelayHostTunnelRequest) =>
34
34
  export type RelayHostTunnelStreamRequestHandler = (request: RelayHostTunnelRequest, sink: RelayHostTunnelStreamSink) => Promise<void> | void;
35
35
  export type RelayHostTunnelWebSocketLike = {
36
36
  on(event: "open", listener: () => void): void;
37
+ on(event: "ping", listener: () => void): void;
37
38
  on(event: "message", listener: (data: unknown) => void): void;
38
39
  on(event: "close", listener: (code?: number, reason?: unknown) => void): void;
39
40
  on(event: "error", listener: (error: unknown) => void): void;
40
41
  send(data: string, callback?: (error?: Error) => void): void;
41
42
  close(code?: number, reason?: string): void;
43
+ terminate(): void;
42
44
  };
43
45
  export type RelayHostTunnelConnector = (options: {
44
46
  url: string;
@@ -58,6 +60,7 @@ export type ConnectRelayHostTunnelOptions = {
58
60
  relayProjectRef: RelayProjectRef;
59
61
  hostConnectionRef: HostConnectionRef;
60
62
  hostConnectionToken: string;
63
+ heartbeatIntervalMs: number;
61
64
  handleRequest: RelayHostTunnelRequestHandler;
62
65
  handleStreamRequest?: RelayHostTunnelStreamRequestHandler;
63
66
  connect?: RelayHostTunnelConnector;
@@ -2,6 +2,7 @@ import { createRequire } from "node:module";
2
2
  import { redactAndTruncateText } from "@tutti/shared/utils";
3
3
  import { chunkToBase64, internalErrorResponse, parseHostRequestFrame, parseHostStreamCancelFrame, sendFrame, sendResponse, } from "./tunnel-frames.js";
4
4
  const loadNativeModule = createRequire(import.meta.url);
5
+ const HOST_LIVENESS_INTERVAL_MULTIPLIER = 4;
5
6
  function defaultConnector(options) {
6
7
  const wsModule = loadNativeModule("ws");
7
8
  const WebSocketCtor = typeof wsModule === "function" ? wsModule : (wsModule.WebSocket ?? wsModule.default);
@@ -18,15 +19,23 @@ function tunnelUrl(options) {
18
19
  return url.toString();
19
20
  }
20
21
  export function connectRelayHostTunnel(options) {
22
+ if (!Number.isFinite(options.heartbeatIntervalMs) || options.heartbeatIntervalMs <= 0) {
23
+ throw new TypeError("Relay heartbeat interval must be a positive finite number");
24
+ }
21
25
  const socket = (options.connect ?? defaultConnector)({
22
26
  url: tunnelUrl(options),
23
27
  headers: {
24
28
  authorization: `Bearer ${options.hostConnectionToken}`,
25
29
  },
26
30
  });
31
+ const livenessTimeoutMs = options.heartbeatIntervalMs * HOST_LIVENESS_INTERVAL_MULTIPLIER;
27
32
  let resolveConnected;
28
33
  let rejectConnected;
29
34
  let connectedSettled = false;
35
+ let opened = false;
36
+ let ending = false;
37
+ let socketClosed = false;
38
+ let livenessTimer;
30
39
  const connected = new Promise((resolve, reject) => {
31
40
  resolveConnected = () => {
32
41
  connectedSettled = true;
@@ -42,10 +51,38 @@ export function connectRelayHostTunnel(options) {
42
51
  resolveClosed = resolve;
43
52
  });
44
53
  const streamControllers = new Map();
54
+ const clearLivenessDeadline = () => {
55
+ if (livenessTimer !== undefined) {
56
+ clearTimeout(livenessTimer);
57
+ livenessTimer = undefined;
58
+ }
59
+ };
60
+ const terminateSocket = () => {
61
+ if (ending || socketClosed) {
62
+ return;
63
+ }
64
+ ending = true;
65
+ clearLivenessDeadline();
66
+ socket.terminate();
67
+ };
68
+ const refreshLivenessDeadline = () => {
69
+ if (!opened || ending || socketClosed) {
70
+ return;
71
+ }
72
+ clearLivenessDeadline();
73
+ livenessTimer = setTimeout(terminateSocket, livenessTimeoutMs);
74
+ livenessTimer.unref?.();
75
+ };
45
76
  socket.on("open", () => {
77
+ opened = true;
78
+ refreshLivenessDeadline();
46
79
  resolveConnected();
47
80
  });
81
+ socket.on("ping", () => {
82
+ refreshLivenessDeadline();
83
+ });
48
84
  socket.on("message", (data) => {
85
+ refreshLivenessDeadline();
49
86
  const cancelRequestId = parseHostStreamCancelFrame(data);
50
87
  if (cancelRequestId !== null) {
51
88
  streamControllers.get(cancelRequestId)?.abort();
@@ -122,9 +159,19 @@ export function connectRelayHostTunnel(options) {
122
159
  socket.on("error", (error) => {
123
160
  if (!connectedSettled) {
124
161
  rejectConnected(error instanceof Error ? error : new Error("Relay host tunnel failed"));
162
+ return;
163
+ }
164
+ if (opened) {
165
+ terminateSocket();
125
166
  }
126
167
  });
127
168
  socket.on("close", (code, reason) => {
169
+ if (socketClosed) {
170
+ return;
171
+ }
172
+ socketClosed = true;
173
+ ending = true;
174
+ clearLivenessDeadline();
128
175
  for (const controller of streamControllers.values()) {
129
176
  controller.abort();
130
177
  }
@@ -148,6 +195,11 @@ export function connectRelayHostTunnel(options) {
148
195
  connected,
149
196
  closed,
150
197
  close: (code = 1000, reason = "closed") => {
198
+ if (ending || socketClosed) {
199
+ return;
200
+ }
201
+ ending = true;
202
+ clearLivenessDeadline();
151
203
  socket.close(code, reason);
152
204
  },
153
205
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfey/tutti",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -0,0 +1,62 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="2048" height="1180" viewBox="0 0 2048 1180" role="img" aria-labelledby="title description">
3
+ <title id="title">Beethoven — Symphony No. 5 / Opening — rhythmic modules</title>
4
+ <desc id="description">Phrase windows become a balanced, deterministically scattered set of blocks, circles, steps, dots and stripes.</desc>
5
+ <rect width="100%" height="100%" fill="#F8FAF9" />
6
+ <rect data-role="module-stripe" x="1137.35" y="691.93" width="183.59" height="21.35" rx="0.00" fill="#F0F2F1" opacity="0.822" />
7
+ <rect data-role="module-stripe" x="1137.35" y="728.65" width="183.59" height="21.35" rx="0.00" fill="#F0F2F1" opacity="0.822" />
8
+ <rect data-role="module-stripe" x="1137.35" y="765.37" width="183.59" height="21.35" rx="0.00" fill="#F0F2F1" opacity="0.822" />
9
+ <rect data-role="module-stripe" x="1137.35" y="802.09" width="183.59" height="21.35" rx="0.00" fill="#F0F2F1" opacity="0.822" />
10
+ <rect data-role="module-stripe" x="1137.35" y="838.81" width="183.59" height="21.35" rx="0.00" fill="#F0F2F1" opacity="0.822" />
11
+ <rect data-role="module-square" x="66.89" y="6.71" width="242.63" height="242.63" rx="0.00" fill="#E7EBE9" opacity="0.807" />
12
+ <ellipse data-role="module-dot" cx="1576.58" cy="708.56" rx="14.07" ry="14.07" fill="#E7EBE9" opacity="0.819" />
13
+ <ellipse data-role="module-dot" cx="1636.88" cy="708.56" rx="14.07" ry="14.07" fill="#E7EBE9" opacity="0.819" />
14
+ <ellipse data-role="module-dot" cx="1697.17" cy="708.56" rx="14.07" ry="14.07" fill="#E7EBE9" opacity="0.819" />
15
+ <ellipse data-role="module-dot" cx="1576.58" cy="768.86" rx="14.07" ry="14.07" fill="#E7EBE9" opacity="0.819" />
16
+ <ellipse data-role="module-dot" cx="1636.88" cy="768.86" rx="14.07" ry="14.07" fill="#E7EBE9" opacity="0.819" />
17
+ <ellipse data-role="module-dot" cx="1697.17" cy="768.86" rx="14.07" ry="14.07" fill="#E7EBE9" opacity="0.819" />
18
+ <ellipse data-role="module-dot" cx="1576.58" cy="829.16" rx="14.07" ry="14.07" fill="#E7EBE9" opacity="0.819" />
19
+ <ellipse data-role="module-dot" cx="1636.88" cy="829.16" rx="14.07" ry="14.07" fill="#E7EBE9" opacity="0.819" />
20
+ <ellipse data-role="module-dot" cx="1697.17" cy="829.16" rx="14.07" ry="14.07" fill="#E7EBE9" opacity="0.819" />
21
+ <ellipse data-role="module-circle" cx="1957.82" cy="533.45" rx="137.05" ry="137.05" fill="#DFE4E2" opacity="0.805" />
22
+ <polygon data-role="module-step" points="912.72,246.39 912.72,85.98 1036.91,85.98 1036.91,-12.34 1171.45,-12.34 1171.45,246.39" fill="#F0F2F1" opacity="0.807" />
23
+ <ellipse data-role="module-dot" cx="571.69" cy="32.03" rx="14.30" ry="14.30" fill="#F0F2F1" opacity="0.808" />
24
+ <ellipse data-role="module-dot" cx="632.99" cy="32.03" rx="14.30" ry="14.30" fill="#F0F2F1" opacity="0.808" />
25
+ <ellipse data-role="module-dot" cx="694.29" cy="32.03" rx="14.30" ry="14.30" fill="#F0F2F1" opacity="0.808" />
26
+ <ellipse data-role="module-dot" cx="571.69" cy="93.34" rx="14.30" ry="14.30" fill="#F0F2F1" opacity="0.808" />
27
+ <ellipse data-role="module-dot" cx="632.99" cy="93.34" rx="14.30" ry="14.30" fill="#F0F2F1" opacity="0.808" />
28
+ <ellipse data-role="module-dot" cx="694.29" cy="93.34" rx="14.30" ry="14.30" fill="#F0F2F1" opacity="0.808" />
29
+ <ellipse data-role="module-dot" cx="571.69" cy="154.64" rx="14.30" ry="14.30" fill="#F0F2F1" opacity="0.808" />
30
+ <ellipse data-role="module-dot" cx="632.99" cy="154.64" rx="14.30" ry="14.30" fill="#F0F2F1" opacity="0.808" />
31
+ <ellipse data-role="module-dot" cx="694.29" cy="154.64" rx="14.30" ry="14.30" fill="#F0F2F1" opacity="0.808" />
32
+ <rect data-role="module-square" x="1762.53" y="948.05" width="241.91" height="241.91" rx="0.00" fill="#EDF0EE" opacity="0.796" />
33
+ <rect data-role="module-square" x="811.20" y="520.41" width="241.45" height="241.45" rx="0.00" fill="#DFE4E2" opacity="0.796" />
34
+ <rect data-role="module-square" x="319.82" y="722.09" width="283.55" height="283.55" rx="0.00" fill="#DFE4E2" opacity="0.808" />
35
+ <polygon data-role="module-step" points="1832.59,134.41 1981.10,134.41 1981.10,249.39 2072.12,249.39 2072.12,373.94 1832.59,373.94" fill="#F0F2F1" opacity="0.805" />
36
+ <ellipse data-role="module-circle" cx="526.42" cy="417.47" rx="116.56" ry="116.56" fill="#E7EBE9" opacity="0.800" />
37
+ <polygon data-role="module-step" points="1410.90,1221.55 1259.09,1221.55 1259.09,1104.01 1166.04,1104.01 1166.04,976.69 1410.90,976.69" fill="#EDF0EE" opacity="0.803" />
38
+ <ellipse data-role="module-circle" cx="1404.78" cy="511.10" rx="120.93" ry="120.93" fill="#DFE4E2" opacity="0.803" />
39
+ <polygon data-role="module-step" points="213.74,991.79 213.74,1133.02 104.41,1133.02 104.41,1219.58 -14.04,1219.58 -14.04,991.79" fill="#E7EBE9" opacity="0.799" />
40
+ <ellipse data-role="module-dot" cx="1372.88" cy="71.41" rx="15.67" ry="15.67" fill="#EDF0EE" opacity="0.808" />
41
+ <ellipse data-role="module-dot" cx="1440.03" cy="71.41" rx="15.67" ry="15.67" fill="#EDF0EE" opacity="0.808" />
42
+ <ellipse data-role="module-dot" cx="1507.18" cy="71.41" rx="15.67" ry="15.67" fill="#EDF0EE" opacity="0.808" />
43
+ <ellipse data-role="module-dot" cx="1372.88" cy="138.55" rx="15.67" ry="15.67" fill="#EDF0EE" opacity="0.808" />
44
+ <ellipse data-role="module-dot" cx="1440.03" cy="138.55" rx="15.67" ry="15.67" fill="#EDF0EE" opacity="0.808" />
45
+ <ellipse data-role="module-dot" cx="1507.18" cy="138.55" rx="15.67" ry="15.67" fill="#EDF0EE" opacity="0.808" />
46
+ <ellipse data-role="module-dot" cx="1372.88" cy="205.70" rx="15.67" ry="15.67" fill="#EDF0EE" opacity="0.808" />
47
+ <ellipse data-role="module-dot" cx="1440.03" cy="205.70" rx="15.67" ry="15.67" fill="#EDF0EE" opacity="0.808" />
48
+ <ellipse data-role="module-dot" cx="1507.18" cy="205.70" rx="15.67" ry="15.67" fill="#EDF0EE" opacity="0.808" />
49
+ <rect data-role="module-stripe" x="8.18" y="441.70" width="22.25" height="229.64" rx="0.00" fill="#DFE4E2" opacity="0.802" />
50
+ <rect data-role="module-stripe" x="46.45" y="441.70" width="22.25" height="229.64" rx="0.00" fill="#DFE4E2" opacity="0.802" />
51
+ <rect data-role="module-stripe" x="84.73" y="441.70" width="22.25" height="229.64" rx="0.00" fill="#DFE4E2" opacity="0.802" />
52
+ <rect data-role="module-stripe" x="123.00" y="441.70" width="22.25" height="229.64" rx="0.00" fill="#DFE4E2" opacity="0.802" />
53
+ <rect data-role="module-stripe" x="161.27" y="441.70" width="22.25" height="229.64" rx="0.00" fill="#DFE4E2" opacity="0.802" />
54
+ <rect data-role="module-stripe" x="199.54" y="441.70" width="22.25" height="229.64" rx="0.00" fill="#DFE4E2" opacity="0.802" />
55
+ <rect data-role="module-stripe" x="746.51" y="966.35" width="24.83" height="213.57" rx="0.00" fill="#F0F2F1" opacity="0.819" />
56
+ <rect data-role="module-stripe" x="789.23" y="966.35" width="24.83" height="213.57" rx="0.00" fill="#F0F2F1" opacity="0.819" />
57
+ <rect data-role="module-stripe" x="831.94" y="966.35" width="24.83" height="213.57" rx="0.00" fill="#F0F2F1" opacity="0.819" />
58
+ <rect data-role="module-stripe" x="874.65" y="966.35" width="24.83" height="213.57" rx="0.00" fill="#F0F2F1" opacity="0.819" />
59
+ <rect data-role="module-stripe" x="917.37" y="966.35" width="24.83" height="213.57" rx="0.00" fill="#F0F2F1" opacity="0.819" />
60
+ <polygon data-role="edge-arc" points="0.00,1180.00 -0.00,822.29 23.40,823.05 46.69,825.35 69.79,829.16 92.58,834.48 114.98,841.27 136.89,849.52 158.21,859.18 178.86,870.21 198.73,882.57 217.76,896.21 235.86,911.06 252.94,927.06 268.94,944.14 283.79,962.24 297.43,981.27 309.79,1001.14 320.82,1021.79 330.48,1043.11 338.73,1065.02 345.52,1087.42 350.84,1110.21 354.65,1133.31 356.95,1156.60 357.71,1180.00" fill="#EDF0EE" opacity="0.640" />
61
+ <polygon data-role="edge-arc" points="2048.00,0.00 2048.00,381.79 2023.03,380.98 1998.17,378.53 1973.52,374.46 1949.18,368.79 1925.28,361.53 1901.89,352.73 1879.14,342.42 1857.10,330.64 1835.89,317.45 1815.58,302.90 1796.27,287.05 1778.03,269.97 1760.95,251.73 1745.10,232.42 1730.55,212.11 1717.36,190.90 1705.58,168.86 1695.27,146.11 1686.47,122.72 1679.21,98.82 1673.54,74.48 1669.47,49.83 1667.02,24.97 1666.21,0.00" fill="#DFE4E2" opacity="0.600" />
62
+ </svg>
@@ -0,0 +1,58 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="2048" height="1180" viewBox="0 0 2048 1180" role="img" aria-labelledby="title description">
3
+ <title id="title">Beethoven — Symphony No. 7 / Allegretto — rhythmic modules</title>
4
+ <desc id="description">Phrase windows become a balanced, deterministically scattered set of blocks, circles, steps, dots and stripes.</desc>
5
+ <rect width="100%" height="100%" fill="#F8FAF9" />
6
+ <ellipse data-role="module-dot" cx="4.75" cy="147.17" rx="13.01" ry="13.01" fill="#F0F2F1" opacity="0.774" />
7
+ <ellipse data-role="module-dot" cx="60.50" cy="147.17" rx="13.01" ry="13.01" fill="#F0F2F1" opacity="0.774" />
8
+ <ellipse data-role="module-dot" cx="116.25" cy="147.17" rx="13.01" ry="13.01" fill="#F0F2F1" opacity="0.774" />
9
+ <ellipse data-role="module-dot" cx="4.75" cy="202.91" rx="13.01" ry="13.01" fill="#F0F2F1" opacity="0.774" />
10
+ <ellipse data-role="module-dot" cx="60.50" cy="202.91" rx="13.01" ry="13.01" fill="#F0F2F1" opacity="0.774" />
11
+ <ellipse data-role="module-dot" cx="116.25" cy="202.91" rx="13.01" ry="13.01" fill="#F0F2F1" opacity="0.774" />
12
+ <ellipse data-role="module-dot" cx="4.75" cy="258.66" rx="13.01" ry="13.01" fill="#F0F2F1" opacity="0.774" />
13
+ <ellipse data-role="module-dot" cx="60.50" cy="258.66" rx="13.01" ry="13.01" fill="#F0F2F1" opacity="0.774" />
14
+ <ellipse data-role="module-dot" cx="116.25" cy="258.66" rx="13.01" ry="13.01" fill="#F0F2F1" opacity="0.774" />
15
+ <ellipse data-role="module-circle" cx="125.52" cy="712.10" rx="117.81" ry="117.81" fill="#E7EBE9" opacity="0.781" />
16
+ <ellipse data-role="module-dot" cx="1026.73" cy="531.40" rx="11.45" ry="11.45" fill="#E7EBE9" opacity="0.777" />
17
+ <ellipse data-role="module-dot" cx="1075.79" cy="531.40" rx="11.45" ry="11.45" fill="#E7EBE9" opacity="0.777" />
18
+ <ellipse data-role="module-dot" cx="1124.86" cy="531.40" rx="11.45" ry="11.45" fill="#E7EBE9" opacity="0.777" />
19
+ <ellipse data-role="module-dot" cx="1026.73" cy="580.47" rx="11.45" ry="11.45" fill="#E7EBE9" opacity="0.777" />
20
+ <ellipse data-role="module-dot" cx="1075.79" cy="580.47" rx="11.45" ry="11.45" fill="#E7EBE9" opacity="0.777" />
21
+ <ellipse data-role="module-dot" cx="1124.86" cy="580.47" rx="11.45" ry="11.45" fill="#E7EBE9" opacity="0.777" />
22
+ <ellipse data-role="module-dot" cx="1026.73" cy="629.54" rx="11.45" ry="11.45" fill="#E7EBE9" opacity="0.777" />
23
+ <ellipse data-role="module-dot" cx="1075.79" cy="629.54" rx="11.45" ry="11.45" fill="#E7EBE9" opacity="0.777" />
24
+ <ellipse data-role="module-dot" cx="1124.86" cy="629.54" rx="11.45" ry="11.45" fill="#E7EBE9" opacity="0.777" />
25
+ <ellipse data-role="module-circle" cx="565.46" cy="889.20" rx="95.25" ry="95.25" fill="#EDF0EE" opacity="0.785" />
26
+ <ellipse data-role="module-circle" cx="563.71" cy="302.72" rx="99.85" ry="99.85" fill="#DFE4E2" opacity="0.779" />
27
+ <rect data-role="module-stripe" x="895.25" y="910.83" width="25.17" height="216.47" rx="0.00" fill="#E7EBE9" opacity="0.795" />
28
+ <rect data-role="module-stripe" x="938.54" y="910.83" width="25.17" height="216.47" rx="0.00" fill="#E7EBE9" opacity="0.795" />
29
+ <rect data-role="module-stripe" x="981.83" y="910.83" width="25.17" height="216.47" rx="0.00" fill="#E7EBE9" opacity="0.795" />
30
+ <rect data-role="module-stripe" x="1025.13" y="910.83" width="25.17" height="216.47" rx="0.00" fill="#E7EBE9" opacity="0.795" />
31
+ <rect data-role="module-stripe" x="1068.42" y="910.83" width="25.17" height="216.47" rx="0.00" fill="#E7EBE9" opacity="0.795" />
32
+ <rect data-role="module-square" x="1367.27" y="637.80" width="203.02" height="203.02" rx="0.00" fill="#EDF0EE" opacity="0.786" />
33
+ <rect data-role="module-square" x="615.21" y="113.67" width="202.07" height="202.07" rx="0.00" fill="#EDF0EE" opacity="0.796" />
34
+ <rect data-role="module-square" x="1786.35" y="974.13" width="233.20" height="233.20" rx="0.00" fill="#DFE4E2" opacity="0.785" />
35
+ <rect data-role="module-stripe" x="470.19" y="7.84" width="24.41" height="209.88" rx="0.00" fill="#E7EBE9" opacity="0.794" />
36
+ <rect data-role="module-stripe" x="512.17" y="7.84" width="24.41" height="209.88" rx="0.00" fill="#E7EBE9" opacity="0.794" />
37
+ <rect data-role="module-stripe" x="554.15" y="7.84" width="24.41" height="209.88" rx="0.00" fill="#E7EBE9" opacity="0.794" />
38
+ <rect data-role="module-stripe" x="596.12" y="7.84" width="24.41" height="209.88" rx="0.00" fill="#E7EBE9" opacity="0.794" />
39
+ <rect data-role="module-stripe" x="638.10" y="7.84" width="24.41" height="209.88" rx="0.00" fill="#E7EBE9" opacity="0.794" />
40
+ <polygon data-role="module-step" points="1516.11,329.09 1350.08,329.09 1350.08,200.56 1248.33,200.56 1248.33,61.31 1516.11,61.31" fill="#E7EBE9" opacity="0.794" />
41
+ <rect data-role="module-stripe" x="1876.23" y="246.53" width="22.63" height="194.65" rx="0.00" fill="#EDF0EE" opacity="0.789" />
42
+ <rect data-role="module-stripe" x="1915.16" y="246.53" width="22.63" height="194.65" rx="0.00" fill="#EDF0EE" opacity="0.789" />
43
+ <rect data-role="module-stripe" x="1954.09" y="246.53" width="22.63" height="194.65" rx="0.00" fill="#EDF0EE" opacity="0.789" />
44
+ <rect data-role="module-stripe" x="1993.02" y="246.53" width="22.63" height="194.65" rx="0.00" fill="#EDF0EE" opacity="0.789" />
45
+ <rect data-role="module-stripe" x="2031.95" y="246.53" width="22.63" height="194.65" rx="0.00" fill="#EDF0EE" opacity="0.789" />
46
+ <polygon data-role="module-step" points="228.37,1057.21 79.79,1057.21 79.79,942.18 -11.27,942.18 -11.27,817.57 228.37,817.57" fill="#DFE4E2" opacity="0.792" />
47
+ <ellipse data-role="module-dot" cx="1356.55" cy="1063.59" rx="10.33" ry="10.33" fill="#F0F2F1" opacity="0.778" />
48
+ <ellipse data-role="module-dot" cx="1400.83" cy="1063.59" rx="10.33" ry="10.33" fill="#F0F2F1" opacity="0.778" />
49
+ <ellipse data-role="module-dot" cx="1445.11" cy="1063.59" rx="10.33" ry="10.33" fill="#F0F2F1" opacity="0.778" />
50
+ <ellipse data-role="module-dot" cx="1356.55" cy="1107.87" rx="10.33" ry="10.33" fill="#F0F2F1" opacity="0.778" />
51
+ <ellipse data-role="module-dot" cx="1400.83" cy="1107.87" rx="10.33" ry="10.33" fill="#F0F2F1" opacity="0.778" />
52
+ <ellipse data-role="module-dot" cx="1445.11" cy="1107.87" rx="10.33" ry="10.33" fill="#F0F2F1" opacity="0.778" />
53
+ <ellipse data-role="module-dot" cx="1356.55" cy="1152.15" rx="10.33" ry="10.33" fill="#F0F2F1" opacity="0.778" />
54
+ <ellipse data-role="module-dot" cx="1400.83" cy="1152.15" rx="10.33" ry="10.33" fill="#F0F2F1" opacity="0.778" />
55
+ <ellipse data-role="module-dot" cx="1445.11" cy="1152.15" rx="10.33" ry="10.33" fill="#F0F2F1" opacity="0.778" />
56
+ <polygon data-role="edge-arc" points="0.00,1180.00 -0.00,842.60 22.07,843.33 44.04,845.49 65.82,849.09 87.32,854.10 108.45,860.51 129.12,868.29 149.23,877.40 168.70,887.81 187.45,899.47 205.39,912.33 222.46,926.33 238.58,941.42 253.67,957.54 267.67,974.61 280.53,992.55 292.19,1011.30 302.60,1030.77 311.71,1050.88 319.49,1071.55 325.90,1092.68 330.91,1114.18 334.51,1135.96 336.67,1157.93 337.40,1180.00" fill="#EDF0EE" opacity="0.640" />
57
+ <polygon data-role="edge-arc" points="2048.00,0.00 2048.00,364.65 2024.15,363.87 2000.40,361.53 1976.86,357.64 1953.62,352.22 1930.79,345.29 1908.46,336.89 1886.72,327.04 1865.68,315.79 1845.41,303.19 1826.02,289.29 1807.57,274.16 1790.16,257.84 1773.84,240.43 1758.71,221.98 1744.81,202.59 1732.21,182.32 1720.96,161.28 1711.11,139.54 1702.71,117.21 1695.78,94.38 1690.36,71.14 1686.47,47.60 1684.13,23.85 1683.35,0.00" fill="#DFE4E2" opacity="0.600" />
58
+ </svg>
@@ -0,0 +1,50 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="2048" height="1180" viewBox="0 0 2048 1180" role="img" aria-labelledby="title description">
3
+ <title id="title">Dvořák — Symphony No. 9 / Largo — rhythmic modules</title>
4
+ <desc id="description">Phrase windows become a balanced, deterministically scattered set of blocks, circles, steps, dots and stripes.</desc>
5
+ <rect width="100%" height="100%" fill="#F8FAF9" />
6
+ <rect data-role="module-square" x="734.34" y="394.20" width="189.39" height="189.39" rx="0.00" fill="#F0F2F1" opacity="0.778" />
7
+ <rect data-role="module-stripe" x="1261.21" y="715.22" width="25.65" height="220.59" rx="0.00" fill="#F0F2F1" opacity="0.772" />
8
+ <rect data-role="module-stripe" x="1305.33" y="715.22" width="25.65" height="220.59" rx="0.00" fill="#F0F2F1" opacity="0.772" />
9
+ <rect data-role="module-stripe" x="1349.45" y="715.22" width="25.65" height="220.59" rx="0.00" fill="#F0F2F1" opacity="0.772" />
10
+ <rect data-role="module-stripe" x="1393.57" y="715.22" width="25.65" height="220.59" rx="0.00" fill="#F0F2F1" opacity="0.772" />
11
+ <rect data-role="module-stripe" x="1437.69" y="715.22" width="25.65" height="220.59" rx="0.00" fill="#F0F2F1" opacity="0.772" />
12
+ <ellipse data-role="module-circle" cx="92.51" cy="86.94" rx="128.49" ry="128.49" fill="#E7EBE9" opacity="0.767" />
13
+ <rect data-role="module-stripe" x="-36.14" y="795.14" width="25.70" height="221.05" rx="0.00" fill="#DFE4E2" opacity="0.781" />
14
+ <rect data-role="module-stripe" x="8.07" y="795.14" width="25.70" height="221.05" rx="0.00" fill="#DFE4E2" opacity="0.781" />
15
+ <rect data-role="module-stripe" x="52.28" y="795.14" width="25.70" height="221.05" rx="0.00" fill="#DFE4E2" opacity="0.781" />
16
+ <rect data-role="module-stripe" x="96.49" y="795.14" width="25.70" height="221.05" rx="0.00" fill="#DFE4E2" opacity="0.781" />
17
+ <rect data-role="module-stripe" x="140.70" y="795.14" width="25.70" height="221.05" rx="0.00" fill="#DFE4E2" opacity="0.781" />
18
+ <ellipse data-role="module-circle" cx="822.53" cy="920.14" rx="135.89" ry="135.89" fill="#F0F2F1" opacity="0.764" />
19
+ <polygon data-role="module-step" points="1383.27,386.80 1245.28,386.80 1245.28,279.98 1160.71,279.98 1160.71,164.25 1383.27,164.25" fill="#E7EBE9" opacity="0.777" />
20
+ <polygon data-role="module-step" points="1654.02,703.73 1654.02,569.62 1757.85,569.62 1757.85,487.43 1870.32,487.43 1870.32,703.73" fill="#E7EBE9" opacity="0.773" />
21
+ <ellipse data-role="module-dot" cx="592.14" cy="96.66" rx="14.16" ry="14.16" fill="#DFE4E2" opacity="0.764" />
22
+ <ellipse data-role="module-dot" cx="652.84" cy="96.66" rx="14.16" ry="14.16" fill="#DFE4E2" opacity="0.764" />
23
+ <ellipse data-role="module-dot" cx="713.54" cy="96.66" rx="14.16" ry="14.16" fill="#DFE4E2" opacity="0.764" />
24
+ <ellipse data-role="module-dot" cx="592.14" cy="157.35" rx="14.16" ry="14.16" fill="#DFE4E2" opacity="0.764" />
25
+ <ellipse data-role="module-dot" cx="652.84" cy="157.35" rx="14.16" ry="14.16" fill="#DFE4E2" opacity="0.764" />
26
+ <ellipse data-role="module-dot" cx="713.54" cy="157.35" rx="14.16" ry="14.16" fill="#DFE4E2" opacity="0.764" />
27
+ <ellipse data-role="module-dot" cx="592.14" cy="218.05" rx="14.16" ry="14.16" fill="#DFE4E2" opacity="0.764" />
28
+ <ellipse data-role="module-dot" cx="652.84" cy="218.05" rx="14.16" ry="14.16" fill="#DFE4E2" opacity="0.764" />
29
+ <ellipse data-role="module-dot" cx="713.54" cy="218.05" rx="14.16" ry="14.16" fill="#DFE4E2" opacity="0.764" />
30
+ <ellipse data-role="module-dot" cx="344.67" cy="651.92" rx="12.72" ry="12.72" fill="#F0F2F1" opacity="0.766" />
31
+ <ellipse data-role="module-dot" cx="399.20" cy="651.92" rx="12.72" ry="12.72" fill="#F0F2F1" opacity="0.766" />
32
+ <ellipse data-role="module-dot" cx="453.73" cy="651.92" rx="12.72" ry="12.72" fill="#F0F2F1" opacity="0.766" />
33
+ <ellipse data-role="module-dot" cx="344.67" cy="706.45" rx="12.72" ry="12.72" fill="#F0F2F1" opacity="0.766" />
34
+ <ellipse data-role="module-dot" cx="399.20" cy="706.45" rx="12.72" ry="12.72" fill="#F0F2F1" opacity="0.766" />
35
+ <ellipse data-role="module-dot" cx="453.73" cy="706.45" rx="12.72" ry="12.72" fill="#F0F2F1" opacity="0.766" />
36
+ <ellipse data-role="module-dot" cx="344.67" cy="760.98" rx="12.72" ry="12.72" fill="#F0F2F1" opacity="0.766" />
37
+ <ellipse data-role="module-dot" cx="399.20" cy="760.98" rx="12.72" ry="12.72" fill="#F0F2F1" opacity="0.766" />
38
+ <ellipse data-role="module-dot" cx="453.73" cy="760.98" rx="12.72" ry="12.72" fill="#F0F2F1" opacity="0.766" />
39
+ <ellipse data-role="module-circle" cx="1912.72" cy="156.82" rx="129.15" ry="129.15" fill="#DFE4E2" opacity="0.756" />
40
+ <polygon data-role="module-step" points="1747.33,961.00 1901.62,961.00 1901.62,1080.46 1996.18,1080.46 1996.18,1209.86 1747.33,1209.86" fill="#EDF0EE" opacity="0.789" />
41
+ <rect data-role="module-square" x="974.40" y="46.09" width="232.26" height="232.26" rx="0.00" fill="#EDF0EE" opacity="0.791" />
42
+ <rect data-role="module-square" x="299.88" y="414.22" width="217.38" height="217.38" rx="0.00" fill="#F0F2F1" opacity="0.788" />
43
+ <rect data-role="module-stripe" x="1729.70" y="315.79" width="25.15" height="216.28" rx="0.00" fill="#E7EBE9" opacity="0.789" />
44
+ <rect data-role="module-stripe" x="1772.96" y="315.79" width="25.15" height="216.28" rx="0.00" fill="#E7EBE9" opacity="0.789" />
45
+ <rect data-role="module-stripe" x="1816.21" y="315.79" width="25.15" height="216.28" rx="0.00" fill="#E7EBE9" opacity="0.789" />
46
+ <rect data-role="module-stripe" x="1859.47" y="315.79" width="25.15" height="216.28" rx="0.00" fill="#E7EBE9" opacity="0.789" />
47
+ <rect data-role="module-stripe" x="1902.72" y="315.79" width="25.15" height="216.28" rx="0.00" fill="#E7EBE9" opacity="0.789" />
48
+ <polygon data-role="edge-arc" points="2048.00,1180.00 1708.91,1180.00 1709.64,1157.82 1711.81,1135.74 1715.43,1113.85 1720.47,1092.24 1726.91,1071.00 1734.72,1050.24 1743.88,1030.03 1754.34,1010.46 1766.06,991.61 1778.98,973.58 1793.06,956.42 1808.23,940.23 1824.42,925.06 1841.58,910.98 1859.61,898.06 1878.46,886.34 1898.03,875.88 1918.24,866.72 1939.00,858.91 1960.24,852.47 1981.85,847.43 2003.74,843.81 2025.82,841.64 2048.00,840.91" fill="#EDF0EE" opacity="0.640" />
49
+ <polygon data-role="edge-arc" points="0.00,0.00 369.26,0.00 368.47,24.15 366.11,48.20 362.17,72.04 356.68,95.57 349.67,118.70 341.16,141.31 331.18,163.32 319.79,184.63 307.03,205.15 292.96,224.79 277.63,243.47 261.11,261.11 243.47,277.63 224.79,292.96 205.15,307.03 184.63,319.79 163.32,331.18 141.31,341.16 118.70,349.67 95.57,356.68 72.04,362.17 48.20,366.11 24.15,368.47 0.00,369.26" fill="#DFE4E2" opacity="0.600" />
50
+ </svg>