circal-mcp 0.1.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.
- package/README.md +123 -0
- package/dist/circal-mcp.mjs +4467 -0
- package/package.json +35 -0
|
@@ -0,0 +1,4467 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
+
try {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
} catch (e) {
|
|
12
|
+
throw mod = 0, e;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
|
|
32
|
+
// node_modules/.pnpm/react@19.2.8/node_modules/react/cjs/react.production.js
|
|
33
|
+
var require_react_production = __commonJS({
|
|
34
|
+
"node_modules/.pnpm/react@19.2.8/node_modules/react/cjs/react.production.js"(exports) {
|
|
35
|
+
"use strict";
|
|
36
|
+
var REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.transitional.element");
|
|
37
|
+
var REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for("react.portal");
|
|
38
|
+
var REACT_FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for("react.fragment");
|
|
39
|
+
var REACT_STRICT_MODE_TYPE = /* @__PURE__ */ Symbol.for("react.strict_mode");
|
|
40
|
+
var REACT_PROFILER_TYPE = /* @__PURE__ */ Symbol.for("react.profiler");
|
|
41
|
+
var REACT_CONSUMER_TYPE = /* @__PURE__ */ Symbol.for("react.consumer");
|
|
42
|
+
var REACT_CONTEXT_TYPE = /* @__PURE__ */ Symbol.for("react.context");
|
|
43
|
+
var REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref");
|
|
44
|
+
var REACT_SUSPENSE_TYPE = /* @__PURE__ */ Symbol.for("react.suspense");
|
|
45
|
+
var REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo");
|
|
46
|
+
var REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
|
|
47
|
+
var REACT_ACTIVITY_TYPE = /* @__PURE__ */ Symbol.for("react.activity");
|
|
48
|
+
var MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
|
|
49
|
+
function getIteratorFn(maybeIterable) {
|
|
50
|
+
if (null === maybeIterable || "object" !== typeof maybeIterable) return null;
|
|
51
|
+
maybeIterable = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable["@@iterator"];
|
|
52
|
+
return "function" === typeof maybeIterable ? maybeIterable : null;
|
|
53
|
+
}
|
|
54
|
+
var ReactNoopUpdateQueue = {
|
|
55
|
+
isMounted: function() {
|
|
56
|
+
return false;
|
|
57
|
+
},
|
|
58
|
+
enqueueForceUpdate: function() {
|
|
59
|
+
},
|
|
60
|
+
enqueueReplaceState: function() {
|
|
61
|
+
},
|
|
62
|
+
enqueueSetState: function() {
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var assign = Object.assign;
|
|
66
|
+
var emptyObject = {};
|
|
67
|
+
function Component(props, context, updater) {
|
|
68
|
+
this.props = props;
|
|
69
|
+
this.context = context;
|
|
70
|
+
this.refs = emptyObject;
|
|
71
|
+
this.updater = updater || ReactNoopUpdateQueue;
|
|
72
|
+
}
|
|
73
|
+
Component.prototype.isReactComponent = {};
|
|
74
|
+
Component.prototype.setState = function(partialState, callback) {
|
|
75
|
+
if ("object" !== typeof partialState && "function" !== typeof partialState && null != partialState)
|
|
76
|
+
throw Error(
|
|
77
|
+
"takes an object of state variables to update or a function which returns an object of state variables."
|
|
78
|
+
);
|
|
79
|
+
this.updater.enqueueSetState(this, partialState, callback, "setState");
|
|
80
|
+
};
|
|
81
|
+
Component.prototype.forceUpdate = function(callback) {
|
|
82
|
+
this.updater.enqueueForceUpdate(this, callback, "forceUpdate");
|
|
83
|
+
};
|
|
84
|
+
function ComponentDummy() {
|
|
85
|
+
}
|
|
86
|
+
ComponentDummy.prototype = Component.prototype;
|
|
87
|
+
function PureComponent(props, context, updater) {
|
|
88
|
+
this.props = props;
|
|
89
|
+
this.context = context;
|
|
90
|
+
this.refs = emptyObject;
|
|
91
|
+
this.updater = updater || ReactNoopUpdateQueue;
|
|
92
|
+
}
|
|
93
|
+
var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
|
|
94
|
+
pureComponentPrototype.constructor = PureComponent;
|
|
95
|
+
assign(pureComponentPrototype, Component.prototype);
|
|
96
|
+
pureComponentPrototype.isPureReactComponent = true;
|
|
97
|
+
var isArrayImpl = Array.isArray;
|
|
98
|
+
function noop() {
|
|
99
|
+
}
|
|
100
|
+
var ReactSharedInternals = { H: null, A: null, T: null, S: null };
|
|
101
|
+
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
102
|
+
function ReactElement(type, key, props) {
|
|
103
|
+
var refProp = props.ref;
|
|
104
|
+
return {
|
|
105
|
+
$$typeof: REACT_ELEMENT_TYPE,
|
|
106
|
+
type,
|
|
107
|
+
key,
|
|
108
|
+
ref: void 0 !== refProp ? refProp : null,
|
|
109
|
+
props
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function cloneAndReplaceKey(oldElement, newKey) {
|
|
113
|
+
return ReactElement(oldElement.type, newKey, oldElement.props);
|
|
114
|
+
}
|
|
115
|
+
function isValidElement(object) {
|
|
116
|
+
return "object" === typeof object && null !== object && object.$$typeof === REACT_ELEMENT_TYPE;
|
|
117
|
+
}
|
|
118
|
+
function escape(key) {
|
|
119
|
+
var escaperLookup = { "=": "=0", ":": "=2" };
|
|
120
|
+
return "$" + key.replace(/[=:]/g, function(match) {
|
|
121
|
+
return escaperLookup[match];
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
var userProvidedKeyEscapeRegex = /\/+/g;
|
|
125
|
+
function getElementKey(element, index) {
|
|
126
|
+
return "object" === typeof element && null !== element && null != element.key ? escape("" + element.key) : index.toString(36);
|
|
127
|
+
}
|
|
128
|
+
function resolveThenable(thenable) {
|
|
129
|
+
switch (thenable.status) {
|
|
130
|
+
case "fulfilled":
|
|
131
|
+
return thenable.value;
|
|
132
|
+
case "rejected":
|
|
133
|
+
throw thenable.reason;
|
|
134
|
+
default:
|
|
135
|
+
switch ("string" === typeof thenable.status ? thenable.then(noop, noop) : (thenable.status = "pending", thenable.then(
|
|
136
|
+
function(fulfilledValue) {
|
|
137
|
+
"pending" === thenable.status && (thenable.status = "fulfilled", thenable.value = fulfilledValue);
|
|
138
|
+
},
|
|
139
|
+
function(error) {
|
|
140
|
+
"pending" === thenable.status && (thenable.status = "rejected", thenable.reason = error);
|
|
141
|
+
}
|
|
142
|
+
)), thenable.status) {
|
|
143
|
+
case "fulfilled":
|
|
144
|
+
return thenable.value;
|
|
145
|
+
case "rejected":
|
|
146
|
+
throw thenable.reason;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
throw thenable;
|
|
150
|
+
}
|
|
151
|
+
function mapIntoArray(children, array2, escapedPrefix, nameSoFar, callback) {
|
|
152
|
+
var type = typeof children;
|
|
153
|
+
if ("undefined" === type || "boolean" === type) children = null;
|
|
154
|
+
var invokeCallback = false;
|
|
155
|
+
if (null === children) invokeCallback = true;
|
|
156
|
+
else
|
|
157
|
+
switch (type) {
|
|
158
|
+
case "bigint":
|
|
159
|
+
case "string":
|
|
160
|
+
case "number":
|
|
161
|
+
invokeCallback = true;
|
|
162
|
+
break;
|
|
163
|
+
case "object":
|
|
164
|
+
switch (children.$$typeof) {
|
|
165
|
+
case REACT_ELEMENT_TYPE:
|
|
166
|
+
case REACT_PORTAL_TYPE:
|
|
167
|
+
invokeCallback = true;
|
|
168
|
+
break;
|
|
169
|
+
case REACT_LAZY_TYPE:
|
|
170
|
+
return invokeCallback = children._init, mapIntoArray(
|
|
171
|
+
invokeCallback(children._payload),
|
|
172
|
+
array2,
|
|
173
|
+
escapedPrefix,
|
|
174
|
+
nameSoFar,
|
|
175
|
+
callback
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (invokeCallback)
|
|
180
|
+
return callback = callback(children), invokeCallback = "" === nameSoFar ? "." + getElementKey(children, 0) : nameSoFar, isArrayImpl(callback) ? (escapedPrefix = "", null != invokeCallback && (escapedPrefix = invokeCallback.replace(userProvidedKeyEscapeRegex, "$&/") + "/"), mapIntoArray(callback, array2, escapedPrefix, "", function(c) {
|
|
181
|
+
return c;
|
|
182
|
+
})) : null != callback && (isValidElement(callback) && (callback = cloneAndReplaceKey(
|
|
183
|
+
callback,
|
|
184
|
+
escapedPrefix + (null == callback.key || children && children.key === callback.key ? "" : ("" + callback.key).replace(
|
|
185
|
+
userProvidedKeyEscapeRegex,
|
|
186
|
+
"$&/"
|
|
187
|
+
) + "/") + invokeCallback
|
|
188
|
+
)), array2.push(callback)), 1;
|
|
189
|
+
invokeCallback = 0;
|
|
190
|
+
var nextNamePrefix = "" === nameSoFar ? "." : nameSoFar + ":";
|
|
191
|
+
if (isArrayImpl(children))
|
|
192
|
+
for (var i = 0; i < children.length; i++)
|
|
193
|
+
nameSoFar = children[i], type = nextNamePrefix + getElementKey(nameSoFar, i), invokeCallback += mapIntoArray(
|
|
194
|
+
nameSoFar,
|
|
195
|
+
array2,
|
|
196
|
+
escapedPrefix,
|
|
197
|
+
type,
|
|
198
|
+
callback
|
|
199
|
+
);
|
|
200
|
+
else if (i = getIteratorFn(children), "function" === typeof i)
|
|
201
|
+
for (children = i.call(children), i = 0; !(nameSoFar = children.next()).done; )
|
|
202
|
+
nameSoFar = nameSoFar.value, type = nextNamePrefix + getElementKey(nameSoFar, i++), invokeCallback += mapIntoArray(
|
|
203
|
+
nameSoFar,
|
|
204
|
+
array2,
|
|
205
|
+
escapedPrefix,
|
|
206
|
+
type,
|
|
207
|
+
callback
|
|
208
|
+
);
|
|
209
|
+
else if ("object" === type) {
|
|
210
|
+
if ("function" === typeof children.then)
|
|
211
|
+
return mapIntoArray(
|
|
212
|
+
resolveThenable(children),
|
|
213
|
+
array2,
|
|
214
|
+
escapedPrefix,
|
|
215
|
+
nameSoFar,
|
|
216
|
+
callback
|
|
217
|
+
);
|
|
218
|
+
array2 = String(children);
|
|
219
|
+
throw Error(
|
|
220
|
+
"Objects are not valid as a React child (found: " + ("[object Object]" === array2 ? "object with keys {" + Object.keys(children).join(", ") + "}" : array2) + "). If you meant to render a collection of children, use an array instead."
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return invokeCallback;
|
|
224
|
+
}
|
|
225
|
+
function mapChildren(children, func, context) {
|
|
226
|
+
if (null == children) return children;
|
|
227
|
+
var result = [], count = 0;
|
|
228
|
+
mapIntoArray(children, result, "", "", function(child) {
|
|
229
|
+
return func.call(context, child, count++);
|
|
230
|
+
});
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
function lazyInitializer(payload) {
|
|
234
|
+
if (-1 === payload._status) {
|
|
235
|
+
var ctor = payload._result;
|
|
236
|
+
ctor = ctor();
|
|
237
|
+
ctor.then(
|
|
238
|
+
function(moduleObject) {
|
|
239
|
+
if (0 === payload._status || -1 === payload._status)
|
|
240
|
+
payload._status = 1, payload._result = moduleObject;
|
|
241
|
+
},
|
|
242
|
+
function(error) {
|
|
243
|
+
if (0 === payload._status || -1 === payload._status)
|
|
244
|
+
payload._status = 2, payload._result = error;
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
-1 === payload._status && (payload._status = 0, payload._result = ctor);
|
|
248
|
+
}
|
|
249
|
+
if (1 === payload._status) return payload._result.default;
|
|
250
|
+
throw payload._result;
|
|
251
|
+
}
|
|
252
|
+
var reportGlobalError = "function" === typeof reportError ? reportError : function(error) {
|
|
253
|
+
if ("object" === typeof window && "function" === typeof window.ErrorEvent) {
|
|
254
|
+
var event = new window.ErrorEvent("error", {
|
|
255
|
+
bubbles: true,
|
|
256
|
+
cancelable: true,
|
|
257
|
+
message: "object" === typeof error && null !== error && "string" === typeof error.message ? String(error.message) : String(error),
|
|
258
|
+
error
|
|
259
|
+
});
|
|
260
|
+
if (!window.dispatchEvent(event)) return;
|
|
261
|
+
} else if ("object" === typeof process && "function" === typeof process.emit) {
|
|
262
|
+
process.emit("uncaughtException", error);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
console.error(error);
|
|
266
|
+
};
|
|
267
|
+
var Children = {
|
|
268
|
+
map: mapChildren,
|
|
269
|
+
forEach: function(children, forEachFunc, forEachContext) {
|
|
270
|
+
mapChildren(
|
|
271
|
+
children,
|
|
272
|
+
function() {
|
|
273
|
+
forEachFunc.apply(this, arguments);
|
|
274
|
+
},
|
|
275
|
+
forEachContext
|
|
276
|
+
);
|
|
277
|
+
},
|
|
278
|
+
count: function(children) {
|
|
279
|
+
var n = 0;
|
|
280
|
+
mapChildren(children, function() {
|
|
281
|
+
n++;
|
|
282
|
+
});
|
|
283
|
+
return n;
|
|
284
|
+
},
|
|
285
|
+
toArray: function(children) {
|
|
286
|
+
return mapChildren(children, function(child) {
|
|
287
|
+
return child;
|
|
288
|
+
}) || [];
|
|
289
|
+
},
|
|
290
|
+
only: function(children) {
|
|
291
|
+
if (!isValidElement(children))
|
|
292
|
+
throw Error(
|
|
293
|
+
"React.Children.only expected to receive a single React element child."
|
|
294
|
+
);
|
|
295
|
+
return children;
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
exports.Activity = REACT_ACTIVITY_TYPE;
|
|
299
|
+
exports.Children = Children;
|
|
300
|
+
exports.Component = Component;
|
|
301
|
+
exports.Fragment = REACT_FRAGMENT_TYPE;
|
|
302
|
+
exports.Profiler = REACT_PROFILER_TYPE;
|
|
303
|
+
exports.PureComponent = PureComponent;
|
|
304
|
+
exports.StrictMode = REACT_STRICT_MODE_TYPE;
|
|
305
|
+
exports.Suspense = REACT_SUSPENSE_TYPE;
|
|
306
|
+
exports.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = ReactSharedInternals;
|
|
307
|
+
exports.__COMPILER_RUNTIME = {
|
|
308
|
+
__proto__: null,
|
|
309
|
+
c: function(size) {
|
|
310
|
+
return ReactSharedInternals.H.useMemoCache(size);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
exports.cache = function(fn) {
|
|
314
|
+
return function() {
|
|
315
|
+
return fn.apply(null, arguments);
|
|
316
|
+
};
|
|
317
|
+
};
|
|
318
|
+
exports.cacheSignal = function() {
|
|
319
|
+
return null;
|
|
320
|
+
};
|
|
321
|
+
exports.cloneElement = function(element, config, children) {
|
|
322
|
+
if (null === element || void 0 === element)
|
|
323
|
+
throw Error(
|
|
324
|
+
"The argument must be a React element, but you passed " + element + "."
|
|
325
|
+
);
|
|
326
|
+
var props = assign({}, element.props), key = element.key;
|
|
327
|
+
if (null != config)
|
|
328
|
+
for (propName in void 0 !== config.key && (key = "" + config.key), config)
|
|
329
|
+
!hasOwnProperty.call(config, propName) || "key" === propName || "__self" === propName || "__source" === propName || "ref" === propName && void 0 === config.ref || (props[propName] = config[propName]);
|
|
330
|
+
var propName = arguments.length - 2;
|
|
331
|
+
if (1 === propName) props.children = children;
|
|
332
|
+
else if (1 < propName) {
|
|
333
|
+
for (var childArray = Array(propName), i = 0; i < propName; i++)
|
|
334
|
+
childArray[i] = arguments[i + 2];
|
|
335
|
+
props.children = childArray;
|
|
336
|
+
}
|
|
337
|
+
return ReactElement(element.type, key, props);
|
|
338
|
+
};
|
|
339
|
+
exports.createContext = function(defaultValue) {
|
|
340
|
+
defaultValue = {
|
|
341
|
+
$$typeof: REACT_CONTEXT_TYPE,
|
|
342
|
+
_currentValue: defaultValue,
|
|
343
|
+
_currentValue2: defaultValue,
|
|
344
|
+
_threadCount: 0,
|
|
345
|
+
Provider: null,
|
|
346
|
+
Consumer: null
|
|
347
|
+
};
|
|
348
|
+
defaultValue.Provider = defaultValue;
|
|
349
|
+
defaultValue.Consumer = {
|
|
350
|
+
$$typeof: REACT_CONSUMER_TYPE,
|
|
351
|
+
_context: defaultValue
|
|
352
|
+
};
|
|
353
|
+
return defaultValue;
|
|
354
|
+
};
|
|
355
|
+
exports.createElement = function(type, config, children) {
|
|
356
|
+
var propName, props = {}, key = null;
|
|
357
|
+
if (null != config)
|
|
358
|
+
for (propName in void 0 !== config.key && (key = "" + config.key), config)
|
|
359
|
+
hasOwnProperty.call(config, propName) && "key" !== propName && "__self" !== propName && "__source" !== propName && (props[propName] = config[propName]);
|
|
360
|
+
var childrenLength = arguments.length - 2;
|
|
361
|
+
if (1 === childrenLength) props.children = children;
|
|
362
|
+
else if (1 < childrenLength) {
|
|
363
|
+
for (var childArray = Array(childrenLength), i = 0; i < childrenLength; i++)
|
|
364
|
+
childArray[i] = arguments[i + 2];
|
|
365
|
+
props.children = childArray;
|
|
366
|
+
}
|
|
367
|
+
if (type && type.defaultProps)
|
|
368
|
+
for (propName in childrenLength = type.defaultProps, childrenLength)
|
|
369
|
+
void 0 === props[propName] && (props[propName] = childrenLength[propName]);
|
|
370
|
+
return ReactElement(type, key, props);
|
|
371
|
+
};
|
|
372
|
+
exports.createRef = function() {
|
|
373
|
+
return { current: null };
|
|
374
|
+
};
|
|
375
|
+
exports.forwardRef = function(render) {
|
|
376
|
+
return { $$typeof: REACT_FORWARD_REF_TYPE, render };
|
|
377
|
+
};
|
|
378
|
+
exports.isValidElement = isValidElement;
|
|
379
|
+
exports.lazy = function(ctor) {
|
|
380
|
+
return {
|
|
381
|
+
$$typeof: REACT_LAZY_TYPE,
|
|
382
|
+
_payload: { _status: -1, _result: ctor },
|
|
383
|
+
_init: lazyInitializer
|
|
384
|
+
};
|
|
385
|
+
};
|
|
386
|
+
exports.memo = function(type, compare) {
|
|
387
|
+
return {
|
|
388
|
+
$$typeof: REACT_MEMO_TYPE,
|
|
389
|
+
type,
|
|
390
|
+
compare: void 0 === compare ? null : compare
|
|
391
|
+
};
|
|
392
|
+
};
|
|
393
|
+
exports.startTransition = function(scope) {
|
|
394
|
+
var prevTransition = ReactSharedInternals.T, currentTransition = {};
|
|
395
|
+
ReactSharedInternals.T = currentTransition;
|
|
396
|
+
try {
|
|
397
|
+
var returnValue = scope(), onStartTransitionFinish = ReactSharedInternals.S;
|
|
398
|
+
null !== onStartTransitionFinish && onStartTransitionFinish(currentTransition, returnValue);
|
|
399
|
+
"object" === typeof returnValue && null !== returnValue && "function" === typeof returnValue.then && returnValue.then(noop, reportGlobalError);
|
|
400
|
+
} catch (error) {
|
|
401
|
+
reportGlobalError(error);
|
|
402
|
+
} finally {
|
|
403
|
+
null !== prevTransition && null !== currentTransition.types && (prevTransition.types = currentTransition.types), ReactSharedInternals.T = prevTransition;
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
exports.unstable_useCacheRefresh = function() {
|
|
407
|
+
return ReactSharedInternals.H.useCacheRefresh();
|
|
408
|
+
};
|
|
409
|
+
exports.use = function(usable) {
|
|
410
|
+
return ReactSharedInternals.H.use(usable);
|
|
411
|
+
};
|
|
412
|
+
exports.useActionState = function(action, initialState, permalink) {
|
|
413
|
+
return ReactSharedInternals.H.useActionState(action, initialState, permalink);
|
|
414
|
+
};
|
|
415
|
+
exports.useCallback = function(callback, deps) {
|
|
416
|
+
return ReactSharedInternals.H.useCallback(callback, deps);
|
|
417
|
+
};
|
|
418
|
+
exports.useContext = function(Context) {
|
|
419
|
+
return ReactSharedInternals.H.useContext(Context);
|
|
420
|
+
};
|
|
421
|
+
exports.useDebugValue = function() {
|
|
422
|
+
};
|
|
423
|
+
exports.useDeferredValue = function(value, initialValue) {
|
|
424
|
+
return ReactSharedInternals.H.useDeferredValue(value, initialValue);
|
|
425
|
+
};
|
|
426
|
+
exports.useEffect = function(create, deps) {
|
|
427
|
+
return ReactSharedInternals.H.useEffect(create, deps);
|
|
428
|
+
};
|
|
429
|
+
exports.useEffectEvent = function(callback) {
|
|
430
|
+
return ReactSharedInternals.H.useEffectEvent(callback);
|
|
431
|
+
};
|
|
432
|
+
exports.useId = function() {
|
|
433
|
+
return ReactSharedInternals.H.useId();
|
|
434
|
+
};
|
|
435
|
+
exports.useImperativeHandle = function(ref, create, deps) {
|
|
436
|
+
return ReactSharedInternals.H.useImperativeHandle(ref, create, deps);
|
|
437
|
+
};
|
|
438
|
+
exports.useInsertionEffect = function(create, deps) {
|
|
439
|
+
return ReactSharedInternals.H.useInsertionEffect(create, deps);
|
|
440
|
+
};
|
|
441
|
+
exports.useLayoutEffect = function(create, deps) {
|
|
442
|
+
return ReactSharedInternals.H.useLayoutEffect(create, deps);
|
|
443
|
+
};
|
|
444
|
+
exports.useMemo = function(create, deps) {
|
|
445
|
+
return ReactSharedInternals.H.useMemo(create, deps);
|
|
446
|
+
};
|
|
447
|
+
exports.useOptimistic = function(passthrough, reducer) {
|
|
448
|
+
return ReactSharedInternals.H.useOptimistic(passthrough, reducer);
|
|
449
|
+
};
|
|
450
|
+
exports.useReducer = function(reducer, initialArg, init) {
|
|
451
|
+
return ReactSharedInternals.H.useReducer(reducer, initialArg, init);
|
|
452
|
+
};
|
|
453
|
+
exports.useRef = function(initialValue) {
|
|
454
|
+
return ReactSharedInternals.H.useRef(initialValue);
|
|
455
|
+
};
|
|
456
|
+
exports.useState = function(initialState) {
|
|
457
|
+
return ReactSharedInternals.H.useState(initialState);
|
|
458
|
+
};
|
|
459
|
+
exports.useSyncExternalStore = function(subscribe, getSnapshot, getServerSnapshot) {
|
|
460
|
+
return ReactSharedInternals.H.useSyncExternalStore(
|
|
461
|
+
subscribe,
|
|
462
|
+
getSnapshot,
|
|
463
|
+
getServerSnapshot
|
|
464
|
+
);
|
|
465
|
+
};
|
|
466
|
+
exports.useTransition = function() {
|
|
467
|
+
return ReactSharedInternals.H.useTransition();
|
|
468
|
+
};
|
|
469
|
+
exports.version = "19.2.8";
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// node_modules/.pnpm/react@19.2.8/node_modules/react/cjs/react.development.js
|
|
474
|
+
var require_react_development = __commonJS({
|
|
475
|
+
"node_modules/.pnpm/react@19.2.8/node_modules/react/cjs/react.development.js"(exports, module) {
|
|
476
|
+
"use strict";
|
|
477
|
+
"production" !== process.env.NODE_ENV && (function() {
|
|
478
|
+
function defineDeprecationWarning(methodName, info) {
|
|
479
|
+
Object.defineProperty(Component.prototype, methodName, {
|
|
480
|
+
get: function() {
|
|
481
|
+
console.warn(
|
|
482
|
+
"%s(...) is deprecated in plain JavaScript React classes. %s",
|
|
483
|
+
info[0],
|
|
484
|
+
info[1]
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
function getIteratorFn(maybeIterable) {
|
|
490
|
+
if (null === maybeIterable || "object" !== typeof maybeIterable)
|
|
491
|
+
return null;
|
|
492
|
+
maybeIterable = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable["@@iterator"];
|
|
493
|
+
return "function" === typeof maybeIterable ? maybeIterable : null;
|
|
494
|
+
}
|
|
495
|
+
function warnNoop(publicInstance, callerName) {
|
|
496
|
+
publicInstance = (publicInstance = publicInstance.constructor) && (publicInstance.displayName || publicInstance.name) || "ReactClass";
|
|
497
|
+
var warningKey = publicInstance + "." + callerName;
|
|
498
|
+
didWarnStateUpdateForUnmountedComponent[warningKey] || (console.error(
|
|
499
|
+
"Can't call %s on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the %s component.",
|
|
500
|
+
callerName,
|
|
501
|
+
publicInstance
|
|
502
|
+
), didWarnStateUpdateForUnmountedComponent[warningKey] = true);
|
|
503
|
+
}
|
|
504
|
+
function Component(props, context, updater) {
|
|
505
|
+
this.props = props;
|
|
506
|
+
this.context = context;
|
|
507
|
+
this.refs = emptyObject;
|
|
508
|
+
this.updater = updater || ReactNoopUpdateQueue;
|
|
509
|
+
}
|
|
510
|
+
function ComponentDummy() {
|
|
511
|
+
}
|
|
512
|
+
function PureComponent(props, context, updater) {
|
|
513
|
+
this.props = props;
|
|
514
|
+
this.context = context;
|
|
515
|
+
this.refs = emptyObject;
|
|
516
|
+
this.updater = updater || ReactNoopUpdateQueue;
|
|
517
|
+
}
|
|
518
|
+
function noop() {
|
|
519
|
+
}
|
|
520
|
+
function testStringCoercion(value) {
|
|
521
|
+
return "" + value;
|
|
522
|
+
}
|
|
523
|
+
function checkKeyStringCoercion(value) {
|
|
524
|
+
try {
|
|
525
|
+
testStringCoercion(value);
|
|
526
|
+
var JSCompiler_inline_result = false;
|
|
527
|
+
} catch (e) {
|
|
528
|
+
JSCompiler_inline_result = true;
|
|
529
|
+
}
|
|
530
|
+
if (JSCompiler_inline_result) {
|
|
531
|
+
JSCompiler_inline_result = console;
|
|
532
|
+
var JSCompiler_temp_const = JSCompiler_inline_result.error;
|
|
533
|
+
var JSCompiler_inline_result$jscomp$0 = "function" === typeof Symbol && Symbol.toStringTag && value[Symbol.toStringTag] || value.constructor.name || "Object";
|
|
534
|
+
JSCompiler_temp_const.call(
|
|
535
|
+
JSCompiler_inline_result,
|
|
536
|
+
"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
|
|
537
|
+
JSCompiler_inline_result$jscomp$0
|
|
538
|
+
);
|
|
539
|
+
return testStringCoercion(value);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function getComponentNameFromType(type) {
|
|
543
|
+
if (null == type) return null;
|
|
544
|
+
if ("function" === typeof type)
|
|
545
|
+
return type.$$typeof === REACT_CLIENT_REFERENCE ? null : type.displayName || type.name || null;
|
|
546
|
+
if ("string" === typeof type) return type;
|
|
547
|
+
switch (type) {
|
|
548
|
+
case REACT_FRAGMENT_TYPE:
|
|
549
|
+
return "Fragment";
|
|
550
|
+
case REACT_PROFILER_TYPE:
|
|
551
|
+
return "Profiler";
|
|
552
|
+
case REACT_STRICT_MODE_TYPE:
|
|
553
|
+
return "StrictMode";
|
|
554
|
+
case REACT_SUSPENSE_TYPE:
|
|
555
|
+
return "Suspense";
|
|
556
|
+
case REACT_SUSPENSE_LIST_TYPE:
|
|
557
|
+
return "SuspenseList";
|
|
558
|
+
case REACT_ACTIVITY_TYPE:
|
|
559
|
+
return "Activity";
|
|
560
|
+
}
|
|
561
|
+
if ("object" === typeof type)
|
|
562
|
+
switch ("number" === typeof type.tag && console.error(
|
|
563
|
+
"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
|
|
564
|
+
), type.$$typeof) {
|
|
565
|
+
case REACT_PORTAL_TYPE:
|
|
566
|
+
return "Portal";
|
|
567
|
+
case REACT_CONTEXT_TYPE:
|
|
568
|
+
return type.displayName || "Context";
|
|
569
|
+
case REACT_CONSUMER_TYPE:
|
|
570
|
+
return (type._context.displayName || "Context") + ".Consumer";
|
|
571
|
+
case REACT_FORWARD_REF_TYPE:
|
|
572
|
+
var innerType = type.render;
|
|
573
|
+
type = type.displayName;
|
|
574
|
+
type || (type = innerType.displayName || innerType.name || "", type = "" !== type ? "ForwardRef(" + type + ")" : "ForwardRef");
|
|
575
|
+
return type;
|
|
576
|
+
case REACT_MEMO_TYPE:
|
|
577
|
+
return innerType = type.displayName || null, null !== innerType ? innerType : getComponentNameFromType(type.type) || "Memo";
|
|
578
|
+
case REACT_LAZY_TYPE:
|
|
579
|
+
innerType = type._payload;
|
|
580
|
+
type = type._init;
|
|
581
|
+
try {
|
|
582
|
+
return getComponentNameFromType(type(innerType));
|
|
583
|
+
} catch (x) {
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
function getTaskName(type) {
|
|
589
|
+
if (type === REACT_FRAGMENT_TYPE) return "<>";
|
|
590
|
+
if ("object" === typeof type && null !== type && type.$$typeof === REACT_LAZY_TYPE)
|
|
591
|
+
return "<...>";
|
|
592
|
+
try {
|
|
593
|
+
var name = getComponentNameFromType(type);
|
|
594
|
+
return name ? "<" + name + ">" : "<...>";
|
|
595
|
+
} catch (x) {
|
|
596
|
+
return "<...>";
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function getOwner() {
|
|
600
|
+
var dispatcher = ReactSharedInternals.A;
|
|
601
|
+
return null === dispatcher ? null : dispatcher.getOwner();
|
|
602
|
+
}
|
|
603
|
+
function UnknownOwner() {
|
|
604
|
+
return Error("react-stack-top-frame");
|
|
605
|
+
}
|
|
606
|
+
function hasValidKey(config) {
|
|
607
|
+
if (hasOwnProperty.call(config, "key")) {
|
|
608
|
+
var getter = Object.getOwnPropertyDescriptor(config, "key").get;
|
|
609
|
+
if (getter && getter.isReactWarning) return false;
|
|
610
|
+
}
|
|
611
|
+
return void 0 !== config.key;
|
|
612
|
+
}
|
|
613
|
+
function defineKeyPropWarningGetter(props, displayName) {
|
|
614
|
+
function warnAboutAccessingKey() {
|
|
615
|
+
specialPropKeyWarningShown || (specialPropKeyWarningShown = true, console.error(
|
|
616
|
+
"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
|
|
617
|
+
displayName
|
|
618
|
+
));
|
|
619
|
+
}
|
|
620
|
+
warnAboutAccessingKey.isReactWarning = true;
|
|
621
|
+
Object.defineProperty(props, "key", {
|
|
622
|
+
get: warnAboutAccessingKey,
|
|
623
|
+
configurable: true
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
function elementRefGetterWithDeprecationWarning() {
|
|
627
|
+
var componentName = getComponentNameFromType(this.type);
|
|
628
|
+
didWarnAboutElementRef[componentName] || (didWarnAboutElementRef[componentName] = true, console.error(
|
|
629
|
+
"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
|
|
630
|
+
));
|
|
631
|
+
componentName = this.props.ref;
|
|
632
|
+
return void 0 !== componentName ? componentName : null;
|
|
633
|
+
}
|
|
634
|
+
function ReactElement(type, key, props, owner, debugStack, debugTask) {
|
|
635
|
+
var refProp = props.ref;
|
|
636
|
+
type = {
|
|
637
|
+
$$typeof: REACT_ELEMENT_TYPE,
|
|
638
|
+
type,
|
|
639
|
+
key,
|
|
640
|
+
props,
|
|
641
|
+
_owner: owner
|
|
642
|
+
};
|
|
643
|
+
null !== (void 0 !== refProp ? refProp : null) ? Object.defineProperty(type, "ref", {
|
|
644
|
+
enumerable: false,
|
|
645
|
+
get: elementRefGetterWithDeprecationWarning
|
|
646
|
+
}) : Object.defineProperty(type, "ref", { enumerable: false, value: null });
|
|
647
|
+
type._store = {};
|
|
648
|
+
Object.defineProperty(type._store, "validated", {
|
|
649
|
+
configurable: false,
|
|
650
|
+
enumerable: false,
|
|
651
|
+
writable: true,
|
|
652
|
+
value: 0
|
|
653
|
+
});
|
|
654
|
+
Object.defineProperty(type, "_debugInfo", {
|
|
655
|
+
configurable: false,
|
|
656
|
+
enumerable: false,
|
|
657
|
+
writable: true,
|
|
658
|
+
value: null
|
|
659
|
+
});
|
|
660
|
+
Object.defineProperty(type, "_debugStack", {
|
|
661
|
+
configurable: false,
|
|
662
|
+
enumerable: false,
|
|
663
|
+
writable: true,
|
|
664
|
+
value: debugStack
|
|
665
|
+
});
|
|
666
|
+
Object.defineProperty(type, "_debugTask", {
|
|
667
|
+
configurable: false,
|
|
668
|
+
enumerable: false,
|
|
669
|
+
writable: true,
|
|
670
|
+
value: debugTask
|
|
671
|
+
});
|
|
672
|
+
Object.freeze && (Object.freeze(type.props), Object.freeze(type));
|
|
673
|
+
return type;
|
|
674
|
+
}
|
|
675
|
+
function cloneAndReplaceKey(oldElement, newKey) {
|
|
676
|
+
newKey = ReactElement(
|
|
677
|
+
oldElement.type,
|
|
678
|
+
newKey,
|
|
679
|
+
oldElement.props,
|
|
680
|
+
oldElement._owner,
|
|
681
|
+
oldElement._debugStack,
|
|
682
|
+
oldElement._debugTask
|
|
683
|
+
);
|
|
684
|
+
oldElement._store && (newKey._store.validated = oldElement._store.validated);
|
|
685
|
+
return newKey;
|
|
686
|
+
}
|
|
687
|
+
function validateChildKeys(node) {
|
|
688
|
+
isValidElement(node) ? node._store && (node._store.validated = 1) : "object" === typeof node && null !== node && node.$$typeof === REACT_LAZY_TYPE && ("fulfilled" === node._payload.status ? isValidElement(node._payload.value) && node._payload.value._store && (node._payload.value._store.validated = 1) : node._store && (node._store.validated = 1));
|
|
689
|
+
}
|
|
690
|
+
function isValidElement(object) {
|
|
691
|
+
return "object" === typeof object && null !== object && object.$$typeof === REACT_ELEMENT_TYPE;
|
|
692
|
+
}
|
|
693
|
+
function escape(key) {
|
|
694
|
+
var escaperLookup = { "=": "=0", ":": "=2" };
|
|
695
|
+
return "$" + key.replace(/[=:]/g, function(match) {
|
|
696
|
+
return escaperLookup[match];
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
function getElementKey(element, index) {
|
|
700
|
+
return "object" === typeof element && null !== element && null != element.key ? (checkKeyStringCoercion(element.key), escape("" + element.key)) : index.toString(36);
|
|
701
|
+
}
|
|
702
|
+
function resolveThenable(thenable) {
|
|
703
|
+
switch (thenable.status) {
|
|
704
|
+
case "fulfilled":
|
|
705
|
+
return thenable.value;
|
|
706
|
+
case "rejected":
|
|
707
|
+
throw thenable.reason;
|
|
708
|
+
default:
|
|
709
|
+
switch ("string" === typeof thenable.status ? thenable.then(noop, noop) : (thenable.status = "pending", thenable.then(
|
|
710
|
+
function(fulfilledValue) {
|
|
711
|
+
"pending" === thenable.status && (thenable.status = "fulfilled", thenable.value = fulfilledValue);
|
|
712
|
+
},
|
|
713
|
+
function(error) {
|
|
714
|
+
"pending" === thenable.status && (thenable.status = "rejected", thenable.reason = error);
|
|
715
|
+
}
|
|
716
|
+
)), thenable.status) {
|
|
717
|
+
case "fulfilled":
|
|
718
|
+
return thenable.value;
|
|
719
|
+
case "rejected":
|
|
720
|
+
throw thenable.reason;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
throw thenable;
|
|
724
|
+
}
|
|
725
|
+
function mapIntoArray(children, array2, escapedPrefix, nameSoFar, callback) {
|
|
726
|
+
var type = typeof children;
|
|
727
|
+
if ("undefined" === type || "boolean" === type) children = null;
|
|
728
|
+
var invokeCallback = false;
|
|
729
|
+
if (null === children) invokeCallback = true;
|
|
730
|
+
else
|
|
731
|
+
switch (type) {
|
|
732
|
+
case "bigint":
|
|
733
|
+
case "string":
|
|
734
|
+
case "number":
|
|
735
|
+
invokeCallback = true;
|
|
736
|
+
break;
|
|
737
|
+
case "object":
|
|
738
|
+
switch (children.$$typeof) {
|
|
739
|
+
case REACT_ELEMENT_TYPE:
|
|
740
|
+
case REACT_PORTAL_TYPE:
|
|
741
|
+
invokeCallback = true;
|
|
742
|
+
break;
|
|
743
|
+
case REACT_LAZY_TYPE:
|
|
744
|
+
return invokeCallback = children._init, mapIntoArray(
|
|
745
|
+
invokeCallback(children._payload),
|
|
746
|
+
array2,
|
|
747
|
+
escapedPrefix,
|
|
748
|
+
nameSoFar,
|
|
749
|
+
callback
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (invokeCallback) {
|
|
754
|
+
invokeCallback = children;
|
|
755
|
+
callback = callback(invokeCallback);
|
|
756
|
+
var childKey = "" === nameSoFar ? "." + getElementKey(invokeCallback, 0) : nameSoFar;
|
|
757
|
+
isArrayImpl(callback) ? (escapedPrefix = "", null != childKey && (escapedPrefix = childKey.replace(userProvidedKeyEscapeRegex, "$&/") + "/"), mapIntoArray(callback, array2, escapedPrefix, "", function(c) {
|
|
758
|
+
return c;
|
|
759
|
+
})) : null != callback && (isValidElement(callback) && (null != callback.key && (invokeCallback && invokeCallback.key === callback.key || checkKeyStringCoercion(callback.key)), escapedPrefix = cloneAndReplaceKey(
|
|
760
|
+
callback,
|
|
761
|
+
escapedPrefix + (null == callback.key || invokeCallback && invokeCallback.key === callback.key ? "" : ("" + callback.key).replace(
|
|
762
|
+
userProvidedKeyEscapeRegex,
|
|
763
|
+
"$&/"
|
|
764
|
+
) + "/") + childKey
|
|
765
|
+
), "" !== nameSoFar && null != invokeCallback && isValidElement(invokeCallback) && null == invokeCallback.key && invokeCallback._store && !invokeCallback._store.validated && (escapedPrefix._store.validated = 2), callback = escapedPrefix), array2.push(callback));
|
|
766
|
+
return 1;
|
|
767
|
+
}
|
|
768
|
+
invokeCallback = 0;
|
|
769
|
+
childKey = "" === nameSoFar ? "." : nameSoFar + ":";
|
|
770
|
+
if (isArrayImpl(children))
|
|
771
|
+
for (var i = 0; i < children.length; i++)
|
|
772
|
+
nameSoFar = children[i], type = childKey + getElementKey(nameSoFar, i), invokeCallback += mapIntoArray(
|
|
773
|
+
nameSoFar,
|
|
774
|
+
array2,
|
|
775
|
+
escapedPrefix,
|
|
776
|
+
type,
|
|
777
|
+
callback
|
|
778
|
+
);
|
|
779
|
+
else if (i = getIteratorFn(children), "function" === typeof i)
|
|
780
|
+
for (i === children.entries && (didWarnAboutMaps || console.warn(
|
|
781
|
+
"Using Maps as children is not supported. Use an array of keyed ReactElements instead."
|
|
782
|
+
), didWarnAboutMaps = true), children = i.call(children), i = 0; !(nameSoFar = children.next()).done; )
|
|
783
|
+
nameSoFar = nameSoFar.value, type = childKey + getElementKey(nameSoFar, i++), invokeCallback += mapIntoArray(
|
|
784
|
+
nameSoFar,
|
|
785
|
+
array2,
|
|
786
|
+
escapedPrefix,
|
|
787
|
+
type,
|
|
788
|
+
callback
|
|
789
|
+
);
|
|
790
|
+
else if ("object" === type) {
|
|
791
|
+
if ("function" === typeof children.then)
|
|
792
|
+
return mapIntoArray(
|
|
793
|
+
resolveThenable(children),
|
|
794
|
+
array2,
|
|
795
|
+
escapedPrefix,
|
|
796
|
+
nameSoFar,
|
|
797
|
+
callback
|
|
798
|
+
);
|
|
799
|
+
array2 = String(children);
|
|
800
|
+
throw Error(
|
|
801
|
+
"Objects are not valid as a React child (found: " + ("[object Object]" === array2 ? "object with keys {" + Object.keys(children).join(", ") + "}" : array2) + "). If you meant to render a collection of children, use an array instead."
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
return invokeCallback;
|
|
805
|
+
}
|
|
806
|
+
function mapChildren(children, func, context) {
|
|
807
|
+
if (null == children) return children;
|
|
808
|
+
var result = [], count = 0;
|
|
809
|
+
mapIntoArray(children, result, "", "", function(child) {
|
|
810
|
+
return func.call(context, child, count++);
|
|
811
|
+
});
|
|
812
|
+
return result;
|
|
813
|
+
}
|
|
814
|
+
function lazyInitializer(payload) {
|
|
815
|
+
if (-1 === payload._status) {
|
|
816
|
+
var ioInfo = payload._ioInfo;
|
|
817
|
+
null != ioInfo && (ioInfo.start = ioInfo.end = performance.now());
|
|
818
|
+
ioInfo = payload._result;
|
|
819
|
+
var thenable = ioInfo();
|
|
820
|
+
thenable.then(
|
|
821
|
+
function(moduleObject) {
|
|
822
|
+
if (0 === payload._status || -1 === payload._status) {
|
|
823
|
+
payload._status = 1;
|
|
824
|
+
payload._result = moduleObject;
|
|
825
|
+
var _ioInfo = payload._ioInfo;
|
|
826
|
+
null != _ioInfo && (_ioInfo.end = performance.now());
|
|
827
|
+
void 0 === thenable.status && (thenable.status = "fulfilled", thenable.value = moduleObject);
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
function(error) {
|
|
831
|
+
if (0 === payload._status || -1 === payload._status) {
|
|
832
|
+
payload._status = 2;
|
|
833
|
+
payload._result = error;
|
|
834
|
+
var _ioInfo2 = payload._ioInfo;
|
|
835
|
+
null != _ioInfo2 && (_ioInfo2.end = performance.now());
|
|
836
|
+
void 0 === thenable.status && (thenable.status = "rejected", thenable.reason = error);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
);
|
|
840
|
+
ioInfo = payload._ioInfo;
|
|
841
|
+
if (null != ioInfo) {
|
|
842
|
+
ioInfo.value = thenable;
|
|
843
|
+
var displayName = thenable.displayName;
|
|
844
|
+
"string" === typeof displayName && (ioInfo.name = displayName);
|
|
845
|
+
}
|
|
846
|
+
-1 === payload._status && (payload._status = 0, payload._result = thenable);
|
|
847
|
+
}
|
|
848
|
+
if (1 === payload._status)
|
|
849
|
+
return ioInfo = payload._result, void 0 === ioInfo && console.error(
|
|
850
|
+
"lazy: Expected the result of a dynamic import() call. Instead received: %s\n\nYour code should look like: \n const MyComponent = lazy(() => import('./MyComponent'))\n\nDid you accidentally put curly braces around the import?",
|
|
851
|
+
ioInfo
|
|
852
|
+
), "default" in ioInfo || console.error(
|
|
853
|
+
"lazy: Expected the result of a dynamic import() call. Instead received: %s\n\nYour code should look like: \n const MyComponent = lazy(() => import('./MyComponent'))",
|
|
854
|
+
ioInfo
|
|
855
|
+
), ioInfo.default;
|
|
856
|
+
throw payload._result;
|
|
857
|
+
}
|
|
858
|
+
function resolveDispatcher() {
|
|
859
|
+
var dispatcher = ReactSharedInternals.H;
|
|
860
|
+
null === dispatcher && console.error(
|
|
861
|
+
"Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem."
|
|
862
|
+
);
|
|
863
|
+
return dispatcher;
|
|
864
|
+
}
|
|
865
|
+
function releaseAsyncTransition() {
|
|
866
|
+
ReactSharedInternals.asyncTransitions--;
|
|
867
|
+
}
|
|
868
|
+
function enqueueTask(task) {
|
|
869
|
+
if (null === enqueueTaskImpl)
|
|
870
|
+
try {
|
|
871
|
+
var requireString = ("require" + Math.random()).slice(0, 7);
|
|
872
|
+
enqueueTaskImpl = (module && module[requireString]).call(
|
|
873
|
+
module,
|
|
874
|
+
"timers"
|
|
875
|
+
).setImmediate;
|
|
876
|
+
} catch (_err) {
|
|
877
|
+
enqueueTaskImpl = function(callback) {
|
|
878
|
+
false === didWarnAboutMessageChannel && (didWarnAboutMessageChannel = true, "undefined" === typeof MessageChannel && console.error(
|
|
879
|
+
"This browser does not have a MessageChannel implementation, so enqueuing tasks via await act(async () => ...) will fail. Please file an issue at https://github.com/facebook/react/issues if you encounter this warning."
|
|
880
|
+
));
|
|
881
|
+
var channel = new MessageChannel();
|
|
882
|
+
channel.port1.onmessage = callback;
|
|
883
|
+
channel.port2.postMessage(void 0);
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
return enqueueTaskImpl(task);
|
|
887
|
+
}
|
|
888
|
+
function aggregateErrors(errors) {
|
|
889
|
+
return 1 < errors.length && "function" === typeof AggregateError ? new AggregateError(errors) : errors[0];
|
|
890
|
+
}
|
|
891
|
+
function popActScope(prevActQueue, prevActScopeDepth) {
|
|
892
|
+
prevActScopeDepth !== actScopeDepth - 1 && console.error(
|
|
893
|
+
"You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. "
|
|
894
|
+
);
|
|
895
|
+
actScopeDepth = prevActScopeDepth;
|
|
896
|
+
}
|
|
897
|
+
function recursivelyFlushAsyncActWork(returnValue, resolve, reject) {
|
|
898
|
+
var queue = ReactSharedInternals.actQueue;
|
|
899
|
+
if (null !== queue)
|
|
900
|
+
if (0 !== queue.length)
|
|
901
|
+
try {
|
|
902
|
+
flushActQueue(queue);
|
|
903
|
+
enqueueTask(function() {
|
|
904
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve, reject);
|
|
905
|
+
});
|
|
906
|
+
return;
|
|
907
|
+
} catch (error) {
|
|
908
|
+
ReactSharedInternals.thrownErrors.push(error);
|
|
909
|
+
}
|
|
910
|
+
else ReactSharedInternals.actQueue = null;
|
|
911
|
+
0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve(returnValue);
|
|
912
|
+
}
|
|
913
|
+
function flushActQueue(queue) {
|
|
914
|
+
if (!isFlushing) {
|
|
915
|
+
isFlushing = true;
|
|
916
|
+
var i = 0;
|
|
917
|
+
try {
|
|
918
|
+
for (; i < queue.length; i++) {
|
|
919
|
+
var callback = queue[i];
|
|
920
|
+
do {
|
|
921
|
+
ReactSharedInternals.didUsePromise = false;
|
|
922
|
+
var continuation = callback(false);
|
|
923
|
+
if (null !== continuation) {
|
|
924
|
+
if (ReactSharedInternals.didUsePromise) {
|
|
925
|
+
queue[i] = callback;
|
|
926
|
+
queue.splice(0, i);
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
callback = continuation;
|
|
930
|
+
} else break;
|
|
931
|
+
} while (1);
|
|
932
|
+
}
|
|
933
|
+
queue.length = 0;
|
|
934
|
+
} catch (error) {
|
|
935
|
+
queue.splice(0, i + 1), ReactSharedInternals.thrownErrors.push(error);
|
|
936
|
+
} finally {
|
|
937
|
+
isFlushing = false;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
|
|
942
|
+
var REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = /* @__PURE__ */ Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = /* @__PURE__ */ Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = /* @__PURE__ */ Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = /* @__PURE__ */ Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = /* @__PURE__ */ Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = /* @__PURE__ */ Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo"), REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy"), REACT_ACTIVITY_TYPE = /* @__PURE__ */ Symbol.for("react.activity"), MAYBE_ITERATOR_SYMBOL = Symbol.iterator, didWarnStateUpdateForUnmountedComponent = {}, ReactNoopUpdateQueue = {
|
|
943
|
+
isMounted: function() {
|
|
944
|
+
return false;
|
|
945
|
+
},
|
|
946
|
+
enqueueForceUpdate: function(publicInstance) {
|
|
947
|
+
warnNoop(publicInstance, "forceUpdate");
|
|
948
|
+
},
|
|
949
|
+
enqueueReplaceState: function(publicInstance) {
|
|
950
|
+
warnNoop(publicInstance, "replaceState");
|
|
951
|
+
},
|
|
952
|
+
enqueueSetState: function(publicInstance) {
|
|
953
|
+
warnNoop(publicInstance, "setState");
|
|
954
|
+
}
|
|
955
|
+
}, assign = Object.assign, emptyObject = {};
|
|
956
|
+
Object.freeze(emptyObject);
|
|
957
|
+
Component.prototype.isReactComponent = {};
|
|
958
|
+
Component.prototype.setState = function(partialState, callback) {
|
|
959
|
+
if ("object" !== typeof partialState && "function" !== typeof partialState && null != partialState)
|
|
960
|
+
throw Error(
|
|
961
|
+
"takes an object of state variables to update or a function which returns an object of state variables."
|
|
962
|
+
);
|
|
963
|
+
this.updater.enqueueSetState(this, partialState, callback, "setState");
|
|
964
|
+
};
|
|
965
|
+
Component.prototype.forceUpdate = function(callback) {
|
|
966
|
+
this.updater.enqueueForceUpdate(this, callback, "forceUpdate");
|
|
967
|
+
};
|
|
968
|
+
var deprecatedAPIs = {
|
|
969
|
+
isMounted: [
|
|
970
|
+
"isMounted",
|
|
971
|
+
"Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."
|
|
972
|
+
],
|
|
973
|
+
replaceState: [
|
|
974
|
+
"replaceState",
|
|
975
|
+
"Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."
|
|
976
|
+
]
|
|
977
|
+
};
|
|
978
|
+
for (fnName in deprecatedAPIs)
|
|
979
|
+
deprecatedAPIs.hasOwnProperty(fnName) && defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
|
|
980
|
+
ComponentDummy.prototype = Component.prototype;
|
|
981
|
+
deprecatedAPIs = PureComponent.prototype = new ComponentDummy();
|
|
982
|
+
deprecatedAPIs.constructor = PureComponent;
|
|
983
|
+
assign(deprecatedAPIs, Component.prototype);
|
|
984
|
+
deprecatedAPIs.isPureReactComponent = true;
|
|
985
|
+
var isArrayImpl = Array.isArray, REACT_CLIENT_REFERENCE = /* @__PURE__ */ Symbol.for("react.client.reference"), ReactSharedInternals = {
|
|
986
|
+
H: null,
|
|
987
|
+
A: null,
|
|
988
|
+
T: null,
|
|
989
|
+
S: null,
|
|
990
|
+
actQueue: null,
|
|
991
|
+
asyncTransitions: 0,
|
|
992
|
+
isBatchingLegacy: false,
|
|
993
|
+
didScheduleLegacyUpdate: false,
|
|
994
|
+
didUsePromise: false,
|
|
995
|
+
thrownErrors: [],
|
|
996
|
+
getCurrentStack: null,
|
|
997
|
+
recentlyCreatedOwnerStacks: 0
|
|
998
|
+
}, hasOwnProperty = Object.prototype.hasOwnProperty, createTask = console.createTask ? console.createTask : function() {
|
|
999
|
+
return null;
|
|
1000
|
+
};
|
|
1001
|
+
deprecatedAPIs = {
|
|
1002
|
+
react_stack_bottom_frame: function(callStackForError) {
|
|
1003
|
+
return callStackForError();
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1006
|
+
var specialPropKeyWarningShown, didWarnAboutOldJSXRuntime;
|
|
1007
|
+
var didWarnAboutElementRef = {};
|
|
1008
|
+
var unknownOwnerDebugStack = deprecatedAPIs.react_stack_bottom_frame.bind(
|
|
1009
|
+
deprecatedAPIs,
|
|
1010
|
+
UnknownOwner
|
|
1011
|
+
)();
|
|
1012
|
+
var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
|
|
1013
|
+
var didWarnAboutMaps = false, userProvidedKeyEscapeRegex = /\/+/g, reportGlobalError = "function" === typeof reportError ? reportError : function(error) {
|
|
1014
|
+
if ("object" === typeof window && "function" === typeof window.ErrorEvent) {
|
|
1015
|
+
var event = new window.ErrorEvent("error", {
|
|
1016
|
+
bubbles: true,
|
|
1017
|
+
cancelable: true,
|
|
1018
|
+
message: "object" === typeof error && null !== error && "string" === typeof error.message ? String(error.message) : String(error),
|
|
1019
|
+
error
|
|
1020
|
+
});
|
|
1021
|
+
if (!window.dispatchEvent(event)) return;
|
|
1022
|
+
} else if ("object" === typeof process && "function" === typeof process.emit) {
|
|
1023
|
+
process.emit("uncaughtException", error);
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
console.error(error);
|
|
1027
|
+
}, didWarnAboutMessageChannel = false, enqueueTaskImpl = null, actScopeDepth = 0, didWarnNoAwaitAct = false, isFlushing = false, queueSeveralMicrotasks = "function" === typeof queueMicrotask ? function(callback) {
|
|
1028
|
+
queueMicrotask(function() {
|
|
1029
|
+
return queueMicrotask(callback);
|
|
1030
|
+
});
|
|
1031
|
+
} : enqueueTask;
|
|
1032
|
+
deprecatedAPIs = Object.freeze({
|
|
1033
|
+
__proto__: null,
|
|
1034
|
+
c: function(size) {
|
|
1035
|
+
return resolveDispatcher().useMemoCache(size);
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
var fnName = {
|
|
1039
|
+
map: mapChildren,
|
|
1040
|
+
forEach: function(children, forEachFunc, forEachContext) {
|
|
1041
|
+
mapChildren(
|
|
1042
|
+
children,
|
|
1043
|
+
function() {
|
|
1044
|
+
forEachFunc.apply(this, arguments);
|
|
1045
|
+
},
|
|
1046
|
+
forEachContext
|
|
1047
|
+
);
|
|
1048
|
+
},
|
|
1049
|
+
count: function(children) {
|
|
1050
|
+
var n = 0;
|
|
1051
|
+
mapChildren(children, function() {
|
|
1052
|
+
n++;
|
|
1053
|
+
});
|
|
1054
|
+
return n;
|
|
1055
|
+
},
|
|
1056
|
+
toArray: function(children) {
|
|
1057
|
+
return mapChildren(children, function(child) {
|
|
1058
|
+
return child;
|
|
1059
|
+
}) || [];
|
|
1060
|
+
},
|
|
1061
|
+
only: function(children) {
|
|
1062
|
+
if (!isValidElement(children))
|
|
1063
|
+
throw Error(
|
|
1064
|
+
"React.Children.only expected to receive a single React element child."
|
|
1065
|
+
);
|
|
1066
|
+
return children;
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
exports.Activity = REACT_ACTIVITY_TYPE;
|
|
1070
|
+
exports.Children = fnName;
|
|
1071
|
+
exports.Component = Component;
|
|
1072
|
+
exports.Fragment = REACT_FRAGMENT_TYPE;
|
|
1073
|
+
exports.Profiler = REACT_PROFILER_TYPE;
|
|
1074
|
+
exports.PureComponent = PureComponent;
|
|
1075
|
+
exports.StrictMode = REACT_STRICT_MODE_TYPE;
|
|
1076
|
+
exports.Suspense = REACT_SUSPENSE_TYPE;
|
|
1077
|
+
exports.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = ReactSharedInternals;
|
|
1078
|
+
exports.__COMPILER_RUNTIME = deprecatedAPIs;
|
|
1079
|
+
exports.act = function(callback) {
|
|
1080
|
+
var prevActQueue = ReactSharedInternals.actQueue, prevActScopeDepth = actScopeDepth;
|
|
1081
|
+
actScopeDepth++;
|
|
1082
|
+
var queue = ReactSharedInternals.actQueue = null !== prevActQueue ? prevActQueue : [], didAwaitActCall = false;
|
|
1083
|
+
try {
|
|
1084
|
+
var result = callback();
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
ReactSharedInternals.thrownErrors.push(error);
|
|
1087
|
+
}
|
|
1088
|
+
if (0 < ReactSharedInternals.thrownErrors.length)
|
|
1089
|
+
throw popActScope(prevActQueue, prevActScopeDepth), callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
|
|
1090
|
+
if (null !== result && "object" === typeof result && "function" === typeof result.then) {
|
|
1091
|
+
var thenable = result;
|
|
1092
|
+
queueSeveralMicrotasks(function() {
|
|
1093
|
+
didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = true, console.error(
|
|
1094
|
+
"You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"
|
|
1095
|
+
));
|
|
1096
|
+
});
|
|
1097
|
+
return {
|
|
1098
|
+
then: function(resolve, reject) {
|
|
1099
|
+
didAwaitActCall = true;
|
|
1100
|
+
thenable.then(
|
|
1101
|
+
function(returnValue) {
|
|
1102
|
+
popActScope(prevActQueue, prevActScopeDepth);
|
|
1103
|
+
if (0 === prevActScopeDepth) {
|
|
1104
|
+
try {
|
|
1105
|
+
flushActQueue(queue), enqueueTask(function() {
|
|
1106
|
+
return recursivelyFlushAsyncActWork(
|
|
1107
|
+
returnValue,
|
|
1108
|
+
resolve,
|
|
1109
|
+
reject
|
|
1110
|
+
);
|
|
1111
|
+
});
|
|
1112
|
+
} catch (error$0) {
|
|
1113
|
+
ReactSharedInternals.thrownErrors.push(error$0);
|
|
1114
|
+
}
|
|
1115
|
+
if (0 < ReactSharedInternals.thrownErrors.length) {
|
|
1116
|
+
var _thrownError = aggregateErrors(
|
|
1117
|
+
ReactSharedInternals.thrownErrors
|
|
1118
|
+
);
|
|
1119
|
+
ReactSharedInternals.thrownErrors.length = 0;
|
|
1120
|
+
reject(_thrownError);
|
|
1121
|
+
}
|
|
1122
|
+
} else resolve(returnValue);
|
|
1123
|
+
},
|
|
1124
|
+
function(error) {
|
|
1125
|
+
popActScope(prevActQueue, prevActScopeDepth);
|
|
1126
|
+
0 < ReactSharedInternals.thrownErrors.length ? (error = aggregateErrors(
|
|
1127
|
+
ReactSharedInternals.thrownErrors
|
|
1128
|
+
), ReactSharedInternals.thrownErrors.length = 0, reject(error)) : reject(error);
|
|
1129
|
+
}
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
var returnValue$jscomp$0 = result;
|
|
1135
|
+
popActScope(prevActQueue, prevActScopeDepth);
|
|
1136
|
+
0 === prevActScopeDepth && (flushActQueue(queue), 0 !== queue.length && queueSeveralMicrotasks(function() {
|
|
1137
|
+
didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = true, console.error(
|
|
1138
|
+
"A component suspended inside an `act` scope, but the `act` call was not awaited. When testing React components that depend on asynchronous data, you must await the result:\n\nawait act(() => ...)"
|
|
1139
|
+
));
|
|
1140
|
+
}), ReactSharedInternals.actQueue = null);
|
|
1141
|
+
if (0 < ReactSharedInternals.thrownErrors.length)
|
|
1142
|
+
throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
|
|
1143
|
+
return {
|
|
1144
|
+
then: function(resolve, reject) {
|
|
1145
|
+
didAwaitActCall = true;
|
|
1146
|
+
0 === prevActScopeDepth ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
|
|
1147
|
+
return recursivelyFlushAsyncActWork(
|
|
1148
|
+
returnValue$jscomp$0,
|
|
1149
|
+
resolve,
|
|
1150
|
+
reject
|
|
1151
|
+
);
|
|
1152
|
+
})) : resolve(returnValue$jscomp$0);
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
};
|
|
1156
|
+
exports.cache = function(fn) {
|
|
1157
|
+
return function() {
|
|
1158
|
+
return fn.apply(null, arguments);
|
|
1159
|
+
};
|
|
1160
|
+
};
|
|
1161
|
+
exports.cacheSignal = function() {
|
|
1162
|
+
return null;
|
|
1163
|
+
};
|
|
1164
|
+
exports.captureOwnerStack = function() {
|
|
1165
|
+
var getCurrentStack = ReactSharedInternals.getCurrentStack;
|
|
1166
|
+
return null === getCurrentStack ? null : getCurrentStack();
|
|
1167
|
+
};
|
|
1168
|
+
exports.cloneElement = function(element, config, children) {
|
|
1169
|
+
if (null === element || void 0 === element)
|
|
1170
|
+
throw Error(
|
|
1171
|
+
"The argument must be a React element, but you passed " + element + "."
|
|
1172
|
+
);
|
|
1173
|
+
var props = assign({}, element.props), key = element.key, owner = element._owner;
|
|
1174
|
+
if (null != config) {
|
|
1175
|
+
var JSCompiler_inline_result;
|
|
1176
|
+
a: {
|
|
1177
|
+
if (hasOwnProperty.call(config, "ref") && (JSCompiler_inline_result = Object.getOwnPropertyDescriptor(
|
|
1178
|
+
config,
|
|
1179
|
+
"ref"
|
|
1180
|
+
).get) && JSCompiler_inline_result.isReactWarning) {
|
|
1181
|
+
JSCompiler_inline_result = false;
|
|
1182
|
+
break a;
|
|
1183
|
+
}
|
|
1184
|
+
JSCompiler_inline_result = void 0 !== config.ref;
|
|
1185
|
+
}
|
|
1186
|
+
JSCompiler_inline_result && (owner = getOwner());
|
|
1187
|
+
hasValidKey(config) && (checkKeyStringCoercion(config.key), key = "" + config.key);
|
|
1188
|
+
for (propName in config)
|
|
1189
|
+
!hasOwnProperty.call(config, propName) || "key" === propName || "__self" === propName || "__source" === propName || "ref" === propName && void 0 === config.ref || (props[propName] = config[propName]);
|
|
1190
|
+
}
|
|
1191
|
+
var propName = arguments.length - 2;
|
|
1192
|
+
if (1 === propName) props.children = children;
|
|
1193
|
+
else if (1 < propName) {
|
|
1194
|
+
JSCompiler_inline_result = Array(propName);
|
|
1195
|
+
for (var i = 0; i < propName; i++)
|
|
1196
|
+
JSCompiler_inline_result[i] = arguments[i + 2];
|
|
1197
|
+
props.children = JSCompiler_inline_result;
|
|
1198
|
+
}
|
|
1199
|
+
props = ReactElement(
|
|
1200
|
+
element.type,
|
|
1201
|
+
key,
|
|
1202
|
+
props,
|
|
1203
|
+
owner,
|
|
1204
|
+
element._debugStack,
|
|
1205
|
+
element._debugTask
|
|
1206
|
+
);
|
|
1207
|
+
for (key = 2; key < arguments.length; key++)
|
|
1208
|
+
validateChildKeys(arguments[key]);
|
|
1209
|
+
return props;
|
|
1210
|
+
};
|
|
1211
|
+
exports.createContext = function(defaultValue) {
|
|
1212
|
+
defaultValue = {
|
|
1213
|
+
$$typeof: REACT_CONTEXT_TYPE,
|
|
1214
|
+
_currentValue: defaultValue,
|
|
1215
|
+
_currentValue2: defaultValue,
|
|
1216
|
+
_threadCount: 0,
|
|
1217
|
+
Provider: null,
|
|
1218
|
+
Consumer: null
|
|
1219
|
+
};
|
|
1220
|
+
defaultValue.Provider = defaultValue;
|
|
1221
|
+
defaultValue.Consumer = {
|
|
1222
|
+
$$typeof: REACT_CONSUMER_TYPE,
|
|
1223
|
+
_context: defaultValue
|
|
1224
|
+
};
|
|
1225
|
+
defaultValue._currentRenderer = null;
|
|
1226
|
+
defaultValue._currentRenderer2 = null;
|
|
1227
|
+
return defaultValue;
|
|
1228
|
+
};
|
|
1229
|
+
exports.createElement = function(type, config, children) {
|
|
1230
|
+
for (var i = 2; i < arguments.length; i++)
|
|
1231
|
+
validateChildKeys(arguments[i]);
|
|
1232
|
+
i = {};
|
|
1233
|
+
var key = null;
|
|
1234
|
+
if (null != config)
|
|
1235
|
+
for (propName in didWarnAboutOldJSXRuntime || !("__self" in config) || "key" in config || (didWarnAboutOldJSXRuntime = true, console.warn(
|
|
1236
|
+
"Your app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance: https://react.dev/link/new-jsx-transform"
|
|
1237
|
+
)), hasValidKey(config) && (checkKeyStringCoercion(config.key), key = "" + config.key), config)
|
|
1238
|
+
hasOwnProperty.call(config, propName) && "key" !== propName && "__self" !== propName && "__source" !== propName && (i[propName] = config[propName]);
|
|
1239
|
+
var childrenLength = arguments.length - 2;
|
|
1240
|
+
if (1 === childrenLength) i.children = children;
|
|
1241
|
+
else if (1 < childrenLength) {
|
|
1242
|
+
for (var childArray = Array(childrenLength), _i = 0; _i < childrenLength; _i++)
|
|
1243
|
+
childArray[_i] = arguments[_i + 2];
|
|
1244
|
+
Object.freeze && Object.freeze(childArray);
|
|
1245
|
+
i.children = childArray;
|
|
1246
|
+
}
|
|
1247
|
+
if (type && type.defaultProps)
|
|
1248
|
+
for (propName in childrenLength = type.defaultProps, childrenLength)
|
|
1249
|
+
void 0 === i[propName] && (i[propName] = childrenLength[propName]);
|
|
1250
|
+
key && defineKeyPropWarningGetter(
|
|
1251
|
+
i,
|
|
1252
|
+
"function" === typeof type ? type.displayName || type.name || "Unknown" : type
|
|
1253
|
+
);
|
|
1254
|
+
var propName = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
|
|
1255
|
+
return ReactElement(
|
|
1256
|
+
type,
|
|
1257
|
+
key,
|
|
1258
|
+
i,
|
|
1259
|
+
getOwner(),
|
|
1260
|
+
propName ? Error("react-stack-top-frame") : unknownOwnerDebugStack,
|
|
1261
|
+
propName ? createTask(getTaskName(type)) : unknownOwnerDebugTask
|
|
1262
|
+
);
|
|
1263
|
+
};
|
|
1264
|
+
exports.createRef = function() {
|
|
1265
|
+
var refObject = { current: null };
|
|
1266
|
+
Object.seal(refObject);
|
|
1267
|
+
return refObject;
|
|
1268
|
+
};
|
|
1269
|
+
exports.forwardRef = function(render) {
|
|
1270
|
+
null != render && render.$$typeof === REACT_MEMO_TYPE ? console.error(
|
|
1271
|
+
"forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."
|
|
1272
|
+
) : "function" !== typeof render ? console.error(
|
|
1273
|
+
"forwardRef requires a render function but was given %s.",
|
|
1274
|
+
null === render ? "null" : typeof render
|
|
1275
|
+
) : 0 !== render.length && 2 !== render.length && console.error(
|
|
1276
|
+
"forwardRef render functions accept exactly two parameters: props and ref. %s",
|
|
1277
|
+
1 === render.length ? "Did you forget to use the ref parameter?" : "Any additional parameter will be undefined."
|
|
1278
|
+
);
|
|
1279
|
+
null != render && null != render.defaultProps && console.error(
|
|
1280
|
+
"forwardRef render functions do not support defaultProps. Did you accidentally pass a React component?"
|
|
1281
|
+
);
|
|
1282
|
+
var elementType = { $$typeof: REACT_FORWARD_REF_TYPE, render }, ownName;
|
|
1283
|
+
Object.defineProperty(elementType, "displayName", {
|
|
1284
|
+
enumerable: false,
|
|
1285
|
+
configurable: true,
|
|
1286
|
+
get: function() {
|
|
1287
|
+
return ownName;
|
|
1288
|
+
},
|
|
1289
|
+
set: function(name) {
|
|
1290
|
+
ownName = name;
|
|
1291
|
+
render.name || render.displayName || (Object.defineProperty(render, "name", { value: name }), render.displayName = name);
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
return elementType;
|
|
1295
|
+
};
|
|
1296
|
+
exports.isValidElement = isValidElement;
|
|
1297
|
+
exports.lazy = function(ctor) {
|
|
1298
|
+
ctor = { _status: -1, _result: ctor };
|
|
1299
|
+
var lazyType = {
|
|
1300
|
+
$$typeof: REACT_LAZY_TYPE,
|
|
1301
|
+
_payload: ctor,
|
|
1302
|
+
_init: lazyInitializer
|
|
1303
|
+
}, ioInfo = {
|
|
1304
|
+
name: "lazy",
|
|
1305
|
+
start: -1,
|
|
1306
|
+
end: -1,
|
|
1307
|
+
value: null,
|
|
1308
|
+
owner: null,
|
|
1309
|
+
debugStack: Error("react-stack-top-frame"),
|
|
1310
|
+
debugTask: console.createTask ? console.createTask("lazy()") : null
|
|
1311
|
+
};
|
|
1312
|
+
ctor._ioInfo = ioInfo;
|
|
1313
|
+
lazyType._debugInfo = [{ awaited: ioInfo }];
|
|
1314
|
+
return lazyType;
|
|
1315
|
+
};
|
|
1316
|
+
exports.memo = function(type, compare) {
|
|
1317
|
+
null == type && console.error(
|
|
1318
|
+
"memo: The first argument must be a component. Instead received: %s",
|
|
1319
|
+
null === type ? "null" : typeof type
|
|
1320
|
+
);
|
|
1321
|
+
compare = {
|
|
1322
|
+
$$typeof: REACT_MEMO_TYPE,
|
|
1323
|
+
type,
|
|
1324
|
+
compare: void 0 === compare ? null : compare
|
|
1325
|
+
};
|
|
1326
|
+
var ownName;
|
|
1327
|
+
Object.defineProperty(compare, "displayName", {
|
|
1328
|
+
enumerable: false,
|
|
1329
|
+
configurable: true,
|
|
1330
|
+
get: function() {
|
|
1331
|
+
return ownName;
|
|
1332
|
+
},
|
|
1333
|
+
set: function(name) {
|
|
1334
|
+
ownName = name;
|
|
1335
|
+
type.name || type.displayName || (Object.defineProperty(type, "name", { value: name }), type.displayName = name);
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
return compare;
|
|
1339
|
+
};
|
|
1340
|
+
exports.startTransition = function(scope) {
|
|
1341
|
+
var prevTransition = ReactSharedInternals.T, currentTransition = {};
|
|
1342
|
+
currentTransition._updatedFibers = /* @__PURE__ */ new Set();
|
|
1343
|
+
ReactSharedInternals.T = currentTransition;
|
|
1344
|
+
try {
|
|
1345
|
+
var returnValue = scope(), onStartTransitionFinish = ReactSharedInternals.S;
|
|
1346
|
+
null !== onStartTransitionFinish && onStartTransitionFinish(currentTransition, returnValue);
|
|
1347
|
+
"object" === typeof returnValue && null !== returnValue && "function" === typeof returnValue.then && (ReactSharedInternals.asyncTransitions++, returnValue.then(releaseAsyncTransition, releaseAsyncTransition), returnValue.then(noop, reportGlobalError));
|
|
1348
|
+
} catch (error) {
|
|
1349
|
+
reportGlobalError(error);
|
|
1350
|
+
} finally {
|
|
1351
|
+
null === prevTransition && currentTransition._updatedFibers && (scope = currentTransition._updatedFibers.size, currentTransition._updatedFibers.clear(), 10 < scope && console.warn(
|
|
1352
|
+
"Detected a large number of updates inside startTransition. If this is due to a subscription please re-write it to use React provided hooks. Otherwise concurrent mode guarantees are off the table."
|
|
1353
|
+
)), null !== prevTransition && null !== currentTransition.types && (null !== prevTransition.types && prevTransition.types !== currentTransition.types && console.error(
|
|
1354
|
+
"We expected inner Transitions to have transferred the outer types set and that you cannot add to the outer Transition while inside the inner.This is a bug in React."
|
|
1355
|
+
), prevTransition.types = currentTransition.types), ReactSharedInternals.T = prevTransition;
|
|
1356
|
+
}
|
|
1357
|
+
};
|
|
1358
|
+
exports.unstable_useCacheRefresh = function() {
|
|
1359
|
+
return resolveDispatcher().useCacheRefresh();
|
|
1360
|
+
};
|
|
1361
|
+
exports.use = function(usable) {
|
|
1362
|
+
return resolveDispatcher().use(usable);
|
|
1363
|
+
};
|
|
1364
|
+
exports.useActionState = function(action, initialState, permalink) {
|
|
1365
|
+
return resolveDispatcher().useActionState(
|
|
1366
|
+
action,
|
|
1367
|
+
initialState,
|
|
1368
|
+
permalink
|
|
1369
|
+
);
|
|
1370
|
+
};
|
|
1371
|
+
exports.useCallback = function(callback, deps) {
|
|
1372
|
+
return resolveDispatcher().useCallback(callback, deps);
|
|
1373
|
+
};
|
|
1374
|
+
exports.useContext = function(Context) {
|
|
1375
|
+
var dispatcher = resolveDispatcher();
|
|
1376
|
+
Context.$$typeof === REACT_CONSUMER_TYPE && console.error(
|
|
1377
|
+
"Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you mean to call useContext(Context) instead?"
|
|
1378
|
+
);
|
|
1379
|
+
return dispatcher.useContext(Context);
|
|
1380
|
+
};
|
|
1381
|
+
exports.useDebugValue = function(value, formatterFn) {
|
|
1382
|
+
return resolveDispatcher().useDebugValue(value, formatterFn);
|
|
1383
|
+
};
|
|
1384
|
+
exports.useDeferredValue = function(value, initialValue) {
|
|
1385
|
+
return resolveDispatcher().useDeferredValue(value, initialValue);
|
|
1386
|
+
};
|
|
1387
|
+
exports.useEffect = function(create, deps) {
|
|
1388
|
+
null == create && console.warn(
|
|
1389
|
+
"React Hook useEffect requires an effect callback. Did you forget to pass a callback to the hook?"
|
|
1390
|
+
);
|
|
1391
|
+
return resolveDispatcher().useEffect(create, deps);
|
|
1392
|
+
};
|
|
1393
|
+
exports.useEffectEvent = function(callback) {
|
|
1394
|
+
return resolveDispatcher().useEffectEvent(callback);
|
|
1395
|
+
};
|
|
1396
|
+
exports.useId = function() {
|
|
1397
|
+
return resolveDispatcher().useId();
|
|
1398
|
+
};
|
|
1399
|
+
exports.useImperativeHandle = function(ref, create, deps) {
|
|
1400
|
+
return resolveDispatcher().useImperativeHandle(ref, create, deps);
|
|
1401
|
+
};
|
|
1402
|
+
exports.useInsertionEffect = function(create, deps) {
|
|
1403
|
+
null == create && console.warn(
|
|
1404
|
+
"React Hook useInsertionEffect requires an effect callback. Did you forget to pass a callback to the hook?"
|
|
1405
|
+
);
|
|
1406
|
+
return resolveDispatcher().useInsertionEffect(create, deps);
|
|
1407
|
+
};
|
|
1408
|
+
exports.useLayoutEffect = function(create, deps) {
|
|
1409
|
+
null == create && console.warn(
|
|
1410
|
+
"React Hook useLayoutEffect requires an effect callback. Did you forget to pass a callback to the hook?"
|
|
1411
|
+
);
|
|
1412
|
+
return resolveDispatcher().useLayoutEffect(create, deps);
|
|
1413
|
+
};
|
|
1414
|
+
exports.useMemo = function(create, deps) {
|
|
1415
|
+
return resolveDispatcher().useMemo(create, deps);
|
|
1416
|
+
};
|
|
1417
|
+
exports.useOptimistic = function(passthrough, reducer) {
|
|
1418
|
+
return resolveDispatcher().useOptimistic(passthrough, reducer);
|
|
1419
|
+
};
|
|
1420
|
+
exports.useReducer = function(reducer, initialArg, init) {
|
|
1421
|
+
return resolveDispatcher().useReducer(reducer, initialArg, init);
|
|
1422
|
+
};
|
|
1423
|
+
exports.useRef = function(initialValue) {
|
|
1424
|
+
return resolveDispatcher().useRef(initialValue);
|
|
1425
|
+
};
|
|
1426
|
+
exports.useState = function(initialState) {
|
|
1427
|
+
return resolveDispatcher().useState(initialState);
|
|
1428
|
+
};
|
|
1429
|
+
exports.useSyncExternalStore = function(subscribe, getSnapshot, getServerSnapshot) {
|
|
1430
|
+
return resolveDispatcher().useSyncExternalStore(
|
|
1431
|
+
subscribe,
|
|
1432
|
+
getSnapshot,
|
|
1433
|
+
getServerSnapshot
|
|
1434
|
+
);
|
|
1435
|
+
};
|
|
1436
|
+
exports.useTransition = function() {
|
|
1437
|
+
return resolveDispatcher().useTransition();
|
|
1438
|
+
};
|
|
1439
|
+
exports.version = "19.2.8";
|
|
1440
|
+
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
|
|
1441
|
+
})();
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
|
|
1445
|
+
// node_modules/.pnpm/react@19.2.8/node_modules/react/index.js
|
|
1446
|
+
var require_react = __commonJS({
|
|
1447
|
+
"node_modules/.pnpm/react@19.2.8/node_modules/react/index.js"(exports, module) {
|
|
1448
|
+
"use strict";
|
|
1449
|
+
if (process.env.NODE_ENV === "production") {
|
|
1450
|
+
module.exports = require_react_production();
|
|
1451
|
+
} else {
|
|
1452
|
+
module.exports = require_react_development();
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
// mcp/index.ts
|
|
1458
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
1459
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
1460
|
+
|
|
1461
|
+
// mcp/file.ts
|
|
1462
|
+
import { randomBytes } from "node:crypto";
|
|
1463
|
+
import { readFile, rename, writeFile } from "node:fs/promises";
|
|
1464
|
+
import { dirname, join } from "node:path";
|
|
1465
|
+
|
|
1466
|
+
// src/lib/time.ts
|
|
1467
|
+
var MINUTES_PER_DAY = 1440;
|
|
1468
|
+
var DAY_MS = 864e5;
|
|
1469
|
+
var WEEKDAYS = [
|
|
1470
|
+
"Sunday",
|
|
1471
|
+
"Monday",
|
|
1472
|
+
"Tuesday",
|
|
1473
|
+
"Wednesday",
|
|
1474
|
+
"Thursday",
|
|
1475
|
+
"Friday",
|
|
1476
|
+
"Saturday"
|
|
1477
|
+
];
|
|
1478
|
+
var MONTHS = [
|
|
1479
|
+
"January",
|
|
1480
|
+
"February",
|
|
1481
|
+
"March",
|
|
1482
|
+
"April",
|
|
1483
|
+
"May",
|
|
1484
|
+
"June",
|
|
1485
|
+
"July",
|
|
1486
|
+
"August",
|
|
1487
|
+
"September",
|
|
1488
|
+
"October",
|
|
1489
|
+
"November",
|
|
1490
|
+
"December"
|
|
1491
|
+
];
|
|
1492
|
+
function startOfDay(d) {
|
|
1493
|
+
const out = new Date(d);
|
|
1494
|
+
out.setHours(0, 0, 0, 0);
|
|
1495
|
+
return out;
|
|
1496
|
+
}
|
|
1497
|
+
function addDays(d, n) {
|
|
1498
|
+
const out = new Date(d);
|
|
1499
|
+
out.setDate(out.getDate() + n);
|
|
1500
|
+
return out;
|
|
1501
|
+
}
|
|
1502
|
+
function daysInMonth(year, month) {
|
|
1503
|
+
return new Date(year, month + 1, 0).getDate();
|
|
1504
|
+
}
|
|
1505
|
+
function isSameDay(a, b) {
|
|
1506
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
1507
|
+
}
|
|
1508
|
+
function daysBetween(from, to) {
|
|
1509
|
+
return Math.round((startOfDay(to).getTime() - startOfDay(from).getTime()) / 864e5);
|
|
1510
|
+
}
|
|
1511
|
+
function dayKey(d) {
|
|
1512
|
+
const m = `${d.getMonth() + 1}`.padStart(2, "0");
|
|
1513
|
+
const day = `${d.getDate()}`.padStart(2, "0");
|
|
1514
|
+
return `${d.getFullYear()}-${m}-${day}`;
|
|
1515
|
+
}
|
|
1516
|
+
function fromDayKey(key) {
|
|
1517
|
+
const [y, m, d] = key.split("-").map(Number);
|
|
1518
|
+
return new Date(y, (m ?? 1) - 1, d ?? 1);
|
|
1519
|
+
}
|
|
1520
|
+
function minutesOfDay(d) {
|
|
1521
|
+
return d.getHours() * 60 + d.getMinutes() + d.getSeconds() / 60;
|
|
1522
|
+
}
|
|
1523
|
+
function atMinutes(day, minutes) {
|
|
1524
|
+
const out = startOfDay(day);
|
|
1525
|
+
out.setMinutes(Math.round(minutes));
|
|
1526
|
+
return out;
|
|
1527
|
+
}
|
|
1528
|
+
function isZone(value) {
|
|
1529
|
+
if (!value) return false;
|
|
1530
|
+
try {
|
|
1531
|
+
new Intl.DateTimeFormat("en-US", { timeZone: value });
|
|
1532
|
+
return true;
|
|
1533
|
+
} catch {
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
var ZONE_FORMATTERS = /* @__PURE__ */ new Map();
|
|
1538
|
+
function zoneFormatter(zone) {
|
|
1539
|
+
let fmt = ZONE_FORMATTERS.get(zone);
|
|
1540
|
+
if (!fmt) {
|
|
1541
|
+
fmt = new Intl.DateTimeFormat("en-US", {
|
|
1542
|
+
timeZone: zone,
|
|
1543
|
+
hourCycle: "h23",
|
|
1544
|
+
year: "numeric",
|
|
1545
|
+
month: "2-digit",
|
|
1546
|
+
day: "2-digit",
|
|
1547
|
+
hour: "2-digit",
|
|
1548
|
+
minute: "2-digit",
|
|
1549
|
+
second: "2-digit"
|
|
1550
|
+
});
|
|
1551
|
+
ZONE_FORMATTERS.set(zone, fmt);
|
|
1552
|
+
}
|
|
1553
|
+
return fmt;
|
|
1554
|
+
}
|
|
1555
|
+
function zoneParts(at, zone) {
|
|
1556
|
+
const out = {};
|
|
1557
|
+
for (const part of zoneFormatter(zone).formatToParts(at)) {
|
|
1558
|
+
if (part.type !== "literal") out[part.type] = Number(part.value);
|
|
1559
|
+
}
|
|
1560
|
+
return {
|
|
1561
|
+
year: out.year ?? at.getFullYear(),
|
|
1562
|
+
month: out.month ?? at.getMonth() + 1,
|
|
1563
|
+
day: out.day ?? at.getDate(),
|
|
1564
|
+
// `hourCycle: "h23"` still emits 24 for midnight in some engines.
|
|
1565
|
+
hour: (out.hour ?? at.getHours()) % 24,
|
|
1566
|
+
minute: out.minute ?? at.getMinutes(),
|
|
1567
|
+
second: out.second ?? at.getSeconds()
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
function zoneOffset(at, zone) {
|
|
1571
|
+
const p = zoneParts(at, zone);
|
|
1572
|
+
const asUtc = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
|
|
1573
|
+
return Math.round((asUtc - Math.floor(at.getTime() / 1e3) * 1e3) / 6e4);
|
|
1574
|
+
}
|
|
1575
|
+
function zonedToUtc(year, month, day, hour, minute, zone, second = 0) {
|
|
1576
|
+
const naive = Date.UTC(year, month - 1, day, hour, minute, second);
|
|
1577
|
+
const before = naive - zoneOffset(new Date(naive - DAY_MS), zone) * 6e4;
|
|
1578
|
+
const after = naive - zoneOffset(new Date(naive + DAY_MS), zone) * 6e4;
|
|
1579
|
+
const reads = (at) => {
|
|
1580
|
+
const p = zoneParts(new Date(at), zone);
|
|
1581
|
+
return p.year === year && p.month === month && p.day === day && p.hour === hour && p.minute === minute && p.second === second;
|
|
1582
|
+
};
|
|
1583
|
+
if (reads(before)) return before;
|
|
1584
|
+
if (reads(after)) return after;
|
|
1585
|
+
return before;
|
|
1586
|
+
}
|
|
1587
|
+
function wallClockIn(at, zone) {
|
|
1588
|
+
const p = zoneParts(new Date(at), zone);
|
|
1589
|
+
return new Date(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
|
|
1590
|
+
}
|
|
1591
|
+
function instantOfWall(wall, zone) {
|
|
1592
|
+
return zonedToUtc(
|
|
1593
|
+
wall.getFullYear(),
|
|
1594
|
+
wall.getMonth() + 1,
|
|
1595
|
+
wall.getDate(),
|
|
1596
|
+
wall.getHours(),
|
|
1597
|
+
wall.getMinutes(),
|
|
1598
|
+
zone,
|
|
1599
|
+
wall.getSeconds()
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
function zoneCity(zone) {
|
|
1603
|
+
const last = zone.split("/").pop() ?? zone;
|
|
1604
|
+
return last.replace(/_/g, " ");
|
|
1605
|
+
}
|
|
1606
|
+
function zoneRegion(zone) {
|
|
1607
|
+
const parts = zone.split("/");
|
|
1608
|
+
return parts.length > 1 ? parts.slice(0, -1).join("/").replace(/_/g, " ") : "";
|
|
1609
|
+
}
|
|
1610
|
+
function zoneLabel(zone) {
|
|
1611
|
+
const region = zoneRegion(zone);
|
|
1612
|
+
return region ? `${zoneCity(zone)} \xB7 ${region}` : zoneCity(zone);
|
|
1613
|
+
}
|
|
1614
|
+
function localZone() {
|
|
1615
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1616
|
+
}
|
|
1617
|
+
function splitTime(d, clock = "12h") {
|
|
1618
|
+
const minutes = `${d.getMinutes()}`.padStart(2, "0");
|
|
1619
|
+
if (clock === "24h") {
|
|
1620
|
+
return { time: `${`${d.getHours()}`.padStart(2, "0")}:${minutes}`, suffix: "" };
|
|
1621
|
+
}
|
|
1622
|
+
const h = d.getHours();
|
|
1623
|
+
return { time: `${h % 12 || 12}:${minutes}`, suffix: h < 12 ? "AM" : "PM" };
|
|
1624
|
+
}
|
|
1625
|
+
function formatTime(d, clock = "12h") {
|
|
1626
|
+
const { time, suffix } = splitTime(d, clock);
|
|
1627
|
+
return suffix ? `${time} ${suffix}` : time;
|
|
1628
|
+
}
|
|
1629
|
+
function formatTimeShort(d, clock = "12h") {
|
|
1630
|
+
if (clock === "24h") return formatTime(d, clock);
|
|
1631
|
+
const h = d.getHours() % 12 || 12;
|
|
1632
|
+
const suffix = d.getHours() < 12 ? "AM" : "PM";
|
|
1633
|
+
return d.getMinutes() === 0 ? `${h} ${suffix}` : `${h}:${`${d.getMinutes()}`.padStart(2, "0")} ${suffix}`;
|
|
1634
|
+
}
|
|
1635
|
+
function formatDuration(minutes) {
|
|
1636
|
+
const m = Math.max(0, Math.round(minutes));
|
|
1637
|
+
if (m < 60) return `${m}m`;
|
|
1638
|
+
const h = Math.floor(m / 60);
|
|
1639
|
+
const rem = m % 60;
|
|
1640
|
+
return rem === 0 ? `${h}h` : `${h}h ${rem}m`;
|
|
1641
|
+
}
|
|
1642
|
+
function parseClock(value) {
|
|
1643
|
+
const match = /^\s*(\d{1,2})(?::?(\d{2}))?\s*(?:([ap])\.?m?\.?)?\s*$/i.exec(value);
|
|
1644
|
+
if (!match) return null;
|
|
1645
|
+
let hours = Number(match[1]);
|
|
1646
|
+
const minutes = match[2] ? Number(match[2]) : 0;
|
|
1647
|
+
const meridiem = match[3]?.toLowerCase();
|
|
1648
|
+
if (minutes > 59) return null;
|
|
1649
|
+
if (meridiem) {
|
|
1650
|
+
if (hours < 1 || hours > 12) return null;
|
|
1651
|
+
hours = hours % 12 + (meridiem === "p" ? 12 : 0);
|
|
1652
|
+
} else if (hours > 23) {
|
|
1653
|
+
return null;
|
|
1654
|
+
}
|
|
1655
|
+
return hours * 60 + minutes;
|
|
1656
|
+
}
|
|
1657
|
+
function snap(minutes, step = 15) {
|
|
1658
|
+
return Math.round(minutes / step) * step;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// src/lib/utils.ts
|
|
1662
|
+
function isRecord(value) {
|
|
1663
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// src/lib/recur.ts
|
|
1667
|
+
var FREQUENCIES = [
|
|
1668
|
+
"never",
|
|
1669
|
+
"secondly",
|
|
1670
|
+
"minutely",
|
|
1671
|
+
"hourly",
|
|
1672
|
+
"daily",
|
|
1673
|
+
"weekly",
|
|
1674
|
+
"monthly",
|
|
1675
|
+
"yearly"
|
|
1676
|
+
];
|
|
1677
|
+
var UNITS = {
|
|
1678
|
+
never: "",
|
|
1679
|
+
secondly: "second",
|
|
1680
|
+
minutely: "minute",
|
|
1681
|
+
hourly: "hour",
|
|
1682
|
+
daily: "day",
|
|
1683
|
+
weekly: "week",
|
|
1684
|
+
monthly: "month",
|
|
1685
|
+
yearly: "year"
|
|
1686
|
+
};
|
|
1687
|
+
function repeatUnit(repeat, n) {
|
|
1688
|
+
return n === 1 ? UNITS[repeat] : `${UNITS[repeat]}s`;
|
|
1689
|
+
}
|
|
1690
|
+
var NEVER = {
|
|
1691
|
+
repeat: "never",
|
|
1692
|
+
interval: 1,
|
|
1693
|
+
until: 0,
|
|
1694
|
+
wkst: 1,
|
|
1695
|
+
byMonth: [],
|
|
1696
|
+
byWeekNo: [],
|
|
1697
|
+
byYearDay: [],
|
|
1698
|
+
byMonthDay: [],
|
|
1699
|
+
byDay: [],
|
|
1700
|
+
byHour: [],
|
|
1701
|
+
byMinute: [],
|
|
1702
|
+
bySecond: [],
|
|
1703
|
+
bySetPos: [],
|
|
1704
|
+
exdates: [],
|
|
1705
|
+
rdates: []
|
|
1706
|
+
};
|
|
1707
|
+
function recurrenceOf(source) {
|
|
1708
|
+
return {
|
|
1709
|
+
repeat: source.repeat,
|
|
1710
|
+
interval: source.interval,
|
|
1711
|
+
until: source.until,
|
|
1712
|
+
wkst: source.wkst,
|
|
1713
|
+
byMonth: [...source.byMonth],
|
|
1714
|
+
byWeekNo: [...source.byWeekNo],
|
|
1715
|
+
byYearDay: [...source.byYearDay],
|
|
1716
|
+
byMonthDay: [...source.byMonthDay],
|
|
1717
|
+
byDay: source.byDay.map((d) => ({ ...d })),
|
|
1718
|
+
byHour: [...source.byHour],
|
|
1719
|
+
byMinute: [...source.byMinute],
|
|
1720
|
+
bySecond: [...source.bySecond],
|
|
1721
|
+
bySetPos: [...source.bySetPos],
|
|
1722
|
+
exdates: [...source.exdates],
|
|
1723
|
+
rdates: [...source.rdates]
|
|
1724
|
+
};
|
|
1725
|
+
}
|
|
1726
|
+
function repeats(rule) {
|
|
1727
|
+
return rule.repeat !== "never" || rule.rdates.length > 0;
|
|
1728
|
+
}
|
|
1729
|
+
var UNSIGNED_RANGE = {
|
|
1730
|
+
byMonth: [1, 12],
|
|
1731
|
+
byHour: [0, 23],
|
|
1732
|
+
byMinute: [0, 59],
|
|
1733
|
+
bySecond: [0, 60]
|
|
1734
|
+
};
|
|
1735
|
+
var SIGNED_MAX = {
|
|
1736
|
+
byWeekNo: 53,
|
|
1737
|
+
byYearDay: 366,
|
|
1738
|
+
byMonthDay: 31,
|
|
1739
|
+
bySetPos: 366
|
|
1740
|
+
};
|
|
1741
|
+
var MAX_INTERVAL = 999;
|
|
1742
|
+
var WEEKDAY_MAX = 6;
|
|
1743
|
+
var NTH_MAX = 5;
|
|
1744
|
+
var DATE_KEY = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2})?$/;
|
|
1745
|
+
function unsignedInts(v, [lo, hi]) {
|
|
1746
|
+
return Array.isArray(v) ? [...new Set(v.filter((n) => Number.isInteger(n) && n >= lo && n <= hi))] : [];
|
|
1747
|
+
}
|
|
1748
|
+
function signedInts(v, max) {
|
|
1749
|
+
return Array.isArray(v) ? [
|
|
1750
|
+
...new Set(
|
|
1751
|
+
v.filter((n) => Number.isInteger(n) && n !== 0 && n >= -max && n <= max)
|
|
1752
|
+
)
|
|
1753
|
+
] : [];
|
|
1754
|
+
}
|
|
1755
|
+
function dateKeys(v) {
|
|
1756
|
+
return Array.isArray(v) ? [...new Set(v.filter((k) => typeof k === "string" && DATE_KEY.test(k)))] : [];
|
|
1757
|
+
}
|
|
1758
|
+
function readRecurrence(value) {
|
|
1759
|
+
if (!isRecord(value)) return { ...NEVER };
|
|
1760
|
+
const dates = { exdates: dateKeys(value.exdates), rdates: dateKeys(value.rdates) };
|
|
1761
|
+
const { repeat, interval, until, wkst } = value;
|
|
1762
|
+
if (!FREQUENCIES.includes(repeat) || repeat === "never") {
|
|
1763
|
+
return { ...NEVER, ...dates };
|
|
1764
|
+
}
|
|
1765
|
+
const byDay = Array.isArray(value.byDay) ? value.byDay.filter(
|
|
1766
|
+
(d) => isRecord(d) && Number.isInteger(d.day) && d.day >= 0 && d.day <= WEEKDAY_MAX && Number.isInteger(d.nth) && d.nth >= -NTH_MAX && d.nth <= NTH_MAX
|
|
1767
|
+
).map((d) => ({ day: d.day, nth: d.nth })) : [];
|
|
1768
|
+
return {
|
|
1769
|
+
repeat,
|
|
1770
|
+
interval: typeof interval === "number" && Number.isFinite(interval) ? Math.min(MAX_INTERVAL, Math.max(1, Math.trunc(interval))) : NEVER.interval,
|
|
1771
|
+
until: typeof until === "number" && Number.isFinite(until) && until > 0 ? until : NEVER.until,
|
|
1772
|
+
wkst: Number.isInteger(wkst) && wkst >= 0 && wkst <= WEEKDAY_MAX ? wkst : NEVER.wkst,
|
|
1773
|
+
byMonth: unsignedInts(value.byMonth, UNSIGNED_RANGE.byMonth),
|
|
1774
|
+
byWeekNo: signedInts(value.byWeekNo, SIGNED_MAX.byWeekNo),
|
|
1775
|
+
byYearDay: signedInts(value.byYearDay, SIGNED_MAX.byYearDay),
|
|
1776
|
+
byMonthDay: signedInts(value.byMonthDay, SIGNED_MAX.byMonthDay),
|
|
1777
|
+
byDay,
|
|
1778
|
+
byHour: unsignedInts(value.byHour, UNSIGNED_RANGE.byHour),
|
|
1779
|
+
byMinute: unsignedInts(value.byMinute, UNSIGNED_RANGE.byMinute),
|
|
1780
|
+
bySecond: unsignedInts(value.bySecond, UNSIGNED_RANGE.bySecond),
|
|
1781
|
+
bySetPos: signedInts(value.bySetPos, SIGNED_MAX.bySetPos),
|
|
1782
|
+
...dates
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
var pad = (n) => String(n).padStart(2, "0");
|
|
1786
|
+
function instantKey(at) {
|
|
1787
|
+
const d = at instanceof Date ? at : new Date(at);
|
|
1788
|
+
return `${dayKey(d)}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
1789
|
+
}
|
|
1790
|
+
function keyToInstant(key, seed) {
|
|
1791
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2}))?$/.exec(key);
|
|
1792
|
+
if (!m) return null;
|
|
1793
|
+
const [y, mo, d] = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
1794
|
+
if (m[4] === void 0) {
|
|
1795
|
+
return new Date(y, mo - 1, d, seed.getHours(), seed.getMinutes(), seed.getSeconds()).getTime();
|
|
1796
|
+
}
|
|
1797
|
+
return new Date(y, mo - 1, d, Number(m[4]), Number(m[5]), Number(m[6])).getTime();
|
|
1798
|
+
}
|
|
1799
|
+
var HOUR_MS = 36e5;
|
|
1800
|
+
var MINUTE_MS = 6e4;
|
|
1801
|
+
var SECOND_MS = 1e3;
|
|
1802
|
+
var MAX_INSTANTS = 512;
|
|
1803
|
+
var isLeap = (y) => new Date(y, 1, 29).getMonth() === 1;
|
|
1804
|
+
var daysInYear = (y) => isLeap(y) ? 366 : 365;
|
|
1805
|
+
function weekStart(d, wkst) {
|
|
1806
|
+
const day = startOfDay(d);
|
|
1807
|
+
return addDays(day, -((day.getDay() - wkst + 7) % 7));
|
|
1808
|
+
}
|
|
1809
|
+
function monthDayOf(y, m, day) {
|
|
1810
|
+
const total = daysInMonth(y, m);
|
|
1811
|
+
const date = day > 0 ? day : total + 1 + day;
|
|
1812
|
+
return date >= 1 && date <= total ? new Date(y, m, date) : null;
|
|
1813
|
+
}
|
|
1814
|
+
function weekNoStart(year, n, wkst) {
|
|
1815
|
+
const firstWeek = (y) => {
|
|
1816
|
+
const jan1 = new Date(y, 0, 1);
|
|
1817
|
+
const into = (jan1.getDay() - wkst + 7) % 7;
|
|
1818
|
+
return addDays(jan1, into > 3 ? 7 - into : -into);
|
|
1819
|
+
};
|
|
1820
|
+
const start = firstWeek(year);
|
|
1821
|
+
const total = Math.round(daysBetween(start, firstWeek(year + 1)) / 7);
|
|
1822
|
+
const index = n > 0 ? n : total + 1 + n;
|
|
1823
|
+
return index >= 1 && index <= total ? addDays(start, (index - 1) * 7) : null;
|
|
1824
|
+
}
|
|
1825
|
+
function weekdaysIn(from, to, day) {
|
|
1826
|
+
const out = [];
|
|
1827
|
+
let cursor = addDays(from, (day - from.getDay() + 7) % 7);
|
|
1828
|
+
while (cursor < to) {
|
|
1829
|
+
out.push(cursor);
|
|
1830
|
+
cursor = addDays(cursor, 7);
|
|
1831
|
+
}
|
|
1832
|
+
return out;
|
|
1833
|
+
}
|
|
1834
|
+
function byDayWithin(rules, from, to) {
|
|
1835
|
+
const out = [];
|
|
1836
|
+
for (const rule of rules) {
|
|
1837
|
+
const all = weekdaysIn(from, to, rule.day);
|
|
1838
|
+
if (rule.nth === 0) out.push(...all);
|
|
1839
|
+
else {
|
|
1840
|
+
const hit = rule.nth > 0 ? all[rule.nth - 1] : all[all.length + rule.nth];
|
|
1841
|
+
if (hit) out.push(hit);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
return out;
|
|
1845
|
+
}
|
|
1846
|
+
var sortDates = (dates) => [...new Map(dates.map((d) => [d.getTime(), d])).values()].sort(
|
|
1847
|
+
(a, b) => a.getTime() - b.getTime()
|
|
1848
|
+
);
|
|
1849
|
+
function inMonths(rule, d) {
|
|
1850
|
+
return rule.byMonth.length === 0 || rule.byMonth.includes(d.getMonth() + 1);
|
|
1851
|
+
}
|
|
1852
|
+
function onMonthDays(rule, d) {
|
|
1853
|
+
if (rule.byMonthDay.length === 0) return true;
|
|
1854
|
+
const total = daysInMonth(d.getFullYear(), d.getMonth());
|
|
1855
|
+
return rule.byMonthDay.some((n) => (n > 0 ? n : total + 1 + n) === d.getDate());
|
|
1856
|
+
}
|
|
1857
|
+
function onWeekdays(rule, d) {
|
|
1858
|
+
return rule.byDay.length === 0 || rule.byDay.some((w) => w.day === d.getDay());
|
|
1859
|
+
}
|
|
1860
|
+
function onYearDays(rule, d) {
|
|
1861
|
+
if (rule.byYearDay.length === 0) return true;
|
|
1862
|
+
const len = daysInYear(d.getFullYear());
|
|
1863
|
+
const doy = daysBetween(new Date(d.getFullYear(), 0, 1), d) + 1;
|
|
1864
|
+
return rule.byYearDay.some((n) => (n > 0 ? n : len + 1 + n) === doy);
|
|
1865
|
+
}
|
|
1866
|
+
function daysOfPeriod(rule, seed, period) {
|
|
1867
|
+
const y = period.getFullYear();
|
|
1868
|
+
if (rule.repeat === "daily") {
|
|
1869
|
+
const d = startOfDay(period);
|
|
1870
|
+
return inMonths(rule, d) && onMonthDays(rule, d) && onWeekdays(rule, d) ? [d] : [];
|
|
1871
|
+
}
|
|
1872
|
+
if (rule.repeat === "weekly") {
|
|
1873
|
+
const from = weekStart(period, rule.wkst);
|
|
1874
|
+
const to = addDays(from, 7);
|
|
1875
|
+
const days = rule.byDay.length > 0 ? byDayWithin(rule.byDay, from, to) : [seedDayIn(seed, from)];
|
|
1876
|
+
return sortDates(days.filter((d) => inMonths(rule, d)));
|
|
1877
|
+
}
|
|
1878
|
+
if (rule.repeat === "monthly") {
|
|
1879
|
+
if (!inMonths(rule, period)) return [];
|
|
1880
|
+
const m = period.getMonth();
|
|
1881
|
+
const from = new Date(y, m, 1);
|
|
1882
|
+
const to = new Date(y, m + 1, 1);
|
|
1883
|
+
const fromMonthDay = rule.byMonthDay.map((n) => monthDayOf(y, m, n)).filter((d) => d !== null);
|
|
1884
|
+
const fromWeekday = rule.byDay.length > 0 ? byDayWithin(rule.byDay, from, to) : [];
|
|
1885
|
+
if (rule.byMonthDay.length > 0 && rule.byDay.length > 0) {
|
|
1886
|
+
const wanted = new Set(fromWeekday.map((d) => d.getTime()));
|
|
1887
|
+
return sortDates(fromMonthDay.filter((d) => wanted.has(d.getTime())));
|
|
1888
|
+
}
|
|
1889
|
+
if (rule.byMonthDay.length > 0) return sortDates(fromMonthDay);
|
|
1890
|
+
if (rule.byDay.length > 0) return sortDates(fromWeekday);
|
|
1891
|
+
const own = monthDayOf(y, m, seed.getDate());
|
|
1892
|
+
return own ? [own] : [];
|
|
1893
|
+
}
|
|
1894
|
+
return yearlyDays(rule, seed, y);
|
|
1895
|
+
}
|
|
1896
|
+
function seedDayIn(seed, from) {
|
|
1897
|
+
return addDays(from, (seed.getDay() - from.getDay() + 7) % 7);
|
|
1898
|
+
}
|
|
1899
|
+
function yearlyDays(rule, seed, y) {
|
|
1900
|
+
const months = rule.byMonth.length > 0 ? rule.byMonth.map((m) => m - 1) : null;
|
|
1901
|
+
if (rule.byYearDay.length > 0) {
|
|
1902
|
+
const len = daysInYear(y);
|
|
1903
|
+
const days = rule.byYearDay.map((n) => n > 0 ? n : len + 1 + n).filter((n) => n >= 1 && n <= len).map((n) => new Date(y, 0, n));
|
|
1904
|
+
return sortDates(days.filter((d) => inMonths(rule, d) && onWeekdays(rule, d)));
|
|
1905
|
+
}
|
|
1906
|
+
if (rule.byWeekNo.length > 0) {
|
|
1907
|
+
const wanted = rule.byDay.length > 0 ? rule.byDay.map((w) => w.day) : [seed.getDay()];
|
|
1908
|
+
const days = [];
|
|
1909
|
+
for (const n of rule.byWeekNo) {
|
|
1910
|
+
const from = weekNoStart(y, n, rule.wkst);
|
|
1911
|
+
if (!from) continue;
|
|
1912
|
+
for (let i = 0; i < 7; i++) {
|
|
1913
|
+
const d = addDays(from, i);
|
|
1914
|
+
if (wanted.includes(d.getDay())) days.push(d);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
return sortDates(days.filter((d) => inMonths(rule, d)));
|
|
1918
|
+
}
|
|
1919
|
+
if (rule.byDay.length > 0 && rule.byMonthDay.length === 0 && months === null) {
|
|
1920
|
+
return sortDates(byDayWithin(rule.byDay, new Date(y, 0, 1), new Date(y + 1, 0, 1)));
|
|
1921
|
+
}
|
|
1922
|
+
if (rule.byDay.length > 0 || rule.byMonthDay.length > 0) {
|
|
1923
|
+
const list2 = months ?? [...Array(12).keys()];
|
|
1924
|
+
const days = [];
|
|
1925
|
+
for (const m of list2) {
|
|
1926
|
+
days.push(
|
|
1927
|
+
...daysOfPeriod({ ...rule, repeat: "monthly", byMonth: [] }, seed, new Date(y, m, 1))
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
return sortDates(days);
|
|
1931
|
+
}
|
|
1932
|
+
const list = months ?? [seed.getMonth()];
|
|
1933
|
+
return sortDates(list.map((m) => monthDayOf(y, m, seed.getDate())).filter((d) => !!d));
|
|
1934
|
+
}
|
|
1935
|
+
function clockOf(rule, seed) {
|
|
1936
|
+
const hours = rule.byHour.length > 0 ? rule.byHour : [seed.getHours()];
|
|
1937
|
+
const minutes = rule.byMinute.length > 0 ? rule.byMinute : [seed.getMinutes()];
|
|
1938
|
+
const seconds = rule.bySecond.length > 0 ? rule.bySecond : [seed.getSeconds()];
|
|
1939
|
+
const out = [];
|
|
1940
|
+
for (const h of hours) for (const m of minutes) for (const s of seconds) out.push({ h, m, s });
|
|
1941
|
+
return out;
|
|
1942
|
+
}
|
|
1943
|
+
function applySetPos(rule, instants) {
|
|
1944
|
+
if (rule.bySetPos.length === 0) return instants;
|
|
1945
|
+
const picked = rule.bySetPos.map((n) => n > 0 ? instants[n - 1] : instants[instants.length + n]).filter((t) => t !== void 0);
|
|
1946
|
+
return [...new Set(picked)].sort((a, b) => a - b);
|
|
1947
|
+
}
|
|
1948
|
+
function onInterval(rule, seed, period) {
|
|
1949
|
+
const every = Math.max(1, Math.trunc(rule.interval));
|
|
1950
|
+
if (every === 1) return true;
|
|
1951
|
+
switch (rule.repeat) {
|
|
1952
|
+
case "daily":
|
|
1953
|
+
return daysBetween(seed, period) % every === 0;
|
|
1954
|
+
case "weekly":
|
|
1955
|
+
return Math.round(daysBetween(weekStart(seed, rule.wkst), weekStart(period, rule.wkst)) / 7) % every === 0;
|
|
1956
|
+
case "monthly":
|
|
1957
|
+
return ((period.getFullYear() - seed.getFullYear()) * 12 + period.getMonth() - seed.getMonth()) % every === 0;
|
|
1958
|
+
default:
|
|
1959
|
+
return (period.getFullYear() - seed.getFullYear()) % every === 0;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
function periodInstants(rule, seed, period) {
|
|
1963
|
+
if (!onInterval(rule, seed, period)) return [];
|
|
1964
|
+
const clock = clockOf(rule, seed);
|
|
1965
|
+
const out = [];
|
|
1966
|
+
for (const day of daysOfPeriod(rule, seed, period)) {
|
|
1967
|
+
for (const { h, m, s } of clock) {
|
|
1968
|
+
out.push(new Date(day.getFullYear(), day.getMonth(), day.getDate(), h, m, s).getTime());
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
out.sort((a, b) => a - b);
|
|
1972
|
+
return applySetPos(rule, out);
|
|
1973
|
+
}
|
|
1974
|
+
function periodsOver(rule, from, to) {
|
|
1975
|
+
const first = startOfDay(new Date(from));
|
|
1976
|
+
const last = startOfDay(new Date(to - 1));
|
|
1977
|
+
const out = [];
|
|
1978
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1979
|
+
const push = (d) => {
|
|
1980
|
+
if (!seen.has(d.getTime())) {
|
|
1981
|
+
seen.add(d.getTime());
|
|
1982
|
+
out.push(d);
|
|
1983
|
+
}
|
|
1984
|
+
};
|
|
1985
|
+
for (let d = first; d <= last; d = addDays(d, 1)) {
|
|
1986
|
+
switch (rule.repeat) {
|
|
1987
|
+
case "daily":
|
|
1988
|
+
push(d);
|
|
1989
|
+
break;
|
|
1990
|
+
case "weekly":
|
|
1991
|
+
push(weekStart(d, rule.wkst));
|
|
1992
|
+
break;
|
|
1993
|
+
case "monthly":
|
|
1994
|
+
push(new Date(d.getFullYear(), d.getMonth(), 1));
|
|
1995
|
+
break;
|
|
1996
|
+
default:
|
|
1997
|
+
push(new Date(d.getFullYear(), 0, 1));
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
return out;
|
|
2001
|
+
}
|
|
2002
|
+
function subDailyInstants(rule, seed, from, to) {
|
|
2003
|
+
const unit = rule.repeat === "hourly" ? HOUR_MS : rule.repeat === "minutely" ? MINUTE_MS : SECOND_MS;
|
|
2004
|
+
const step = unit * Math.max(1, Math.trunc(rule.interval));
|
|
2005
|
+
const seedMs = seed.getTime();
|
|
2006
|
+
if (to <= seedMs) return [];
|
|
2007
|
+
const out = [];
|
|
2008
|
+
const first = Math.max(0, Math.ceil((from - seedMs) / step));
|
|
2009
|
+
for (let k = first; out.length < MAX_INSTANTS; k++) {
|
|
2010
|
+
const base = seedMs + k * step;
|
|
2011
|
+
if (base >= to) break;
|
|
2012
|
+
for (const t of subDailyClock(rule, base)) {
|
|
2013
|
+
if (t < from || t >= to) continue;
|
|
2014
|
+
const d = new Date(t);
|
|
2015
|
+
if (!inMonths(rule, d) || !onYearDays(rule, d)) continue;
|
|
2016
|
+
if (!onMonthDays(rule, d) || !onWeekdays(rule, d)) continue;
|
|
2017
|
+
if (rule.byHour.length > 0 && !rule.byHour.includes(d.getHours())) continue;
|
|
2018
|
+
if (rule.repeat !== "hourly" && rule.byMinute.length > 0) {
|
|
2019
|
+
if (!rule.byMinute.includes(d.getMinutes())) continue;
|
|
2020
|
+
}
|
|
2021
|
+
if (rule.repeat === "secondly" && rule.bySecond.length > 0) {
|
|
2022
|
+
if (!rule.bySecond.includes(d.getSeconds())) continue;
|
|
2023
|
+
}
|
|
2024
|
+
out.push(t);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
return applySetPos(
|
|
2028
|
+
rule,
|
|
2029
|
+
[...new Set(out)].sort((a, b) => a - b)
|
|
2030
|
+
);
|
|
2031
|
+
}
|
|
2032
|
+
function subDailyClock(rule, base) {
|
|
2033
|
+
const d = new Date(base);
|
|
2034
|
+
const minutes = rule.repeat === "hourly" && rule.byMinute.length > 0 ? rule.byMinute : [null];
|
|
2035
|
+
const seconds = rule.repeat !== "secondly" && rule.bySecond.length > 0 ? rule.bySecond : [null];
|
|
2036
|
+
const out = [];
|
|
2037
|
+
for (const m of minutes) {
|
|
2038
|
+
for (const s of seconds) {
|
|
2039
|
+
const at = new Date(d);
|
|
2040
|
+
if (m !== null) at.setMinutes(m);
|
|
2041
|
+
if (s !== null) at.setSeconds(s);
|
|
2042
|
+
out.push(at.getTime());
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
return out;
|
|
2046
|
+
}
|
|
2047
|
+
function excluded(rule, at) {
|
|
2048
|
+
if (rule.exdates.length === 0) return false;
|
|
2049
|
+
const d = new Date(at);
|
|
2050
|
+
return rule.exdates.includes(dayKey(d)) || rule.exdates.includes(instantKey(d));
|
|
2051
|
+
}
|
|
2052
|
+
function startsBetween(rule, seedMs, from, to) {
|
|
2053
|
+
const seed = new Date(seedMs);
|
|
2054
|
+
const found = /* @__PURE__ */ new Set();
|
|
2055
|
+
if (rule.repeat === "never" && seedMs >= from && seedMs < to) found.add(seedMs);
|
|
2056
|
+
for (const key of rule.rdates) {
|
|
2057
|
+
const at = keyToInstant(key, seed);
|
|
2058
|
+
if (at !== null && at >= from && at < to) found.add(at);
|
|
2059
|
+
}
|
|
2060
|
+
if (rule.repeat !== "never") {
|
|
2061
|
+
const generated = rule.repeat === "hourly" || rule.repeat === "minutely" || rule.repeat === "secondly" ? subDailyInstants(rule, seed, from, to) : periodsOver(rule, from, to).flatMap((p) => periodInstants(rule, seed, p));
|
|
2062
|
+
for (const at of generated) {
|
|
2063
|
+
if (at < seedMs || at < from || at >= to) continue;
|
|
2064
|
+
if (rule.until > 0 && at > rule.until) continue;
|
|
2065
|
+
found.add(at);
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
return [...found].filter((at) => !excluded(rule, at)).sort((a, b) => a - b).slice(0, MAX_INSTANTS);
|
|
2069
|
+
}
|
|
2070
|
+
var ZONE_SLACK = 3 * 60 * 6e4;
|
|
2071
|
+
function startsBetweenIn(rule, seedMs, from, to, zone) {
|
|
2072
|
+
if (!isZone(zone)) return startsBetween(rule, seedMs, from, to);
|
|
2073
|
+
const wallSeed = wallClockIn(seedMs, zone).getTime();
|
|
2074
|
+
const wallFrom = wallClockIn(from - ZONE_SLACK, zone).getTime();
|
|
2075
|
+
const wallTo = wallClockIn(to + ZONE_SLACK, zone).getTime();
|
|
2076
|
+
const wallRule = rule.until > 0 ? { ...rule, until: wallClockIn(rule.until, zone).getTime() } : rule;
|
|
2077
|
+
return startsBetween(wallRule, wallSeed, wallFrom, wallTo).map((wallAt) => instantOfWall(new Date(wallAt), zone)).filter((at) => at >= from && at < to).sort((a, b) => a - b);
|
|
2078
|
+
}
|
|
2079
|
+
function splitRule(rule, seedMs, at) {
|
|
2080
|
+
const seed = new Date(seedMs);
|
|
2081
|
+
const bound = at - 1;
|
|
2082
|
+
const before = (key) => {
|
|
2083
|
+
const instant = keyToInstant(key, seed);
|
|
2084
|
+
return instant === null || instant <= bound;
|
|
2085
|
+
};
|
|
2086
|
+
return {
|
|
2087
|
+
head: {
|
|
2088
|
+
until: rule.until > 0 ? Math.min(rule.until, bound) : bound,
|
|
2089
|
+
exdates: rule.exdates.filter(before),
|
|
2090
|
+
rdates: rule.rdates.filter(before)
|
|
2091
|
+
},
|
|
2092
|
+
tail: {
|
|
2093
|
+
exdates: rule.exdates.filter((k) => !before(k)),
|
|
2094
|
+
rdates: rule.rdates.filter((k) => !before(k))
|
|
2095
|
+
}
|
|
2096
|
+
};
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
// src/lib/fuzzy.ts
|
|
2100
|
+
var MATCH = 10;
|
|
2101
|
+
var RUN = 9;
|
|
2102
|
+
var WORD_START = 12;
|
|
2103
|
+
var LEAD = 6;
|
|
2104
|
+
var GAP = 1;
|
|
2105
|
+
var GAP_CAP = 12;
|
|
2106
|
+
var LENGTH = 0.2;
|
|
2107
|
+
var TYPO = 40;
|
|
2108
|
+
var TYPO_MIN = 4;
|
|
2109
|
+
var WORD_BREAK = /[\s\-_/:.,(–—]/;
|
|
2110
|
+
function scan(text, query, start = 0) {
|
|
2111
|
+
let score = 0;
|
|
2112
|
+
let at = start;
|
|
2113
|
+
let previous = -2;
|
|
2114
|
+
let run = 0;
|
|
2115
|
+
for (const character of query) {
|
|
2116
|
+
if (character === " ") continue;
|
|
2117
|
+
const found = text.indexOf(character, at);
|
|
2118
|
+
if (found === -1) return null;
|
|
2119
|
+
score += MATCH;
|
|
2120
|
+
if (found === previous + 1) score += RUN * ++run;
|
|
2121
|
+
else {
|
|
2122
|
+
run = 0;
|
|
2123
|
+
score -= Math.min(found - at, GAP_CAP) * GAP;
|
|
2124
|
+
}
|
|
2125
|
+
if (found === 0) score += WORD_START + LEAD;
|
|
2126
|
+
else if (WORD_BREAK.test(text[found - 1])) score += WORD_START;
|
|
2127
|
+
previous = found;
|
|
2128
|
+
at = found + 1;
|
|
2129
|
+
}
|
|
2130
|
+
return score - text.length * LENGTH;
|
|
2131
|
+
}
|
|
2132
|
+
function bestScan(text, query) {
|
|
2133
|
+
const first = query.replace(/\s+/g, "")[0];
|
|
2134
|
+
if (!first) return null;
|
|
2135
|
+
let best = null;
|
|
2136
|
+
let tried = false;
|
|
2137
|
+
for (let i = text.indexOf(first); i !== -1; i = text.indexOf(first, i + 1)) {
|
|
2138
|
+
if (tried && i !== 0 && !WORD_BREAK.test(text[i - 1])) continue;
|
|
2139
|
+
tried = true;
|
|
2140
|
+
const score = scan(text, query, i);
|
|
2141
|
+
if (score !== null && (best === null || score > best)) best = score;
|
|
2142
|
+
}
|
|
2143
|
+
return best;
|
|
2144
|
+
}
|
|
2145
|
+
function fuzzyScore(text, query) {
|
|
2146
|
+
const wanted = query.trim().toLowerCase();
|
|
2147
|
+
if (!wanted) return 0;
|
|
2148
|
+
if (!text) return null;
|
|
2149
|
+
const haystack = text.toLowerCase();
|
|
2150
|
+
const clean = bestScan(haystack, wanted);
|
|
2151
|
+
if (clean !== null) return clean;
|
|
2152
|
+
const letters = wanted.replace(/\s+/g, "");
|
|
2153
|
+
if (letters.length < TYPO_MIN) return null;
|
|
2154
|
+
let best = null;
|
|
2155
|
+
for (let i = 0; i < wanted.length; i++) {
|
|
2156
|
+
if (wanted[i] === " ") continue;
|
|
2157
|
+
const score = bestScan(haystack, wanted.slice(0, i) + wanted.slice(i + 1));
|
|
2158
|
+
if (score !== null && (best === null || score > best)) best = score;
|
|
2159
|
+
}
|
|
2160
|
+
return best === null ? null : best - TYPO;
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
// src/lib/events.ts
|
|
2164
|
+
var ICON_MAX = 16;
|
|
2165
|
+
function overridesIn(events) {
|
|
2166
|
+
const out = /* @__PURE__ */ new Map();
|
|
2167
|
+
for (const event of events) {
|
|
2168
|
+
if (!event.seriesId || !event.seriesKey) continue;
|
|
2169
|
+
const keys = out.get(event.seriesId);
|
|
2170
|
+
if (keys) keys.add(event.seriesKey);
|
|
2171
|
+
else out.set(event.seriesId, /* @__PURE__ */ new Set([event.seriesKey]));
|
|
2172
|
+
}
|
|
2173
|
+
return out;
|
|
2174
|
+
}
|
|
2175
|
+
var DAY_MS2 = 864e5;
|
|
2176
|
+
var MAX_SPAN_MS = 366 * DAY_MS2;
|
|
2177
|
+
var DENSE = /* @__PURE__ */ new Set(["secondly", "minutely", "hourly"]);
|
|
2178
|
+
function lookbackCap(repeat) {
|
|
2179
|
+
if (repeat === "never") return Infinity;
|
|
2180
|
+
return DENSE.has(repeat) ? DAY_MS2 : MAX_SPAN_MS;
|
|
2181
|
+
}
|
|
2182
|
+
function occurrencesOn(events, calendars, day) {
|
|
2183
|
+
const hidden = new Set(calendars.filter((c) => !c.visible).map((c) => c.id));
|
|
2184
|
+
const dayStart = startOfDay(day);
|
|
2185
|
+
const dayEnd = addDays(dayStart, 1);
|
|
2186
|
+
const replaced = overridesIn(events);
|
|
2187
|
+
const out = [];
|
|
2188
|
+
for (const event of events) {
|
|
2189
|
+
if (hidden.has(event.calendarId)) continue;
|
|
2190
|
+
const duration = Math.max(event.end - event.start, 0);
|
|
2191
|
+
const span = Math.max(duration, 1);
|
|
2192
|
+
const from = dayStart.getTime() - Math.min(span, lookbackCap(event.repeat));
|
|
2193
|
+
const skip = replaced.get(event.id);
|
|
2194
|
+
for (const at of startsBetweenIn(event, event.start, from, dayEnd.getTime(), event.tzid)) {
|
|
2195
|
+
if (skip?.has(instantKey(at))) continue;
|
|
2196
|
+
const start = new Date(at);
|
|
2197
|
+
const end = new Date(at + duration);
|
|
2198
|
+
if (at + span <= dayStart.getTime() || start >= dayEnd) continue;
|
|
2199
|
+
const continuesBefore = start < dayStart;
|
|
2200
|
+
const continuesAfter = end > dayEnd;
|
|
2201
|
+
const firstDay = startOfDay(start);
|
|
2202
|
+
const lastDay = startOfDay(new Date(Math.max(at, at + duration - 1)));
|
|
2203
|
+
out.push({
|
|
2204
|
+
key: `${event.id}@${instantKey(start)}`,
|
|
2205
|
+
event,
|
|
2206
|
+
start,
|
|
2207
|
+
end,
|
|
2208
|
+
startMin: continuesBefore ? 0 : minutesOfDay(start),
|
|
2209
|
+
// `>=`, not `continuesAfter`: an event ending *exactly* at midnight ends
|
|
2210
|
+
// this day at 1440 rather than the next one at 0. `minutesOfDay` would
|
|
2211
|
+
// say 0, which is a negative duration everywhere it is measured and an
|
|
2212
|
+
// arc drawn backwards round the whole face.
|
|
2213
|
+
endMin: end >= dayEnd ? MINUTES_PER_DAY : minutesOfDay(end),
|
|
2214
|
+
continuesBefore,
|
|
2215
|
+
continuesAfter,
|
|
2216
|
+
dayIndex: daysBetween(firstDay, dayStart) + 1,
|
|
2217
|
+
dayCount: daysBetween(firstDay, lastDay) + 1,
|
|
2218
|
+
// A tick is per occurrence, so it is looked up per occurrence. The
|
|
2219
|
+
// list is empty on everything that is not a task, which is almost
|
|
2220
|
+
// every event in the store.
|
|
2221
|
+
done: event.doneKeys.length > 0 && event.doneKeys.includes(instantKey(start)),
|
|
2222
|
+
lane: 0
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
out.sort((a, b) => a.start.getTime() - b.start.getTime() || a.end.getTime() - b.end.getTime());
|
|
2227
|
+
return out;
|
|
2228
|
+
}
|
|
2229
|
+
function packLanes(occurrences, pad2 = 6) {
|
|
2230
|
+
const laneEnds = [];
|
|
2231
|
+
return occurrences.map((occ) => {
|
|
2232
|
+
if (occ.event.allDay) return occ;
|
|
2233
|
+
if (occ.event.task) return occ;
|
|
2234
|
+
let lane = laneEnds.findIndex((end) => end <= occ.startMin + pad2);
|
|
2235
|
+
if (lane === -1) lane = laneEnds.length;
|
|
2236
|
+
laneEnds[lane] = occ.endMin;
|
|
2237
|
+
return { ...occ, lane };
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
function dayAgenda(events, calendars, day) {
|
|
2241
|
+
const all = occurrencesOn(events, calendars, day);
|
|
2242
|
+
const allDay = all.filter((o) => o.event.allDay);
|
|
2243
|
+
const timed = packLanes(all.filter((o) => !o.event.allDay));
|
|
2244
|
+
return { timed, allDay, all: [...allDay, ...timed] };
|
|
2245
|
+
}
|
|
2246
|
+
function toggleDoneKey(keys, at) {
|
|
2247
|
+
const key = instantKey(at);
|
|
2248
|
+
return keys.includes(key) ? keys.filter((k) => k !== key) : [...keys, key];
|
|
2249
|
+
}
|
|
2250
|
+
var DEFAULT_WAKING = { from: 8 * 60, to: 22 * 60 };
|
|
2251
|
+
function freeGaps(occurrences, minMinutes, from, to) {
|
|
2252
|
+
const busy = occurrences.filter((o) => !o.event.allDay).map((o) => ({ start: Math.max(o.startMin, from), end: Math.min(o.endMin, to) })).filter((b) => b.end > b.start).sort((a, b) => a.start - b.start);
|
|
2253
|
+
const gaps = [];
|
|
2254
|
+
let cursor = from;
|
|
2255
|
+
for (const block of busy) {
|
|
2256
|
+
if (block.start - cursor >= minMinutes) gaps.push({ start: cursor, end: block.start });
|
|
2257
|
+
cursor = Math.max(cursor, block.end);
|
|
2258
|
+
}
|
|
2259
|
+
if (to - cursor >= minMinutes) gaps.push({ start: cursor, end: to });
|
|
2260
|
+
return gaps;
|
|
2261
|
+
}
|
|
2262
|
+
function dayLoad(occurrences, waking) {
|
|
2263
|
+
if (!occurrences.some((occ) => !occ.event.allDay)) return null;
|
|
2264
|
+
const idle = (from, to) => freeGaps(occurrences, 1, from, to).reduce((sum, gap) => sum + (gap.end - gap.start), 0);
|
|
2265
|
+
return {
|
|
2266
|
+
booked: MINUTES_PER_DAY - idle(0, MINUTES_PER_DAY),
|
|
2267
|
+
free: idle(waking.from, waking.to)
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
var FIELD_PENALTY = { location: 30, notes: 45 };
|
|
2271
|
+
var penalise = (score, cost) => score === null ? null : score - cost;
|
|
2272
|
+
function scoreEvent(event, query) {
|
|
2273
|
+
const scores = [
|
|
2274
|
+
fuzzyScore(event.title, query),
|
|
2275
|
+
penalise(fuzzyScore(event.location, query), FIELD_PENALTY.location),
|
|
2276
|
+
penalise(fuzzyScore(event.notes, query), FIELD_PENALTY.notes)
|
|
2277
|
+
].filter((score) => score !== null);
|
|
2278
|
+
return scores.length > 0 ? Math.max(...scores) : null;
|
|
2279
|
+
}
|
|
2280
|
+
var PALETTE = [
|
|
2281
|
+
"#7ce3d8",
|
|
2282
|
+
// aqua
|
|
2283
|
+
"#8ecbff",
|
|
2284
|
+
// sky
|
|
2285
|
+
"#a5a8ff",
|
|
2286
|
+
// periwinkle
|
|
2287
|
+
"#c79bff",
|
|
2288
|
+
// violet
|
|
2289
|
+
"#ff9edb",
|
|
2290
|
+
// orchid
|
|
2291
|
+
"#ff9fb4",
|
|
2292
|
+
// rose
|
|
2293
|
+
"#ffb185",
|
|
2294
|
+
// tangerine
|
|
2295
|
+
"#ffdf8c",
|
|
2296
|
+
// butter
|
|
2297
|
+
"#c6ef8f",
|
|
2298
|
+
// pear
|
|
2299
|
+
"#8bf0b8"
|
|
2300
|
+
// mint
|
|
2301
|
+
];
|
|
2302
|
+
var ORPHAN_COLOR = PALETTE[0];
|
|
2303
|
+
function spanLabel(occ) {
|
|
2304
|
+
return occ.dayCount > 1 ? `Day ${occ.dayIndex}/${occ.dayCount}` : null;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
// src/lib/ics.ts
|
|
2308
|
+
var encoder = new TextEncoder();
|
|
2309
|
+
function fnv1a(source) {
|
|
2310
|
+
let h = 2166136261;
|
|
2311
|
+
for (let i = 0; i < source.length; i++) {
|
|
2312
|
+
h ^= source.charCodeAt(i);
|
|
2313
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
2314
|
+
}
|
|
2315
|
+
return h.toString(36);
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
// src/lib/places.ts
|
|
2319
|
+
var COORDS = {
|
|
2320
|
+
"Africa/Abidjan": [5.32, -4.03],
|
|
2321
|
+
"Africa/Accra": [5.55, -0.22],
|
|
2322
|
+
"Africa/Addis_Ababa": [9.03, 38.7],
|
|
2323
|
+
"Africa/Algiers": [36.78, 3.05],
|
|
2324
|
+
"Africa/Asmara": [15.33, 38.88],
|
|
2325
|
+
"Africa/Bamako": [12.65, -8],
|
|
2326
|
+
"Africa/Bangui": [4.37, 18.58],
|
|
2327
|
+
"Africa/Banjul": [13.47, -16.65],
|
|
2328
|
+
"Africa/Bissau": [11.85, -15.58],
|
|
2329
|
+
"Africa/Blantyre": [-15.78, 35],
|
|
2330
|
+
"Africa/Brazzaville": [-4.27, 15.28],
|
|
2331
|
+
"Africa/Bujumbura": [-3.38, 29.37],
|
|
2332
|
+
"Africa/Cairo": [30.05, 31.25],
|
|
2333
|
+
"Africa/Casablanca": [33.65, -7.58],
|
|
2334
|
+
"Africa/Ceuta": [35.88, -5.32],
|
|
2335
|
+
"Africa/Conakry": [9.52, -13.72],
|
|
2336
|
+
"Africa/Dakar": [14.67, -17.43],
|
|
2337
|
+
"Africa/Dar_es_Salaam": [-6.8, 39.28],
|
|
2338
|
+
"Africa/Djibouti": [11.6, 43.15],
|
|
2339
|
+
"Africa/Douala": [4.05, 9.7],
|
|
2340
|
+
"Africa/El_Aaiun": [27.15, -13.2],
|
|
2341
|
+
"Africa/Freetown": [8.5, -13.25],
|
|
2342
|
+
"Africa/Gaborone": [-24.65, 25.92],
|
|
2343
|
+
"Africa/Harare": [-17.83, 31.05],
|
|
2344
|
+
"Africa/Johannesburg": [-26.25, 28],
|
|
2345
|
+
"Africa/Juba": [4.85, 31.62],
|
|
2346
|
+
"Africa/Kampala": [0.32, 32.42],
|
|
2347
|
+
"Africa/Khartoum": [15.6, 32.53],
|
|
2348
|
+
"Africa/Kigali": [-1.95, 30.07],
|
|
2349
|
+
"Africa/Kinshasa": [-4.3, 15.3],
|
|
2350
|
+
"Africa/Lagos": [6.45, 3.4],
|
|
2351
|
+
"Africa/Libreville": [0.38, 9.45],
|
|
2352
|
+
"Africa/Lome": [6.13, 1.22],
|
|
2353
|
+
"Africa/Luanda": [-8.8, 13.23],
|
|
2354
|
+
"Africa/Lubumbashi": [-11.67, 27.47],
|
|
2355
|
+
"Africa/Lusaka": [-15.42, 28.28],
|
|
2356
|
+
"Africa/Malabo": [3.75, 8.78],
|
|
2357
|
+
"Africa/Maputo": [-25.97, 32.58],
|
|
2358
|
+
"Africa/Maseru": [-29.47, 27.5],
|
|
2359
|
+
"Africa/Mbabane": [-26.3, 31.1],
|
|
2360
|
+
"Africa/Mogadishu": [2.07, 45.37],
|
|
2361
|
+
"Africa/Monrovia": [6.3, -10.78],
|
|
2362
|
+
"Africa/Nairobi": [-1.28, 36.82],
|
|
2363
|
+
"Africa/Ndjamena": [12.12, 15.05],
|
|
2364
|
+
"Africa/Niamey": [13.52, 2.12],
|
|
2365
|
+
"Africa/Nouakchott": [18.1, -15.95],
|
|
2366
|
+
"Africa/Ouagadougou": [12.37, -1.52],
|
|
2367
|
+
"Africa/Porto-Novo": [6.48, 2.62],
|
|
2368
|
+
"Africa/Sao_Tome": [0.33, 6.73],
|
|
2369
|
+
"Africa/Tripoli": [32.9, 13.18],
|
|
2370
|
+
"Africa/Tunis": [36.8, 10.18],
|
|
2371
|
+
"Africa/Windhoek": [-22.57, 17.1],
|
|
2372
|
+
"America/Adak": [51.88, -176.66],
|
|
2373
|
+
"America/Anchorage": [61.22, -149.9],
|
|
2374
|
+
"America/Anguilla": [18.2, -63.07],
|
|
2375
|
+
"America/Antigua": [17.05, -61.8],
|
|
2376
|
+
"America/Araguaina": [-7.2, -48.2],
|
|
2377
|
+
"America/Argentina/Buenos_Aires": [-34.6, -58.45],
|
|
2378
|
+
"America/Argentina/Catamarca": [-28.47, -65.78],
|
|
2379
|
+
"America/Argentina/Cordoba": [-31.4, -64.18],
|
|
2380
|
+
"America/Argentina/Jujuy": [-24.18, -65.3],
|
|
2381
|
+
"America/Argentina/La_Rioja": [-29.43, -66.85],
|
|
2382
|
+
"America/Argentina/Mendoza": [-32.88, -68.82],
|
|
2383
|
+
"America/Argentina/Rio_Gallegos": [-51.63, -69.22],
|
|
2384
|
+
"America/Argentina/Salta": [-24.78, -65.42],
|
|
2385
|
+
"America/Argentina/San_Juan": [-31.53, -68.52],
|
|
2386
|
+
"America/Argentina/San_Luis": [-33.32, -66.35],
|
|
2387
|
+
"America/Argentina/Tucuman": [-26.82, -65.22],
|
|
2388
|
+
"America/Argentina/Ushuaia": [-54.8, -68.3],
|
|
2389
|
+
"America/Aruba": [12.5, -69.97],
|
|
2390
|
+
"America/Asuncion": [-25.27, -57.67],
|
|
2391
|
+
"America/Atikokan": [48.76, -91.62],
|
|
2392
|
+
"America/Bahia": [-12.98, -38.52],
|
|
2393
|
+
"America/Bahia_Banderas": [20.8, -105.25],
|
|
2394
|
+
"America/Barbados": [13.1, -59.62],
|
|
2395
|
+
"America/Belem": [-1.45, -48.48],
|
|
2396
|
+
"America/Belize": [17.5, -88.2],
|
|
2397
|
+
"America/Blanc-Sablon": [51.42, -57.12],
|
|
2398
|
+
"America/Boa_Vista": [2.82, -60.67],
|
|
2399
|
+
"America/Bogota": [4.6, -74.08],
|
|
2400
|
+
"America/Boise": [43.61, -116.2],
|
|
2401
|
+
"America/Cambridge_Bay": [69.11, -105.05],
|
|
2402
|
+
"America/Campo_Grande": [-20.45, -54.62],
|
|
2403
|
+
"America/Cancun": [21.08, -86.77],
|
|
2404
|
+
"America/Caracas": [10.5, -66.93],
|
|
2405
|
+
"America/Cayenne": [4.93, -52.33],
|
|
2406
|
+
"America/Cayman": [19.3, -81.38],
|
|
2407
|
+
"America/Chicago": [41.85, -87.65],
|
|
2408
|
+
"America/Chihuahua": [28.63, -106.08],
|
|
2409
|
+
"America/Ciudad_Juarez": [31.73, -106.48],
|
|
2410
|
+
"America/Costa_Rica": [9.93, -84.08],
|
|
2411
|
+
"America/Coyhaique": [-45.57, -72.07],
|
|
2412
|
+
"America/Creston": [49.1, -116.52],
|
|
2413
|
+
"America/Cuiaba": [-15.58, -56.08],
|
|
2414
|
+
"America/Curacao": [12.18, -69],
|
|
2415
|
+
"America/Danmarkshavn": [76.77, -18.67],
|
|
2416
|
+
"America/Dawson": [64.07, -139.42],
|
|
2417
|
+
"America/Dawson_Creek": [55.77, -120.23],
|
|
2418
|
+
"America/Denver": [39.74, -104.98],
|
|
2419
|
+
"America/Detroit": [42.33, -83.05],
|
|
2420
|
+
"America/Dominica": [15.3, -61.4],
|
|
2421
|
+
"America/Edmonton": [53.55, -113.47],
|
|
2422
|
+
"America/Eirunepe": [-6.67, -69.87],
|
|
2423
|
+
"America/El_Salvador": [13.7, -89.2],
|
|
2424
|
+
"America/Fort_Nelson": [58.8, -122.7],
|
|
2425
|
+
"America/Fortaleza": [-3.72, -38.5],
|
|
2426
|
+
"America/Glace_Bay": [46.2, -59.95],
|
|
2427
|
+
"America/Goose_Bay": [53.33, -60.42],
|
|
2428
|
+
"America/Grand_Turk": [21.47, -71.13],
|
|
2429
|
+
"America/Grenada": [12.05, -61.75],
|
|
2430
|
+
"America/Guadeloupe": [16.23, -61.53],
|
|
2431
|
+
"America/Guatemala": [14.63, -90.52],
|
|
2432
|
+
"America/Guayaquil": [-2.17, -79.83],
|
|
2433
|
+
"America/Guyana": [6.8, -58.17],
|
|
2434
|
+
"America/Halifax": [44.65, -63.6],
|
|
2435
|
+
"America/Havana": [23.13, -82.37],
|
|
2436
|
+
"America/Hermosillo": [29.07, -110.97],
|
|
2437
|
+
"America/Indiana/Indianapolis": [39.77, -86.16],
|
|
2438
|
+
"America/Indiana/Knox": [41.3, -86.62],
|
|
2439
|
+
"America/Indiana/Marengo": [38.38, -86.34],
|
|
2440
|
+
"America/Indiana/Petersburg": [38.49, -87.28],
|
|
2441
|
+
"America/Indiana/Tell_City": [37.95, -86.76],
|
|
2442
|
+
"America/Indiana/Vevay": [38.75, -85.07],
|
|
2443
|
+
"America/Indiana/Vincennes": [38.68, -87.53],
|
|
2444
|
+
"America/Indiana/Winamac": [41.05, -86.6],
|
|
2445
|
+
"America/Inuvik": [68.35, -133.72],
|
|
2446
|
+
"America/Iqaluit": [63.73, -68.47],
|
|
2447
|
+
"America/Jamaica": [17.97, -76.79],
|
|
2448
|
+
"America/Juneau": [58.3, -134.42],
|
|
2449
|
+
"America/Kentucky/Louisville": [38.25, -85.76],
|
|
2450
|
+
"America/Kentucky/Monticello": [36.83, -84.85],
|
|
2451
|
+
"America/Kralendijk": [12.15, -68.28],
|
|
2452
|
+
"America/La_Paz": [-16.5, -68.15],
|
|
2453
|
+
"America/Lima": [-12.05, -77.05],
|
|
2454
|
+
"America/Los_Angeles": [34.05, -118.24],
|
|
2455
|
+
"America/Lower_Princes": [18.05, -63.05],
|
|
2456
|
+
"America/Maceio": [-9.67, -35.72],
|
|
2457
|
+
"America/Managua": [12.15, -86.28],
|
|
2458
|
+
"America/Manaus": [-3.13, -60.02],
|
|
2459
|
+
"America/Marigot": [18.07, -63.08],
|
|
2460
|
+
"America/Martinique": [14.6, -61.08],
|
|
2461
|
+
"America/Matamoros": [25.83, -97.5],
|
|
2462
|
+
"America/Mazatlan": [23.22, -106.42],
|
|
2463
|
+
"America/Menominee": [45.11, -87.61],
|
|
2464
|
+
"America/Merida": [20.97, -89.62],
|
|
2465
|
+
"America/Metlakatla": [55.13, -131.58],
|
|
2466
|
+
"America/Mexico_City": [19.4, -99.15],
|
|
2467
|
+
"America/Miquelon": [47.05, -56.33],
|
|
2468
|
+
"America/Moncton": [46.1, -64.78],
|
|
2469
|
+
"America/Monterrey": [25.67, -100.32],
|
|
2470
|
+
"America/Montevideo": [-34.91, -56.21],
|
|
2471
|
+
"America/Montserrat": [16.72, -62.22],
|
|
2472
|
+
"America/Nassau": [25.08, -77.35],
|
|
2473
|
+
"America/New_York": [40.71, -74.01],
|
|
2474
|
+
"America/Nome": [64.5, -165.41],
|
|
2475
|
+
"America/Noronha": [-3.85, -32.42],
|
|
2476
|
+
"America/North_Dakota/Beulah": [47.26, -101.78],
|
|
2477
|
+
"America/North_Dakota/Center": [47.12, -101.3],
|
|
2478
|
+
"America/North_Dakota/New_Salem": [46.84, -101.41],
|
|
2479
|
+
"America/Nuuk": [64.18, -51.73],
|
|
2480
|
+
"America/Ojinaga": [29.57, -104.42],
|
|
2481
|
+
"America/Panama": [8.97, -79.53],
|
|
2482
|
+
"America/Paramaribo": [5.83, -55.17],
|
|
2483
|
+
"America/Phoenix": [33.45, -112.07],
|
|
2484
|
+
"America/Port-au-Prince": [18.53, -72.33],
|
|
2485
|
+
"America/Port_of_Spain": [10.65, -61.52],
|
|
2486
|
+
"America/Porto_Velho": [-8.77, -63.9],
|
|
2487
|
+
"America/Puerto_Rico": [18.47, -66.11],
|
|
2488
|
+
"America/Punta_Arenas": [-53.15, -70.92],
|
|
2489
|
+
"America/Rankin_Inlet": [62.82, -92.08],
|
|
2490
|
+
"America/Recife": [-8.05, -34.9],
|
|
2491
|
+
"America/Regina": [50.4, -104.65],
|
|
2492
|
+
"America/Resolute": [74.7, -94.83],
|
|
2493
|
+
"America/Rio_Branco": [-9.97, -67.8],
|
|
2494
|
+
"America/Santarem": [-2.43, -54.87],
|
|
2495
|
+
"America/Santiago": [-33.45, -70.67],
|
|
2496
|
+
"America/Santo_Domingo": [18.47, -69.9],
|
|
2497
|
+
"America/Sao_Paulo": [-23.53, -46.62],
|
|
2498
|
+
"America/Scoresbysund": [70.48, -21.97],
|
|
2499
|
+
"America/Sitka": [57.18, -135.3],
|
|
2500
|
+
"America/St_Barthelemy": [17.88, -62.85],
|
|
2501
|
+
"America/St_Johns": [47.57, -52.72],
|
|
2502
|
+
"America/St_Kitts": [17.3, -62.72],
|
|
2503
|
+
"America/St_Lucia": [14.02, -61],
|
|
2504
|
+
"America/St_Thomas": [18.35, -64.93],
|
|
2505
|
+
"America/St_Vincent": [13.15, -61.23],
|
|
2506
|
+
"America/Swift_Current": [50.28, -107.83],
|
|
2507
|
+
"America/Tegucigalpa": [14.1, -87.22],
|
|
2508
|
+
"America/Thule": [76.57, -68.78],
|
|
2509
|
+
"America/Tijuana": [32.53, -117.02],
|
|
2510
|
+
"America/Toronto": [43.65, -79.38],
|
|
2511
|
+
"America/Tortola": [18.45, -64.62],
|
|
2512
|
+
"America/Vancouver": [49.27, -123.12],
|
|
2513
|
+
"America/Whitehorse": [60.72, -135.05],
|
|
2514
|
+
"America/Winnipeg": [49.88, -97.15],
|
|
2515
|
+
"America/Yakutat": [59.55, -139.73],
|
|
2516
|
+
"Antarctica/Casey": [-66.28, 110.52],
|
|
2517
|
+
"Antarctica/Davis": [-68.58, 77.97],
|
|
2518
|
+
"Antarctica/DumontDUrville": [-66.67, 140.02],
|
|
2519
|
+
"Antarctica/Macquarie": [-54.5, 158.95],
|
|
2520
|
+
"Antarctica/Mawson": [-67.6, 62.88],
|
|
2521
|
+
"Antarctica/McMurdo": [-77.83, 166.6],
|
|
2522
|
+
"Antarctica/Palmer": [-64.8, -64.1],
|
|
2523
|
+
"Antarctica/Rothera": [-67.57, -68.13],
|
|
2524
|
+
"Antarctica/Syowa": [-69.01, 39.59],
|
|
2525
|
+
"Antarctica/Troll": [-72.01, 2.53],
|
|
2526
|
+
"Antarctica/Vostok": [-78.4, 106.9],
|
|
2527
|
+
"Arctic/Longyearbyen": [78, 16],
|
|
2528
|
+
"Asia/Aden": [12.75, 45.2],
|
|
2529
|
+
"Asia/Almaty": [43.25, 76.95],
|
|
2530
|
+
"Asia/Amman": [31.95, 35.93],
|
|
2531
|
+
"Asia/Anadyr": [64.75, 177.48],
|
|
2532
|
+
"Asia/Aqtau": [44.52, 50.27],
|
|
2533
|
+
"Asia/Aqtobe": [50.28, 57.17],
|
|
2534
|
+
"Asia/Ashgabat": [37.95, 58.38],
|
|
2535
|
+
"Asia/Atyrau": [47.12, 51.93],
|
|
2536
|
+
"Asia/Baghdad": [33.35, 44.42],
|
|
2537
|
+
"Asia/Bahrain": [26.38, 50.58],
|
|
2538
|
+
"Asia/Baku": [40.38, 49.85],
|
|
2539
|
+
"Asia/Bangkok": [13.75, 100.52],
|
|
2540
|
+
"Asia/Barnaul": [53.37, 83.75],
|
|
2541
|
+
"Asia/Beirut": [33.88, 35.5],
|
|
2542
|
+
"Asia/Bishkek": [42.9, 74.6],
|
|
2543
|
+
"Asia/Brunei": [4.93, 114.92],
|
|
2544
|
+
"Asia/Chita": [52.05, 113.47],
|
|
2545
|
+
"Asia/Colombo": [6.93, 79.85],
|
|
2546
|
+
"Asia/Damascus": [33.5, 36.3],
|
|
2547
|
+
"Asia/Dhaka": [23.72, 90.42],
|
|
2548
|
+
"Asia/Dili": [-8.55, 125.58],
|
|
2549
|
+
"Asia/Dubai": [25.3, 55.3],
|
|
2550
|
+
"Asia/Dushanbe": [38.58, 68.8],
|
|
2551
|
+
"Asia/Famagusta": [35.12, 33.95],
|
|
2552
|
+
"Asia/Gaza": [31.5, 34.47],
|
|
2553
|
+
"Asia/Hebron": [31.53, 35.09],
|
|
2554
|
+
"Asia/Ho_Chi_Minh": [10.75, 106.67],
|
|
2555
|
+
"Asia/Hong_Kong": [22.28, 114.15],
|
|
2556
|
+
"Asia/Hovd": [48.02, 91.65],
|
|
2557
|
+
"Asia/Irkutsk": [52.27, 104.33],
|
|
2558
|
+
"Asia/Jakarta": [-6.17, 106.8],
|
|
2559
|
+
"Asia/Jayapura": [-2.53, 140.7],
|
|
2560
|
+
"Asia/Jerusalem": [31.78, 35.22],
|
|
2561
|
+
"Asia/Kabul": [34.52, 69.2],
|
|
2562
|
+
"Asia/Kamchatka": [53.02, 158.65],
|
|
2563
|
+
"Asia/Karachi": [24.87, 67.05],
|
|
2564
|
+
"Asia/Kathmandu": [27.72, 85.32],
|
|
2565
|
+
"Asia/Khandyga": [62.66, 135.55],
|
|
2566
|
+
"Asia/Kolkata": [22.53, 88.37],
|
|
2567
|
+
"Asia/Krasnoyarsk": [56.02, 92.83],
|
|
2568
|
+
"Asia/Kuala_Lumpur": [3.17, 101.7],
|
|
2569
|
+
"Asia/Kuching": [1.55, 110.33],
|
|
2570
|
+
"Asia/Kuwait": [29.33, 47.98],
|
|
2571
|
+
"Asia/Macau": [22.2, 113.54],
|
|
2572
|
+
"Asia/Magadan": [59.57, 150.8],
|
|
2573
|
+
"Asia/Makassar": [-5.12, 119.4],
|
|
2574
|
+
"Asia/Manila": [14.59, 120.97],
|
|
2575
|
+
"Asia/Muscat": [23.6, 58.58],
|
|
2576
|
+
"Asia/Nicosia": [35.17, 33.37],
|
|
2577
|
+
"Asia/Novokuznetsk": [53.75, 87.12],
|
|
2578
|
+
"Asia/Novosibirsk": [55.03, 82.92],
|
|
2579
|
+
"Asia/Omsk": [55, 73.4],
|
|
2580
|
+
"Asia/Oral": [51.22, 51.35],
|
|
2581
|
+
"Asia/Phnom_Penh": [11.55, 104.92],
|
|
2582
|
+
"Asia/Pontianak": [-0.03, 109.33],
|
|
2583
|
+
"Asia/Pyongyang": [39.02, 125.75],
|
|
2584
|
+
"Asia/Qatar": [25.28, 51.53],
|
|
2585
|
+
"Asia/Qostanay": [53.2, 63.62],
|
|
2586
|
+
"Asia/Qyzylorda": [44.8, 65.47],
|
|
2587
|
+
"Asia/Riyadh": [24.63, 46.72],
|
|
2588
|
+
"Asia/Sakhalin": [46.97, 142.7],
|
|
2589
|
+
"Asia/Samarkand": [39.67, 66.8],
|
|
2590
|
+
"Asia/Seoul": [37.55, 126.97],
|
|
2591
|
+
"Asia/Shanghai": [31.23, 121.47],
|
|
2592
|
+
"Asia/Singapore": [1.28, 103.85],
|
|
2593
|
+
"Asia/Srednekolymsk": [67.47, 153.72],
|
|
2594
|
+
"Asia/Taipei": [25.05, 121.5],
|
|
2595
|
+
"Asia/Tashkent": [41.33, 69.3],
|
|
2596
|
+
"Asia/Tbilisi": [41.72, 44.82],
|
|
2597
|
+
"Asia/Tehran": [35.67, 51.43],
|
|
2598
|
+
"Asia/Thimphu": [27.47, 89.65],
|
|
2599
|
+
"Asia/Tokyo": [35.65, 139.74],
|
|
2600
|
+
"Asia/Tomsk": [56.5, 84.97],
|
|
2601
|
+
"Asia/Ulaanbaatar": [47.92, 106.88],
|
|
2602
|
+
"Asia/Urumqi": [43.8, 87.58],
|
|
2603
|
+
"Asia/Ust-Nera": [64.56, 143.23],
|
|
2604
|
+
"Asia/Vientiane": [17.97, 102.6],
|
|
2605
|
+
"Asia/Vladivostok": [43.17, 131.93],
|
|
2606
|
+
"Asia/Yakutsk": [62, 129.67],
|
|
2607
|
+
"Asia/Yangon": [16.78, 96.17],
|
|
2608
|
+
"Asia/Yekaterinburg": [56.85, 60.6],
|
|
2609
|
+
"Asia/Yerevan": [40.18, 44.5],
|
|
2610
|
+
"Atlantic/Azores": [37.73, -25.67],
|
|
2611
|
+
"Atlantic/Bermuda": [32.28, -64.77],
|
|
2612
|
+
"Atlantic/Canary": [28.1, -15.4],
|
|
2613
|
+
"Atlantic/Cape_Verde": [14.92, -23.52],
|
|
2614
|
+
"Atlantic/Faroe": [62.02, -6.77],
|
|
2615
|
+
"Atlantic/Madeira": [32.63, -16.9],
|
|
2616
|
+
"Atlantic/Reykjavik": [64.15, -21.85],
|
|
2617
|
+
"Atlantic/South_Georgia": [-54.27, -36.53],
|
|
2618
|
+
"Atlantic/St_Helena": [-15.92, -5.7],
|
|
2619
|
+
"Atlantic/Stanley": [-51.7, -57.85],
|
|
2620
|
+
"Australia/Adelaide": [-34.92, 138.58],
|
|
2621
|
+
"Australia/Brisbane": [-27.47, 153.03],
|
|
2622
|
+
"Australia/Broken_Hill": [-31.95, 141.45],
|
|
2623
|
+
"Australia/Darwin": [-12.47, 130.83],
|
|
2624
|
+
"Australia/Eucla": [-31.72, 128.87],
|
|
2625
|
+
"Australia/Hobart": [-42.88, 147.32],
|
|
2626
|
+
"Australia/Lindeman": [-20.27, 149],
|
|
2627
|
+
"Australia/Lord_Howe": [-31.55, 159.08],
|
|
2628
|
+
"Australia/Melbourne": [-37.82, 144.97],
|
|
2629
|
+
"Australia/Perth": [-31.95, 115.85],
|
|
2630
|
+
"Australia/Sydney": [-33.87, 151.22],
|
|
2631
|
+
"Europe/Amsterdam": [52.37, 4.9],
|
|
2632
|
+
"Europe/Andorra": [42.5, 1.52],
|
|
2633
|
+
"Europe/Astrakhan": [46.35, 48.05],
|
|
2634
|
+
"Europe/Athens": [37.97, 23.72],
|
|
2635
|
+
"Europe/Belgrade": [44.83, 20.5],
|
|
2636
|
+
"Europe/Berlin": [52.5, 13.37],
|
|
2637
|
+
"Europe/Bratislava": [48.15, 17.12],
|
|
2638
|
+
"Europe/Brussels": [50.83, 4.33],
|
|
2639
|
+
"Europe/Bucharest": [44.43, 26.1],
|
|
2640
|
+
"Europe/Budapest": [47.5, 19.08],
|
|
2641
|
+
"Europe/Busingen": [47.7, 8.68],
|
|
2642
|
+
"Europe/Chisinau": [47, 28.83],
|
|
2643
|
+
"Europe/Copenhagen": [55.67, 12.58],
|
|
2644
|
+
"Europe/Dublin": [53.33, -6.25],
|
|
2645
|
+
"Europe/Gibraltar": [36.13, -5.35],
|
|
2646
|
+
"Europe/Guernsey": [49.45, -2.54],
|
|
2647
|
+
"Europe/Helsinki": [60.17, 24.97],
|
|
2648
|
+
"Europe/Isle_of_Man": [54.15, -4.47],
|
|
2649
|
+
"Europe/Istanbul": [41.02, 28.97],
|
|
2650
|
+
"Europe/Jersey": [49.18, -2.11],
|
|
2651
|
+
"Europe/Kaliningrad": [54.72, 20.5],
|
|
2652
|
+
"Europe/Kirov": [58.6, 49.65],
|
|
2653
|
+
"Europe/Kyiv": [50.43, 30.52],
|
|
2654
|
+
"Europe/Lisbon": [38.72, -9.13],
|
|
2655
|
+
"Europe/Ljubljana": [46.05, 14.52],
|
|
2656
|
+
"Europe/London": [51.51, -0.13],
|
|
2657
|
+
"Europe/Luxembourg": [49.6, 6.15],
|
|
2658
|
+
"Europe/Madrid": [40.4, -3.68],
|
|
2659
|
+
"Europe/Malta": [35.9, 14.52],
|
|
2660
|
+
"Europe/Mariehamn": [60.1, 19.95],
|
|
2661
|
+
"Europe/Minsk": [53.9, 27.57],
|
|
2662
|
+
"Europe/Monaco": [43.7, 7.38],
|
|
2663
|
+
"Europe/Moscow": [55.76, 37.62],
|
|
2664
|
+
"Europe/Oslo": [59.92, 10.75],
|
|
2665
|
+
"Europe/Paris": [48.87, 2.33],
|
|
2666
|
+
"Europe/Podgorica": [42.43, 19.27],
|
|
2667
|
+
"Europe/Prague": [50.08, 14.43],
|
|
2668
|
+
"Europe/Riga": [56.95, 24.1],
|
|
2669
|
+
"Europe/Rome": [41.9, 12.48],
|
|
2670
|
+
"Europe/Samara": [53.2, 50.15],
|
|
2671
|
+
"Europe/San_Marino": [43.92, 12.47],
|
|
2672
|
+
"Europe/Sarajevo": [43.87, 18.42],
|
|
2673
|
+
"Europe/Saratov": [51.57, 46.03],
|
|
2674
|
+
"Europe/Simferopol": [44.95, 34.1],
|
|
2675
|
+
"Europe/Skopje": [41.98, 21.43],
|
|
2676
|
+
"Europe/Sofia": [42.68, 23.32],
|
|
2677
|
+
"Europe/Stockholm": [59.33, 18.05],
|
|
2678
|
+
"Europe/Tallinn": [59.42, 24.75],
|
|
2679
|
+
"Europe/Tirane": [41.33, 19.83],
|
|
2680
|
+
"Europe/Ulyanovsk": [54.33, 48.4],
|
|
2681
|
+
"Europe/Vaduz": [47.15, 9.52],
|
|
2682
|
+
"Europe/Vatican": [41.9, 12.45],
|
|
2683
|
+
"Europe/Vienna": [48.22, 16.33],
|
|
2684
|
+
"Europe/Vilnius": [54.68, 25.32],
|
|
2685
|
+
"Europe/Volgograd": [48.73, 44.42],
|
|
2686
|
+
"Europe/Warsaw": [52.25, 21],
|
|
2687
|
+
"Europe/Zagreb": [45.8, 15.97],
|
|
2688
|
+
"Europe/Zurich": [47.38, 8.53],
|
|
2689
|
+
"Indian/Antananarivo": [-18.92, 47.52],
|
|
2690
|
+
"Indian/Chagos": [-7.33, 72.42],
|
|
2691
|
+
"Indian/Christmas": [-10.42, 105.72],
|
|
2692
|
+
"Indian/Cocos": [-12.17, 96.92],
|
|
2693
|
+
"Indian/Comoro": [-11.68, 43.27],
|
|
2694
|
+
"Indian/Kerguelen": [-49.35, 70.22],
|
|
2695
|
+
"Indian/Mahe": [-4.67, 55.47],
|
|
2696
|
+
"Indian/Maldives": [4.17, 73.5],
|
|
2697
|
+
"Indian/Mauritius": [-20.17, 57.5],
|
|
2698
|
+
"Indian/Mayotte": [-12.78, 45.23],
|
|
2699
|
+
"Indian/Reunion": [-20.87, 55.47],
|
|
2700
|
+
"Pacific/Apia": [-13.83, -171.73],
|
|
2701
|
+
"Pacific/Auckland": [-36.87, 174.77],
|
|
2702
|
+
"Pacific/Bougainville": [-6.22, 155.57],
|
|
2703
|
+
"Pacific/Chatham": [-43.95, -176.55],
|
|
2704
|
+
"Pacific/Chuuk": [7.42, 151.78],
|
|
2705
|
+
"Pacific/Easter": [-27.15, -109.43],
|
|
2706
|
+
"Pacific/Efate": [-17.67, 168.42],
|
|
2707
|
+
"Pacific/Fakaofo": [-9.37, -171.23],
|
|
2708
|
+
"Pacific/Fiji": [-18.13, 178.42],
|
|
2709
|
+
"Pacific/Funafuti": [-8.52, 179.22],
|
|
2710
|
+
"Pacific/Galapagos": [-0.9, -89.6],
|
|
2711
|
+
"Pacific/Gambier": [-23.13, -134.95],
|
|
2712
|
+
"Pacific/Guadalcanal": [-9.53, 160.2],
|
|
2713
|
+
"Pacific/Guam": [13.47, 144.75],
|
|
2714
|
+
"Pacific/Honolulu": [21.31, -157.86],
|
|
2715
|
+
"Pacific/Kanton": [-2.78, -171.72],
|
|
2716
|
+
"Pacific/Kiritimati": [1.87, -157.33],
|
|
2717
|
+
"Pacific/Kosrae": [5.32, 162.98],
|
|
2718
|
+
"Pacific/Kwajalein": [9.08, 167.33],
|
|
2719
|
+
"Pacific/Majuro": [7.15, 171.2],
|
|
2720
|
+
"Pacific/Marquesas": [-9, -139.5],
|
|
2721
|
+
"Pacific/Midway": [28.22, -177.37],
|
|
2722
|
+
"Pacific/Nauru": [-0.52, 166.92],
|
|
2723
|
+
"Pacific/Niue": [-19.02, -169.92],
|
|
2724
|
+
"Pacific/Norfolk": [-29.05, 167.97],
|
|
2725
|
+
"Pacific/Noumea": [-22.27, 166.45],
|
|
2726
|
+
"Pacific/Pago_Pago": [-14.27, -170.7],
|
|
2727
|
+
"Pacific/Palau": [7.33, 134.48],
|
|
2728
|
+
"Pacific/Pitcairn": [-25.07, -130.08],
|
|
2729
|
+
"Pacific/Pohnpei": [6.97, 158.22],
|
|
2730
|
+
"Pacific/Port_Moresby": [-9.5, 147.17],
|
|
2731
|
+
"Pacific/Rarotonga": [-21.23, -159.77],
|
|
2732
|
+
"Pacific/Saipan": [15.2, 145.75],
|
|
2733
|
+
"Pacific/Tahiti": [-17.53, -149.57],
|
|
2734
|
+
"Pacific/Tarawa": [1.42, 173],
|
|
2735
|
+
"Pacific/Tongatapu": [-21.13, -175.2],
|
|
2736
|
+
"Pacific/Wake": [19.28, 166.62],
|
|
2737
|
+
"Pacific/Wallis": [-13.3, -176.17]
|
|
2738
|
+
};
|
|
2739
|
+
var ALIASES = {
|
|
2740
|
+
"Africa/Asmera": "Africa/Asmara",
|
|
2741
|
+
"America/Buenos_Aires": "America/Argentina/Buenos_Aires",
|
|
2742
|
+
"America/Catamarca": "America/Argentina/Catamarca",
|
|
2743
|
+
"America/Coral_Harbour": "America/Atikokan",
|
|
2744
|
+
"America/Cordoba": "America/Argentina/Cordoba",
|
|
2745
|
+
"America/Godthab": "America/Nuuk",
|
|
2746
|
+
"America/Indianapolis": "America/Indiana/Indianapolis",
|
|
2747
|
+
"America/Jujuy": "America/Argentina/Jujuy",
|
|
2748
|
+
"America/Louisville": "America/Kentucky/Louisville",
|
|
2749
|
+
"America/Mendoza": "America/Argentina/Mendoza",
|
|
2750
|
+
"Asia/Calcutta": "Asia/Kolkata",
|
|
2751
|
+
"Asia/Katmandu": "Asia/Kathmandu",
|
|
2752
|
+
"Asia/Rangoon": "Asia/Yangon",
|
|
2753
|
+
"Asia/Saigon": "Asia/Ho_Chi_Minh",
|
|
2754
|
+
"Atlantic/Faeroe": "Atlantic/Faroe",
|
|
2755
|
+
"Europe/Kiev": "Europe/Kyiv",
|
|
2756
|
+
"Pacific/Enderbury": "Pacific/Kanton",
|
|
2757
|
+
"Pacific/Ponape": "Pacific/Pohnpei",
|
|
2758
|
+
"Pacific/Truk": "Pacific/Chuuk"
|
|
2759
|
+
};
|
|
2760
|
+
function placeForZone(zone) {
|
|
2761
|
+
const coords = COORDS[zone] ?? COORDS[ALIASES[zone]];
|
|
2762
|
+
if (!coords) return null;
|
|
2763
|
+
return { zone, lat: coords[0], lon: coords[1], label: zoneLabel(zone) };
|
|
2764
|
+
}
|
|
2765
|
+
var PLACE_OPTIONS = Object.keys(COORDS).map((zone) => ({ value: zone, label: zoneLabel(zone) })).sort((a, b) => a.label.localeCompare(b.label));
|
|
2766
|
+
|
|
2767
|
+
// src/lib/store.ts
|
|
2768
|
+
var import_react2 = __toESM(require_react(), 1);
|
|
2769
|
+
|
|
2770
|
+
// src/lib/notify.ts
|
|
2771
|
+
var import_react = __toESM(require_react(), 1);
|
|
2772
|
+
|
|
2773
|
+
// src/lib/mutate.ts
|
|
2774
|
+
function applyUpdate(events, id, patch) {
|
|
2775
|
+
return events.map((e) => e.id === id ? { ...e, ...patch } : e);
|
|
2776
|
+
}
|
|
2777
|
+
function applyToggleDone(events, id, at) {
|
|
2778
|
+
return events.map((e) => e.id === id ? { ...e, doneKeys: toggleDoneKey(e.doneKeys, at) } : e);
|
|
2779
|
+
}
|
|
2780
|
+
function applyRemove(events, id) {
|
|
2781
|
+
return events.filter((e) => e.id !== id && e.seriesId !== id);
|
|
2782
|
+
}
|
|
2783
|
+
function applySplit(events, id, at, tail, mintId) {
|
|
2784
|
+
const original = events.find((e) => e.id === id);
|
|
2785
|
+
if (!original) return { events, created: null };
|
|
2786
|
+
const split = splitRule(original, original.start, at);
|
|
2787
|
+
const created = { ...tail, ...split.tail, seriesId: "", seriesKey: "", id: mintId() };
|
|
2788
|
+
const seed = new Date(original.start);
|
|
2789
|
+
const detached = (e) => e.seriesId === id && (keyToInstant(e.seriesKey, seed) ?? -Infinity) >= at;
|
|
2790
|
+
return {
|
|
2791
|
+
events: [
|
|
2792
|
+
...events.map(
|
|
2793
|
+
(e) => e.id === id ? { ...e, ...split.head } : detached(e) ? { ...e, seriesId: created.id } : e
|
|
2794
|
+
),
|
|
2795
|
+
created
|
|
2796
|
+
],
|
|
2797
|
+
created
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
function applyTruncate(events, id, at) {
|
|
2801
|
+
const original = events.find((e) => e.id === id);
|
|
2802
|
+
if (!original) return events;
|
|
2803
|
+
const split = splitRule(original, original.start, at);
|
|
2804
|
+
const seed = new Date(original.start);
|
|
2805
|
+
return events.filter((e) => !(e.seriesId === id && (keyToInstant(e.seriesKey, seed) ?? -Infinity) >= at)).map((e) => e.id === id ? { ...e, ...split.head } : e);
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
// src/lib/quickadd.ts
|
|
2809
|
+
var WEEKDAYS_LOWER = WEEKDAYS.map((w) => w.toLowerCase());
|
|
2810
|
+
var MONTHS_LOWER = MONTHS.map((m) => m.toLowerCase());
|
|
2811
|
+
var DEFAULT_DURATION = 60;
|
|
2812
|
+
var DATE_WORDS = 3;
|
|
2813
|
+
var REPEAT_WORDS = 3;
|
|
2814
|
+
var OFFSET_WORDS = 3;
|
|
2815
|
+
var FILLER = /* @__PURE__ */ new Set(["at", "on", "from", "for", "@"]);
|
|
2816
|
+
function parseOffset(raw) {
|
|
2817
|
+
const query = raw.trim().toLowerCase();
|
|
2818
|
+
if (query === "now") return 0;
|
|
2819
|
+
const relative = /^in\s+(.+)$/.exec(query);
|
|
2820
|
+
if (!relative) return null;
|
|
2821
|
+
const minutes = parseDurationToken(relative[1].replace(/^an?\s+/, "1 "));
|
|
2822
|
+
return minutes !== null && minutes > 0 ? minutes : null;
|
|
2823
|
+
}
|
|
2824
|
+
function parseDate(raw, reference) {
|
|
2825
|
+
const query = raw.trim().toLowerCase();
|
|
2826
|
+
if (!query) return null;
|
|
2827
|
+
const base = startOfDay(reference);
|
|
2828
|
+
if (query === "today") return base;
|
|
2829
|
+
if (query === "tomorrow") return addDays(base, 1);
|
|
2830
|
+
if (query === "yesterday") return addDays(base, -1);
|
|
2831
|
+
const weekdayIndex = WEEKDAYS_LOWER.findIndex((w) => w === query || w.slice(0, 3) === query);
|
|
2832
|
+
if (weekdayIndex !== -1) {
|
|
2833
|
+
const delta = (weekdayIndex - base.getDay() + 7) % 7 || 7;
|
|
2834
|
+
return addDays(base, delta);
|
|
2835
|
+
}
|
|
2836
|
+
const iso = /^(\d{4})-(\d{2})-(\d{2})$/.exec(query);
|
|
2837
|
+
if (iso) {
|
|
2838
|
+
const year = Number(iso[1]);
|
|
2839
|
+
const month = Number(iso[2]) - 1;
|
|
2840
|
+
const dom = Number(iso[3]);
|
|
2841
|
+
if (month >= 0 && month < 12 && dom >= 1 && dom <= daysInMonth(year, month)) {
|
|
2842
|
+
return new Date(year, month, dom);
|
|
2843
|
+
}
|
|
2844
|
+
return null;
|
|
2845
|
+
}
|
|
2846
|
+
const resolveMonthDay = (monthStr, domStr) => {
|
|
2847
|
+
const monthIndex = MONTHS_LOWER.findIndex((m) => m === monthStr || m.slice(0, 3) === monthStr);
|
|
2848
|
+
const dom = Number(domStr);
|
|
2849
|
+
if (monthIndex === -1 || dom < 1 || dom > daysInMonth(reference.getFullYear(), monthIndex))
|
|
2850
|
+
return null;
|
|
2851
|
+
const year = reference.getFullYear();
|
|
2852
|
+
const candidate = new Date(year, monthIndex, dom);
|
|
2853
|
+
return candidate.getTime() < base.getTime() ? new Date(year + 1, monthIndex, dom) : candidate;
|
|
2854
|
+
};
|
|
2855
|
+
const monthDay = /^([a-z]+)\s+(\d{1,2})$/.exec(query);
|
|
2856
|
+
if (monthDay) {
|
|
2857
|
+
const resolved = resolveMonthDay(monthDay[1], monthDay[2]);
|
|
2858
|
+
if (resolved) return resolved;
|
|
2859
|
+
}
|
|
2860
|
+
const dayMonth = /^(\d{1,2})\s+([a-z]+)$/.exec(query);
|
|
2861
|
+
if (dayMonth) {
|
|
2862
|
+
const resolved = resolveMonthDay(dayMonth[2], dayMonth[1]);
|
|
2863
|
+
if (resolved) return resolved;
|
|
2864
|
+
}
|
|
2865
|
+
const relative = /^([+-])(\d+)$/.exec(query);
|
|
2866
|
+
if (relative) {
|
|
2867
|
+
return addDays(base, Number(relative[2]) * (relative[1] === "-" ? -1 : 1));
|
|
2868
|
+
}
|
|
2869
|
+
const inDays = /^in\s+(\d+)\s+days?$/.exec(query);
|
|
2870
|
+
if (inDays) return addDays(base, Number(inDays[1]));
|
|
2871
|
+
const bareDay = /^(\d{1,2})$/.exec(query);
|
|
2872
|
+
if (bareDay) {
|
|
2873
|
+
const dom = Number(bareDay[1]);
|
|
2874
|
+
if (dom < 1 || dom > 31) return null;
|
|
2875
|
+
let year = reference.getFullYear();
|
|
2876
|
+
let month = reference.getMonth();
|
|
2877
|
+
if (dom > daysInMonth(year, month)) return null;
|
|
2878
|
+
let candidate = new Date(year, month, dom);
|
|
2879
|
+
if (candidate.getTime() < base.getTime()) {
|
|
2880
|
+
month += 1;
|
|
2881
|
+
if (month > 11) {
|
|
2882
|
+
month = 0;
|
|
2883
|
+
year += 1;
|
|
2884
|
+
}
|
|
2885
|
+
if (dom > daysInMonth(year, month)) return null;
|
|
2886
|
+
candidate = new Date(year, month, dom);
|
|
2887
|
+
}
|
|
2888
|
+
return candidate;
|
|
2889
|
+
}
|
|
2890
|
+
return null;
|
|
2891
|
+
}
|
|
2892
|
+
function parseDurationToken(raw) {
|
|
2893
|
+
const query = raw.trim().toLowerCase();
|
|
2894
|
+
if (!query) return null;
|
|
2895
|
+
const hours = /^(\d{1,2})\s*(?:h|hr|hrs|hour|hours)\s*(\d{1,2})?\s*(?:m|min|mins)?$/.exec(query);
|
|
2896
|
+
if (hours) return Number(hours[1]) * 60 + Number(hours[2] ?? 0);
|
|
2897
|
+
const minutes = /^(\d{1,3})\s*(?:m|min|mins|minute|minutes)$/.exec(query);
|
|
2898
|
+
if (minutes) return Number(minutes[1]);
|
|
2899
|
+
return null;
|
|
2900
|
+
}
|
|
2901
|
+
function parseSpan(token) {
|
|
2902
|
+
const range = /^(.+?)[-–—](.+)$/.exec(token);
|
|
2903
|
+
if (range) {
|
|
2904
|
+
const from = parseClock(range[1]);
|
|
2905
|
+
const to = parseClock(range[2]);
|
|
2906
|
+
if (from === null || to === null) return null;
|
|
2907
|
+
return { startMin: from, endMin: to > from ? to : to + MINUTES_PER_DAY };
|
|
2908
|
+
}
|
|
2909
|
+
const at = parseClock(token);
|
|
2910
|
+
return at === null ? null : { startMin: at, endMin: null };
|
|
2911
|
+
}
|
|
2912
|
+
var WEEKDAY_NUMBERS = new Map(
|
|
2913
|
+
WEEKDAYS_LOWER.flatMap((name, index) => [
|
|
2914
|
+
[name, index],
|
|
2915
|
+
[name.slice(0, 3), index]
|
|
2916
|
+
])
|
|
2917
|
+
);
|
|
2918
|
+
var FREQUENCY_WORDS = {
|
|
2919
|
+
hourly: { repeat: "hourly", interval: 1, byDay: [] },
|
|
2920
|
+
daily: { repeat: "daily", interval: 1, byDay: [] },
|
|
2921
|
+
weekly: { repeat: "weekly", interval: 1, byDay: [] },
|
|
2922
|
+
biweekly: { repeat: "weekly", interval: 2, byDay: [] },
|
|
2923
|
+
fortnightly: { repeat: "weekly", interval: 2, byDay: [] },
|
|
2924
|
+
monthly: { repeat: "monthly", interval: 1, byDay: [] },
|
|
2925
|
+
yearly: { repeat: "yearly", interval: 1, byDay: [] },
|
|
2926
|
+
annually: { repeat: "yearly", interval: 1, byDay: [] }
|
|
2927
|
+
};
|
|
2928
|
+
var UNIT_WORDS = {
|
|
2929
|
+
hour: "hourly",
|
|
2930
|
+
hours: "hourly",
|
|
2931
|
+
day: "daily",
|
|
2932
|
+
days: "daily",
|
|
2933
|
+
week: "weekly",
|
|
2934
|
+
weeks: "weekly",
|
|
2935
|
+
month: "monthly",
|
|
2936
|
+
months: "monthly",
|
|
2937
|
+
year: "yearly",
|
|
2938
|
+
years: "yearly"
|
|
2939
|
+
};
|
|
2940
|
+
var WORKING_WEEK = [1, 2, 3, 4, 5];
|
|
2941
|
+
var WEEKEND = [6, 0];
|
|
2942
|
+
var weekdays = (days, interval = 1) => ({
|
|
2943
|
+
repeat: "weekly",
|
|
2944
|
+
interval,
|
|
2945
|
+
byDay: days.map((day) => ({ day, nth: 0 }))
|
|
2946
|
+
});
|
|
2947
|
+
function parseRepeat(raw) {
|
|
2948
|
+
const query = raw.trim().toLowerCase();
|
|
2949
|
+
if (!query) return null;
|
|
2950
|
+
const named = FREQUENCY_WORDS[query];
|
|
2951
|
+
if (named) return named;
|
|
2952
|
+
if (query === "weekdays") return weekdays(WORKING_WEEK);
|
|
2953
|
+
if (query === "weekends") return weekdays(WEEKEND);
|
|
2954
|
+
const every = /^every\s+(.+)$/.exec(query);
|
|
2955
|
+
if (!every) return null;
|
|
2956
|
+
const rest = every[1];
|
|
2957
|
+
if (rest === "weekday" || rest === "weekdays") return weekdays(WORKING_WEEK);
|
|
2958
|
+
if (rest === "weekend" || rest === "weekends") return weekdays(WEEKEND);
|
|
2959
|
+
const repeated = /^(other|\d{1,2})\s+(.+)$/.exec(rest);
|
|
2960
|
+
if (repeated) {
|
|
2961
|
+
const interval = repeated[1] === "other" ? 2 : Number(repeated[1]);
|
|
2962
|
+
if (interval < 1) return null;
|
|
2963
|
+
const unit2 = UNIT_WORDS[repeated[2]];
|
|
2964
|
+
if (unit2) return { repeat: unit2, interval, byDay: [] };
|
|
2965
|
+
const day = WEEKDAY_NUMBERS.get(repeated[2]);
|
|
2966
|
+
return day === void 0 ? null : weekdays([day], interval);
|
|
2967
|
+
}
|
|
2968
|
+
const unit = UNIT_WORDS[rest];
|
|
2969
|
+
if (unit) return { repeat: unit, interval: 1, byDay: [] };
|
|
2970
|
+
const days = [];
|
|
2971
|
+
for (const name of rest.split(/[,/]/)) {
|
|
2972
|
+
const day = WEEKDAY_NUMBERS.get(name.trim());
|
|
2973
|
+
if (day === void 0) return null;
|
|
2974
|
+
days.push(day);
|
|
2975
|
+
}
|
|
2976
|
+
return days.length > 0 ? weekdays(days) : null;
|
|
2977
|
+
}
|
|
2978
|
+
function defaultStart(on, durationMin, now = /* @__PURE__ */ new Date()) {
|
|
2979
|
+
const start = isSameDay(on, now) ? snap(minutesOfDay(now) + 7.5, 15) : 9 * 60;
|
|
2980
|
+
return Math.min(start, Math.max(0, MINUTES_PER_DAY - durationMin));
|
|
2981
|
+
}
|
|
2982
|
+
function parseQuickAdd(input, reference, now = /* @__PURE__ */ new Date(), fallbackDuration = DEFAULT_DURATION) {
|
|
2983
|
+
const tokens = input.trim().replace(/(\d)\s*[-–—]\s*(\d)/g, "$1-$2").split(/\s+/).filter(Boolean);
|
|
2984
|
+
if (tokens.length === 0) return null;
|
|
2985
|
+
let end = tokens.length;
|
|
2986
|
+
const phrase = (from, to) => tokens.slice(from, to).join(" ");
|
|
2987
|
+
function claim(words, read) {
|
|
2988
|
+
for (let n = Math.min(words, end); n >= 1; n--) {
|
|
2989
|
+
const value = read(phrase(end - n, end));
|
|
2990
|
+
if (value !== null) {
|
|
2991
|
+
end -= n;
|
|
2992
|
+
while (end > 0 && FILLER.has(tokens[end - 1].toLowerCase())) end -= 1;
|
|
2993
|
+
return value;
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
return null;
|
|
2997
|
+
}
|
|
2998
|
+
let day = startOfDay(reference);
|
|
2999
|
+
let dated = false;
|
|
3000
|
+
let span = null;
|
|
3001
|
+
let duration = null;
|
|
3002
|
+
let repeat = null;
|
|
3003
|
+
let offset = null;
|
|
3004
|
+
while (end > 0) {
|
|
3005
|
+
if (offset === null && span === null) {
|
|
3006
|
+
const value = claim(OFFSET_WORDS, parseOffset);
|
|
3007
|
+
if (value !== null) {
|
|
3008
|
+
offset = value;
|
|
3009
|
+
continue;
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
if (repeat === null) {
|
|
3013
|
+
const value = claim(REPEAT_WORDS, parseRepeat);
|
|
3014
|
+
if (value !== null) {
|
|
3015
|
+
repeat = value;
|
|
3016
|
+
continue;
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
if (duration === null) {
|
|
3020
|
+
const value = claim(2, (p) => {
|
|
3021
|
+
const minutes = parseDurationToken(p);
|
|
3022
|
+
return minutes !== null && minutes > 0 ? minutes : null;
|
|
3023
|
+
});
|
|
3024
|
+
if (value !== null) {
|
|
3025
|
+
duration = value;
|
|
3026
|
+
continue;
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
if (span === null) {
|
|
3030
|
+
const value = claim(2, parseSpan);
|
|
3031
|
+
if (value !== null) {
|
|
3032
|
+
span = value;
|
|
3033
|
+
continue;
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
if (!dated) {
|
|
3037
|
+
const value = claim(DATE_WORDS, (p) => parseDate(p, reference));
|
|
3038
|
+
if (value !== null) {
|
|
3039
|
+
day = startOfDay(value);
|
|
3040
|
+
dated = true;
|
|
3041
|
+
continue;
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
break;
|
|
3045
|
+
}
|
|
3046
|
+
if (duration === null) {
|
|
3047
|
+
for (let words = Math.min(2, end - 1); words >= 1; words--) {
|
|
3048
|
+
const minutes = parseDurationToken(phrase(0, words));
|
|
3049
|
+
if (minutes !== null && minutes > 0) {
|
|
3050
|
+
duration = minutes;
|
|
3051
|
+
tokens.splice(0, words);
|
|
3052
|
+
end -= words;
|
|
3053
|
+
break;
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
const title = phrase(0, end).trim();
|
|
3058
|
+
if (!title) return null;
|
|
3059
|
+
if (!dated && span === null && duration === null && repeat === null && offset === null) {
|
|
3060
|
+
return null;
|
|
3061
|
+
}
|
|
3062
|
+
const fromNow = offset === null ? null : snap(minutesOfDay(now) + offset, 15);
|
|
3063
|
+
if (fromNow !== null && !dated) {
|
|
3064
|
+
day = addDays(startOfDay(now), Math.floor(fromNow / MINUTES_PER_DAY));
|
|
3065
|
+
}
|
|
3066
|
+
if (!dated && repeat && repeat.byDay.length > 0) {
|
|
3067
|
+
const wanted = new Set(repeat.byDay.map((w) => w.day));
|
|
3068
|
+
for (let step = 0; step < 7 && !wanted.has(day.getDay()); step++) day = addDays(day, 1);
|
|
3069
|
+
}
|
|
3070
|
+
let startMin;
|
|
3071
|
+
let endMin;
|
|
3072
|
+
if (span) {
|
|
3073
|
+
startMin = span.startMin;
|
|
3074
|
+
endMin = span.endMin ?? startMin + (duration ?? fallbackDuration);
|
|
3075
|
+
} else if (fromNow !== null) {
|
|
3076
|
+
startMin = fromNow % MINUTES_PER_DAY;
|
|
3077
|
+
endMin = startMin + (duration ?? fallbackDuration);
|
|
3078
|
+
} else if (duration !== null) {
|
|
3079
|
+
startMin = defaultStart(day, duration, now);
|
|
3080
|
+
endMin = startMin + duration;
|
|
3081
|
+
} else {
|
|
3082
|
+
startMin = 0;
|
|
3083
|
+
endMin = MINUTES_PER_DAY;
|
|
3084
|
+
}
|
|
3085
|
+
return {
|
|
3086
|
+
title,
|
|
3087
|
+
day,
|
|
3088
|
+
startMin,
|
|
3089
|
+
endMin,
|
|
3090
|
+
allDay: endMin - startMin >= MINUTES_PER_DAY,
|
|
3091
|
+
repeat
|
|
3092
|
+
};
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
// src/lib/store.ts
|
|
3096
|
+
var DEFAULT_SETTINGS = {
|
|
3097
|
+
// The 0–23 face. It is the one that matches the instrument: a revolution is
|
|
3098
|
+
// a day here, not half of one, so an hour has exactly one place on it and
|
|
3099
|
+
// `13` is where `13` is. AM/PM is a preference, and it is one click away.
|
|
3100
|
+
clock: "24h",
|
|
3101
|
+
secondZone: "",
|
|
3102
|
+
showZoneName: true,
|
|
3103
|
+
showZoneOffset: true,
|
|
3104
|
+
// Off: the captions describe a ring that is already there, but a band is
|
|
3105
|
+
// another ring, and chrome does not get to appear unasked.
|
|
3106
|
+
zoneDaylight: false,
|
|
3107
|
+
zoneMoonlight: false,
|
|
3108
|
+
place: null,
|
|
3109
|
+
// Both on, because the band *is* the track: with one half off the ring
|
|
3110
|
+
// simply stops at dusk (or at dawn), and an incomplete ring reads as a
|
|
3111
|
+
// rendering fault rather than as a choice.
|
|
3112
|
+
daylight: true,
|
|
3113
|
+
moonlight: true,
|
|
3114
|
+
// Always, which is the one default here that a switch does not have to earn.
|
|
3115
|
+
// The glyph budget means a label only appears where the sweep can hold one,
|
|
3116
|
+
// so it cannot crowd the face — and the dial's own argument is legibility,
|
|
3117
|
+
// which an unlabelled ring of four coloured ticks loses. Hiding the fix
|
|
3118
|
+
// behind an off switch would concede the point.
|
|
3119
|
+
arcLabels: "always",
|
|
3120
|
+
// Off, and it is the one mark decision with a default worth arguing about:
|
|
3121
|
+
// see `markOf`. A calendar's glyph repeated down every arc of the day is
|
|
3122
|
+
// decoration, so it is asked for rather than assumed.
|
|
3123
|
+
inheritMarks: false,
|
|
3124
|
+
// Off. The ring is already the picture of a day's load; the line under the
|
|
3125
|
+
// card is the same fact in words, and words earn their place by being asked
|
|
3126
|
+
// for.
|
|
3127
|
+
totals: false,
|
|
3128
|
+
totalsAt: "center",
|
|
3129
|
+
// On: the drag is the one gesture in the app with a granularity you cannot
|
|
3130
|
+
// see mid-move, and a detent you have to look at is not a detent. Both are
|
|
3131
|
+
// heard only as the result of a deliberate drag, and never on load.
|
|
3132
|
+
tickSound: true,
|
|
3133
|
+
tickHaptics: true,
|
|
3134
|
+
// Unknown, and it stays unknown until asked for: a birth date is the one
|
|
3135
|
+
// piece of the user in this app, so nothing invents one.
|
|
3136
|
+
birth: "",
|
|
3137
|
+
// Eighty. High enough to be a life rather than a countdown, round enough to
|
|
3138
|
+
// read as the estimate it is.
|
|
3139
|
+
lifeExpectancy: 80,
|
|
3140
|
+
// On. Three or four marks on thirty thousand dots is a constellation rather
|
|
3141
|
+
// than furniture, and the alternative is a field with no landmarks in it at
|
|
3142
|
+
// all: the decade rules name the rows, and nothing names a day.
|
|
3143
|
+
milestones: true,
|
|
3144
|
+
// On. The grid's one weakness was always that a dot is four pixels, and a
|
|
3145
|
+
// magnifier that only appears while you are pointing costs the picture
|
|
3146
|
+
// nothing when you are not.
|
|
3147
|
+
lifeLoupe: true,
|
|
3148
|
+
// The default is an instruction rather than an observation, which is what
|
|
3149
|
+
// saves it from being a fortune cookie: a ring returns you to the same
|
|
3150
|
+
// angle, and this is the one thing to do about that.
|
|
3151
|
+
yearQuote: "don't end up in the same place",
|
|
3152
|
+
// Never, until asked for. Everything else in this file appears because the
|
|
3153
|
+
// user did something; a mode that arrives on a timer is the one thing that
|
|
3154
|
+
// could appear because they did not.
|
|
3155
|
+
ambientAfter: 0,
|
|
3156
|
+
// On, and it is the reason the mode is worth having: a face that does not
|
|
3157
|
+
// know it is night is a lamp. Costs nothing where no place is set — there is
|
|
3158
|
+
// no curve to follow, so it does not dim.
|
|
3159
|
+
ambientDim: true,
|
|
3160
|
+
// Both on, for the same reason the working face's pair is: the band *is*
|
|
3161
|
+
// the track, and with one half off the ring stops at dusk. Here it is also
|
|
3162
|
+
// the whole picture — ambient is the mode that is nothing but the rim.
|
|
3163
|
+
ambientDaylight: true,
|
|
3164
|
+
ambientMoonlight: true,
|
|
3165
|
+
// Off. A second place's numerals are a working reference — you look them up
|
|
3166
|
+
// — and this face is not looked *up*.
|
|
3167
|
+
ambientZoneRing: false,
|
|
3168
|
+
// Off, for the reason `arcLabels` defaults to `always`: the argument is
|
|
3169
|
+
// legibility, and at this distance the label is the illegible part.
|
|
3170
|
+
ambientArcLabels: "off",
|
|
3171
|
+
// Off. It is the only thing in this app that draws something the calendar
|
|
3172
|
+
// does not contain, so it has to be asked for by name.
|
|
3173
|
+
moon: false,
|
|
3174
|
+
// Off, and it is the one default here that is a promise rather than a
|
|
3175
|
+
// preference: nothing in circal has ever interrupted anybody, and the first
|
|
3176
|
+
// thing that can must be switched on deliberately.
|
|
3177
|
+
remind: false,
|
|
3178
|
+
// Ten minutes. Long enough to walk somewhere, short enough that the thing
|
|
3179
|
+
// is still the next thing when it lands.
|
|
3180
|
+
remindLead: 10,
|
|
3181
|
+
// An hour, which is what `DEFAULT_DURATION` has always been and what a
|
|
3182
|
+
// VEVENT with no DTEND is read as. This setting exists to be *changed* — a
|
|
3183
|
+
// day of thirty-minute meetings is a real working life — not to have a new
|
|
3184
|
+
// opinion by default.
|
|
3185
|
+
defaultDuration: DEFAULT_DURATION,
|
|
3186
|
+
// The first calendar that is not a subscription, whichever that turns out
|
|
3187
|
+
// to be. Naming one here would be naming a seed calendar, and the seed is
|
|
3188
|
+
// not something a returning user still has.
|
|
3189
|
+
defaultCalendar: "",
|
|
3190
|
+
// Monday, which is what the app has always drawn and what most of the
|
|
3191
|
+
// world already expects — ISO 8601's own answer, and the one that needed
|
|
3192
|
+
// no setting until this one arrived to make it a choice rather than an
|
|
3193
|
+
// assumption.
|
|
3194
|
+
weekStart: 1,
|
|
3195
|
+
// `DEFAULT_WAKING`'s own pair: this setting exists to replace the constant,
|
|
3196
|
+
// not to disagree with it on day one.
|
|
3197
|
+
wakingFrom: DEFAULT_WAKING.from,
|
|
3198
|
+
wakingTo: DEFAULT_WAKING.to
|
|
3199
|
+
};
|
|
3200
|
+
var DEFAULT_CALENDARS = [
|
|
3201
|
+
{ id: "personal", name: "Personal", color: PALETTE[0], icon: "house", visible: true },
|
|
3202
|
+
{ id: "work", name: "Work", color: PALETTE[3], icon: "briefcase", visible: true },
|
|
3203
|
+
{ id: "family", name: "Family", color: PALETTE[5], icon: "users", visible: true }
|
|
3204
|
+
];
|
|
3205
|
+
function newId() {
|
|
3206
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3207
|
+
}
|
|
3208
|
+
function defaultCalendarId(calendars, preferred) {
|
|
3209
|
+
const named = preferred ? calendars.find((c) => c.id === preferred && !c.feed) : void 0;
|
|
3210
|
+
return named?.id ?? calendars.find((c) => !c.feed)?.id ?? DEFAULT_CALENDARS[0].id;
|
|
3211
|
+
}
|
|
3212
|
+
function draftEvent(day, startMin, endMin, calendarId, repeat = "never") {
|
|
3213
|
+
const base = startOfDay(day).getTime();
|
|
3214
|
+
const span = endMin - startMin;
|
|
3215
|
+
const allDay = span >= MINUTES_PER_DAY;
|
|
3216
|
+
const days = Math.round(span / MINUTES_PER_DAY);
|
|
3217
|
+
return {
|
|
3218
|
+
title: "",
|
|
3219
|
+
calendarId,
|
|
3220
|
+
start: allDay ? base : base + startMin * 6e4,
|
|
3221
|
+
end: allDay ? addDays(startOfDay(day), days).getTime() : base + endMin * 6e4,
|
|
3222
|
+
allDay,
|
|
3223
|
+
tzid: "",
|
|
3224
|
+
location: "",
|
|
3225
|
+
url: "",
|
|
3226
|
+
notes: "",
|
|
3227
|
+
...NEVER,
|
|
3228
|
+
repeat,
|
|
3229
|
+
seriesId: "",
|
|
3230
|
+
seriesKey: "",
|
|
3231
|
+
color: "",
|
|
3232
|
+
icon: "",
|
|
3233
|
+
task: false,
|
|
3234
|
+
doneKeys: []
|
|
3235
|
+
};
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
// src/lib/backup.ts
|
|
3239
|
+
function readSettings(value) {
|
|
3240
|
+
if (!isRecord(value)) return DEFAULT_SETTINGS;
|
|
3241
|
+
const place = value.place;
|
|
3242
|
+
return {
|
|
3243
|
+
// A face the backup did not name is the current default, not the old one:
|
|
3244
|
+
// there are exactly two spellings and both are written out by every
|
|
3245
|
+
// version that has ever had the setting.
|
|
3246
|
+
clock: value.clock === "12h" || value.clock === "24h" ? value.clock : DEFAULT_SETTINGS.clock,
|
|
3247
|
+
secondZone: typeof value.secondZone === "string" ? value.secondZone : "",
|
|
3248
|
+
// The dial switches read absent-as-on: a backup written before they
|
|
3249
|
+
// existed came from a dial that was showing all of them.
|
|
3250
|
+
showZoneName: value.showZoneName !== false,
|
|
3251
|
+
showZoneOffset: value.showZoneOffset !== false,
|
|
3252
|
+
// Except the second zone's two halves, off unless asked for — absent
|
|
3253
|
+
// means they were never switched on.
|
|
3254
|
+
zoneDaylight: value.zoneDaylight === true,
|
|
3255
|
+
zoneMoonlight: value.zoneMoonlight === true,
|
|
3256
|
+
// Re-derived from the zone rather than trusted: a backup carries a name we
|
|
3257
|
+
// can look up, and the table is the authority on where that place is.
|
|
3258
|
+
place: isRecord(place) && typeof place.zone === "string" ? placeForZone(place.zone) : null,
|
|
3259
|
+
daylight: value.daylight !== false,
|
|
3260
|
+
// `night` is what the dark half was called before it was split off.
|
|
3261
|
+
moonlight: value.moonlight !== void 0 ? value.moonlight !== false : value.night !== false,
|
|
3262
|
+
// Absent means the backup predates the labels, and the label default is
|
|
3263
|
+
// on: a dial whose arcs are anonymous is not what anybody chose. `sane()`
|
|
3264
|
+
// is what pins the value to a mode the panel can show.
|
|
3265
|
+
arcLabels: typeof value.arcLabels === "string" ? value.arcLabels : DEFAULT_SETTINGS.arcLabels,
|
|
3266
|
+
// Off unless the backup says so — the same reading its default takes, and
|
|
3267
|
+
// an inherited mark arriving unasked would remark every arc on the face.
|
|
3268
|
+
inheritMarks: value.inheritMarks === true,
|
|
3269
|
+
// Off unless the backup says otherwise: absent means it was written
|
|
3270
|
+
// before the line existed, and it is not a line to arrive unasked.
|
|
3271
|
+
totals: value.totals === true,
|
|
3272
|
+
totalsAt: typeof value.totalsAt === "string" ? value.totalsAt : DEFAULT_SETTINGS.totalsAt,
|
|
3273
|
+
// Same reading as the dial switches: a backup from before the detents
|
|
3274
|
+
// existed came from a build where every step was silent by omission, not
|
|
3275
|
+
// by choice, and the default is on.
|
|
3276
|
+
tickSound: value.tickSound !== false,
|
|
3277
|
+
tickHaptics: value.tickHaptics !== false,
|
|
3278
|
+
// The life span. Anything unreadable resets to "never asked", which shows
|
|
3279
|
+
// the prompt again rather than drawing a grid around a date we guessed.
|
|
3280
|
+
birth: typeof value.birth === "string" ? value.birth : "",
|
|
3281
|
+
lifeExpectancy: typeof value.lifeExpectancy === "number" && Number.isFinite(value.lifeExpectancy) ? value.lifeExpectancy : DEFAULT_SETTINGS.lifeExpectancy,
|
|
3282
|
+
// Marks on the grid, read absent-as-on like the dial switches: a backup
|
|
3283
|
+
// written before they existed came from a build where every life was
|
|
3284
|
+
// unmarked by omission rather than by choice.
|
|
3285
|
+
milestones: value.milestones !== false,
|
|
3286
|
+
// Same reading for the loupe, and the same reason: a file written before
|
|
3287
|
+
// it existed is not a file that turned it off.
|
|
3288
|
+
lifeLoupe: value.lifeLoupe !== false,
|
|
3289
|
+
// A line the user wrote, so an absent one is the default rather than
|
|
3290
|
+
// silence — a blank centre is a thing to choose, not to inherit from a
|
|
3291
|
+
// file. `sane()` trims it and pins the length.
|
|
3292
|
+
yearQuote: typeof value.yearQuote === "string" ? value.yearQuote : DEFAULT_SETTINGS.yearQuote,
|
|
3293
|
+
// Ambient. A backup from before the mode came from a build with no mode,
|
|
3294
|
+
// so every one of these reads as the default rather than as a choice —
|
|
3295
|
+
// and the one that could act on its own, the timer, defaults to never.
|
|
3296
|
+
// `sane()` pins the delay and the label mode to values the panel can show.
|
|
3297
|
+
ambientAfter: typeof value.ambientAfter === "number" ? value.ambientAfter : DEFAULT_SETTINGS.ambientAfter,
|
|
3298
|
+
ambientDim: value.ambientDim !== false,
|
|
3299
|
+
ambientDaylight: value.ambientDaylight !== false,
|
|
3300
|
+
ambientMoonlight: value.ambientMoonlight !== false,
|
|
3301
|
+
ambientZoneRing: value.ambientZoneRing === true,
|
|
3302
|
+
ambientArcLabels: typeof value.ambientArcLabels === "string" ? value.ambientArcLabels : DEFAULT_SETTINGS.ambientArcLabels,
|
|
3303
|
+
// Both of these appear because the user asked for them, so an absent one
|
|
3304
|
+
// is off rather than on — the opposite reading from the dial switches
|
|
3305
|
+
// above, and for the opposite reason: one is a thing the face already
|
|
3306
|
+
// drew, these are things that were never there. `sane()` pins the lead.
|
|
3307
|
+
// A file written while the moon had two registers carries the string
|
|
3308
|
+
// `moonPhase` instead, and either register is a moon that was asked for.
|
|
3309
|
+
moon: value.moon === true || typeof value.moonPhase === "string" && value.moonPhase !== "off",
|
|
3310
|
+
remind: value.remind === true,
|
|
3311
|
+
remindLead: typeof value.remindLead === "number" ? value.remindLead : DEFAULT_SETTINGS.remindLead,
|
|
3312
|
+
// What a new event starts as. `sane()` clamps the length; the calendar is
|
|
3313
|
+
// an id and is deliberately not checked against the backup's own
|
|
3314
|
+
// calendars — `defaultCalendarId` resolves it at the moment it is used,
|
|
3315
|
+
// and a restore that dropped it would silently reset a preference over a
|
|
3316
|
+
// calendar the user is about to re-import.
|
|
3317
|
+
defaultDuration: typeof value.defaultDuration === "number" ? value.defaultDuration : DEFAULT_SETTINGS.defaultDuration,
|
|
3318
|
+
defaultCalendar: typeof value.defaultCalendar === "string" ? value.defaultCalendar : "",
|
|
3319
|
+
// Types only: `sane()` is what pins an illegal weekday or an inverted
|
|
3320
|
+
// waking window to the defaults, same as every other bounded field here.
|
|
3321
|
+
weekStart: value.weekStart === 0 || value.weekStart === 1 || value.weekStart === 6 ? value.weekStart : DEFAULT_SETTINGS.weekStart,
|
|
3322
|
+
wakingFrom: typeof value.wakingFrom === "number" ? value.wakingFrom : DEFAULT_SETTINGS.wakingFrom,
|
|
3323
|
+
wakingTo: typeof value.wakingTo === "number" ? value.wakingTo : DEFAULT_SETTINGS.wakingTo
|
|
3324
|
+
};
|
|
3325
|
+
}
|
|
3326
|
+
var INSTANT_KEY = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/;
|
|
3327
|
+
function readDoneKeys(value) {
|
|
3328
|
+
return Array.isArray(value) ? [...new Set(value.filter((k) => typeof k === "string" && INSTANT_KEY.test(k)))] : [];
|
|
3329
|
+
}
|
|
3330
|
+
function derivedId(value, start, end) {
|
|
3331
|
+
const text = (key) => typeof value[key] === "string" ? value[key] : "";
|
|
3332
|
+
return `${start}-${fnv1a([start, end, text("title"), text("location"), text("seriesKey")].join("|"))}`;
|
|
3333
|
+
}
|
|
3334
|
+
function readEvent(value) {
|
|
3335
|
+
if (!isRecord(value)) return null;
|
|
3336
|
+
const { id, title, calendarId, start, end } = value;
|
|
3337
|
+
if (typeof start !== "number" || typeof end !== "number") return null;
|
|
3338
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return null;
|
|
3339
|
+
const task = value.task === true;
|
|
3340
|
+
if (task ? end < start : end <= start) return null;
|
|
3341
|
+
return {
|
|
3342
|
+
// Derived from the record's own fields, never minted: `Math.random()` gave
|
|
3343
|
+
// a different id every read, so the same backup imported twice was two
|
|
3344
|
+
// copies of every event with no way left to tell that they were one. The
|
|
3345
|
+
// same fix, and the same hash, `ics.ts` already uses for a VEVENT that
|
|
3346
|
+
// arrives with no UID.
|
|
3347
|
+
id: typeof id === "string" && id ? id : derivedId(value, start, end),
|
|
3348
|
+
title: typeof title === "string" ? title : "Untitled",
|
|
3349
|
+
calendarId: typeof calendarId === "string" ? calendarId : "",
|
|
3350
|
+
start,
|
|
3351
|
+
end,
|
|
3352
|
+
allDay: value.allDay === true,
|
|
3353
|
+
// An unknown zone must not come back as a zone, and a dropped one
|
|
3354
|
+
// degrades to floating — which is what the app did with every event
|
|
3355
|
+
// before this field existed, so the failure mode is not a new one.
|
|
3356
|
+
tzid: typeof value.tzid === "string" && isZone(value.tzid) ? value.tzid : "",
|
|
3357
|
+
location: typeof value.location === "string" ? value.location : "",
|
|
3358
|
+
url: typeof value.url === "string" ? value.url : "",
|
|
3359
|
+
notes: typeof value.notes === "string" ? value.notes : "",
|
|
3360
|
+
// What a legal rule is lives in `recur.ts`: this file had its own copy of
|
|
3361
|
+
// every RFC bound and the store had a third, and the three did not agree.
|
|
3362
|
+
...readRecurrence(value),
|
|
3363
|
+
seriesId: typeof value.seriesId === "string" ? value.seriesId : "",
|
|
3364
|
+
seriesKey: typeof value.seriesKey === "string" ? value.seriesKey : "",
|
|
3365
|
+
color: typeof value.color === "string" ? value.color : "",
|
|
3366
|
+
icon: typeof value.icon === "string" ? value.icon.slice(0, ICON_MAX) : "",
|
|
3367
|
+
task,
|
|
3368
|
+
// The same instant-key check EXDATE gets, and the same reason: a key this
|
|
3369
|
+
// does not spell matches no occurrence, so it is a row of dead weight that
|
|
3370
|
+
// survives every future export.
|
|
3371
|
+
doneKeys: readDoneKeys(value.doneKeys)
|
|
3372
|
+
};
|
|
3373
|
+
}
|
|
3374
|
+
function readCalendar(value) {
|
|
3375
|
+
if (!isRecord(value)) return null;
|
|
3376
|
+
const { id, name, color, icon } = value;
|
|
3377
|
+
if (typeof id !== "string" || !id) return null;
|
|
3378
|
+
const feedUrl = isRecord(value.feed) && typeof value.feed.url === "string" ? value.feed.url : "";
|
|
3379
|
+
return {
|
|
3380
|
+
id,
|
|
3381
|
+
name: typeof name === "string" && name ? name : id,
|
|
3382
|
+
color: typeof color === "string" && color ? color : "#8ecbff",
|
|
3383
|
+
// Length is the whole check. An icon name this build does not carry
|
|
3384
|
+
// renders as the dot rather than as nothing, so an unknown one is worth
|
|
3385
|
+
// keeping — a backup restored into an older build and back again should
|
|
3386
|
+
// not lose the mark — but an unbounded string here is a paste.
|
|
3387
|
+
icon: typeof icon === "string" ? icon.slice(0, ICON_MAX) : "",
|
|
3388
|
+
visible: value.visible !== false,
|
|
3389
|
+
...feedUrl ? { feed: { url: feedUrl, fetchedAt: 0, error: "" } } : {}
|
|
3390
|
+
};
|
|
3391
|
+
}
|
|
3392
|
+
function readBackup(parsed) {
|
|
3393
|
+
if (!isRecord(parsed) || parsed.app !== "circal") return { ok: false, reason: "not-circal" };
|
|
3394
|
+
const events = (Array.isArray(parsed.events) ? parsed.events : []).map(readEvent).filter((e) => e !== null);
|
|
3395
|
+
const calendars = (Array.isArray(parsed.calendars) ? parsed.calendars : []).map(readCalendar).filter((c) => c !== null);
|
|
3396
|
+
if (calendars.length === 0) return { ok: false, reason: "no-calendars" };
|
|
3397
|
+
const known = new Set(calendars.map((c) => c.id));
|
|
3398
|
+
const fallback = calendars[0].id;
|
|
3399
|
+
const seriesIds = new Set(events.map((e) => e.id));
|
|
3400
|
+
return {
|
|
3401
|
+
ok: true,
|
|
3402
|
+
events: events.map((e) => {
|
|
3403
|
+
const parented = known.has(e.calendarId) ? e : { ...e, calendarId: fallback };
|
|
3404
|
+
return e.seriesId && !seriesIds.has(e.seriesId) ? { ...parented, seriesId: "", seriesKey: "" } : parented;
|
|
3405
|
+
}),
|
|
3406
|
+
calendars,
|
|
3407
|
+
settings: readSettings(parsed.settings)
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3410
|
+
|
|
3411
|
+
// src/lib/mirror.ts
|
|
3412
|
+
var MIRROR_VERSION = 1;
|
|
3413
|
+
function settled(calendars) {
|
|
3414
|
+
return calendars.map(
|
|
3415
|
+
(c) => c.feed ? { ...c, feed: { ...c.feed, fetchedAt: 0, error: "" } } : c
|
|
3416
|
+
);
|
|
3417
|
+
}
|
|
3418
|
+
function serialiseMirror(s, rev, zone = localZone()) {
|
|
3419
|
+
const file = {
|
|
3420
|
+
app: "circal",
|
|
3421
|
+
version: MIRROR_VERSION,
|
|
3422
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3423
|
+
zone,
|
|
3424
|
+
rev,
|
|
3425
|
+
events: s.events,
|
|
3426
|
+
calendars: settled(s.calendars),
|
|
3427
|
+
settings: s.settings
|
|
3428
|
+
};
|
|
3429
|
+
return `${JSON.stringify(file, null, 2)}
|
|
3430
|
+
`;
|
|
3431
|
+
}
|
|
3432
|
+
function readMirrorFile(text) {
|
|
3433
|
+
let parsed;
|
|
3434
|
+
try {
|
|
3435
|
+
parsed = JSON.parse(text);
|
|
3436
|
+
} catch {
|
|
3437
|
+
return null;
|
|
3438
|
+
}
|
|
3439
|
+
const read = readBackup(parsed);
|
|
3440
|
+
if (!read.ok) return null;
|
|
3441
|
+
const envelope = parsed;
|
|
3442
|
+
return {
|
|
3443
|
+
events: read.events,
|
|
3444
|
+
calendars: read.calendars,
|
|
3445
|
+
settings: read.settings,
|
|
3446
|
+
rev: typeof envelope.rev === "number" && Number.isFinite(envelope.rev) ? envelope.rev : 0,
|
|
3447
|
+
// A plain `.json` backup has no zone. Absent means "written by something
|
|
3448
|
+
// that did not say", which the reader treats as agreeing with it — the
|
|
3449
|
+
// alternative is refusing to open every backup ever exported.
|
|
3450
|
+
zone: typeof envelope.zone === "string" ? envelope.zone : ""
|
|
3451
|
+
};
|
|
3452
|
+
}
|
|
3453
|
+
|
|
3454
|
+
// mcp/file.ts
|
|
3455
|
+
var RefusalError = class extends Error {
|
|
3456
|
+
};
|
|
3457
|
+
function resolveFilePath(argv2, env) {
|
|
3458
|
+
const flagIndex = argv2.indexOf("--file");
|
|
3459
|
+
const flagValue = flagIndex >= 0 ? argv2[flagIndex + 1] : void 0;
|
|
3460
|
+
const path2 = flagValue || env.CIRCAL_FILE;
|
|
3461
|
+
if (!path2) {
|
|
3462
|
+
throw new Error(
|
|
3463
|
+
"circal-mcp needs a mirror file to read and write. Set the CIRCAL_FILE environment variable or pass --file <path> on the command line. Neither was given."
|
|
3464
|
+
);
|
|
3465
|
+
}
|
|
3466
|
+
return path2;
|
|
3467
|
+
}
|
|
3468
|
+
async function readMirror(path2) {
|
|
3469
|
+
const text = await readFile(path2, "utf8");
|
|
3470
|
+
const contents = readMirrorFile(text);
|
|
3471
|
+
if (!contents) {
|
|
3472
|
+
throw new Error(`${path2} is not a circal mirror file (or a circal .json backup).`);
|
|
3473
|
+
}
|
|
3474
|
+
return contents;
|
|
3475
|
+
}
|
|
3476
|
+
async function atomicWrite(path2, body) {
|
|
3477
|
+
const tmp = join(dirname(path2), `.circal-mcp-${randomBytes(6).toString("hex")}.tmp`);
|
|
3478
|
+
await writeFile(tmp, body, "utf8");
|
|
3479
|
+
await rename(tmp, path2);
|
|
3480
|
+
}
|
|
3481
|
+
function zoneWarning(contents) {
|
|
3482
|
+
const here = localZone();
|
|
3483
|
+
if (contents.zone === "" || contents.zone === here) return null;
|
|
3484
|
+
return `Warning: this file was last written from ${contents.zone}, this server is running in ${here}. Day boundaries may disagree.`;
|
|
3485
|
+
}
|
|
3486
|
+
function assertZoneAllowsMutation(contents, env) {
|
|
3487
|
+
const here = localZone();
|
|
3488
|
+
if (contents.zone === "" || contents.zone === here) return;
|
|
3489
|
+
if (env.CIRCAL_ALLOW_ZONE_MISMATCH === "1") return;
|
|
3490
|
+
throw new RefusalError(
|
|
3491
|
+
`Refusing to write: the file's zone (${contents.zone}) does not match this server's (${here}) \u2014 a write here could land on the wrong day there. Set CIRCAL_ALLOW_ZONE_MISMATCH=1 to write anyway.`
|
|
3492
|
+
);
|
|
3493
|
+
}
|
|
3494
|
+
async function loadForRead(ctx2) {
|
|
3495
|
+
const contents = await readMirror(ctx2.path);
|
|
3496
|
+
return { contents, warning: zoneWarning(contents) };
|
|
3497
|
+
}
|
|
3498
|
+
async function casWriteOnce(ctx2, mutate) {
|
|
3499
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
3500
|
+
const before = await readMirror(ctx2.path);
|
|
3501
|
+
assertZoneAllowsMutation(before, ctx2.env);
|
|
3502
|
+
const { result, summary } = mutate(before);
|
|
3503
|
+
const after = await readMirror(ctx2.path);
|
|
3504
|
+
if (after.rev !== before.rev) {
|
|
3505
|
+
if (attempt === 1) {
|
|
3506
|
+
throw new Error(
|
|
3507
|
+
`The file moved under this write: read at rev ${before.rev}, still found rev ${after.rev} after retrying once. Something else is writing it \u2014 try again.`
|
|
3508
|
+
);
|
|
3509
|
+
}
|
|
3510
|
+
continue;
|
|
3511
|
+
}
|
|
3512
|
+
await atomicWrite(ctx2.path, serialiseMirror(result, after.rev + 1, after.zone || localZone()));
|
|
3513
|
+
return summary;
|
|
3514
|
+
}
|
|
3515
|
+
throw new Error("unreachable");
|
|
3516
|
+
}
|
|
3517
|
+
var writeQueue = Promise.resolve();
|
|
3518
|
+
function casWrite(ctx2, mutate) {
|
|
3519
|
+
const run = writeQueue.then(() => casWriteOnce(ctx2, mutate));
|
|
3520
|
+
writeQueue = run.then(
|
|
3521
|
+
() => void 0,
|
|
3522
|
+
() => void 0
|
|
3523
|
+
);
|
|
3524
|
+
return run;
|
|
3525
|
+
}
|
|
3526
|
+
|
|
3527
|
+
// mcp/find.ts
|
|
3528
|
+
import { readFile as readFile2, readdir, stat } from "node:fs/promises";
|
|
3529
|
+
import { homedir } from "node:os";
|
|
3530
|
+
import { join as join2 } from "node:path";
|
|
3531
|
+
var SKIP_DIR_NAMES = { node_modules: true, Library: true, ".git": true };
|
|
3532
|
+
var MAX_FILE_BYTES = 32 * 1024 * 1024;
|
|
3533
|
+
function roots(home) {
|
|
3534
|
+
return [
|
|
3535
|
+
home,
|
|
3536
|
+
join2(home, "Documents"),
|
|
3537
|
+
join2(home, "Desktop"),
|
|
3538
|
+
join2(home, "Downloads"),
|
|
3539
|
+
join2(home, "Library", "Mobile Documents", "com~apple~CloudDocs"),
|
|
3540
|
+
// iCloud Drive (Documents)
|
|
3541
|
+
join2(home, "Dropbox"),
|
|
3542
|
+
join2(home, "Sync")
|
|
3543
|
+
];
|
|
3544
|
+
}
|
|
3545
|
+
async function jsonFilesUnder(root) {
|
|
3546
|
+
const found = [];
|
|
3547
|
+
const top = await listDir(root);
|
|
3548
|
+
for (const entry of top) {
|
|
3549
|
+
if (entry.name.startsWith(".")) continue;
|
|
3550
|
+
const full = join2(root, entry.name);
|
|
3551
|
+
if (entry.isFile()) {
|
|
3552
|
+
if (entry.name.toLowerCase().endsWith(".json")) found.push(full);
|
|
3553
|
+
continue;
|
|
3554
|
+
}
|
|
3555
|
+
if (!entry.isDirectory() || SKIP_DIR_NAMES[entry.name]) continue;
|
|
3556
|
+
for (const sub of await listDir(full)) {
|
|
3557
|
+
if (sub.name.startsWith(".") || !sub.isFile()) continue;
|
|
3558
|
+
if (sub.name.toLowerCase().endsWith(".json")) found.push(join2(full, sub.name));
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
return found;
|
|
3562
|
+
}
|
|
3563
|
+
async function listDir(path2) {
|
|
3564
|
+
try {
|
|
3565
|
+
return await readdir(path2, { withFileTypes: true });
|
|
3566
|
+
} catch {
|
|
3567
|
+
return [];
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
async function asMirror(path2) {
|
|
3571
|
+
try {
|
|
3572
|
+
const info = await stat(path2);
|
|
3573
|
+
if (!info.isFile() || info.size > MAX_FILE_BYTES) return null;
|
|
3574
|
+
const text = await readFile2(path2, "utf8");
|
|
3575
|
+
const contents = readMirrorFile(text);
|
|
3576
|
+
return contents ? { path: path2, contents } : null;
|
|
3577
|
+
} catch {
|
|
3578
|
+
return null;
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
async function runFind(home = homedir()) {
|
|
3582
|
+
const searchRoots = roots(home);
|
|
3583
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
3584
|
+
for (const root of searchRoots) {
|
|
3585
|
+
for (const path2 of await jsonFilesUnder(root)) candidates.add(path2);
|
|
3586
|
+
}
|
|
3587
|
+
const hits = [];
|
|
3588
|
+
for (const path2 of [...candidates].sort()) {
|
|
3589
|
+
const hit = await asMirror(path2);
|
|
3590
|
+
if (hit) hits.push(hit);
|
|
3591
|
+
}
|
|
3592
|
+
if (hits.length === 0) {
|
|
3593
|
+
console.log("No circal mirror file found.\n");
|
|
3594
|
+
console.log("Searched these locations (their own files, and one level of subdirectories):");
|
|
3595
|
+
for (const root of searchRoots) console.log(` ${root}`);
|
|
3596
|
+
console.log(
|
|
3597
|
+
"\nTurn the mirror on from circal's Settings panel to create a file, then run this again."
|
|
3598
|
+
);
|
|
3599
|
+
return;
|
|
3600
|
+
}
|
|
3601
|
+
console.log(`Found ${hits.length} circal mirror file${hits.length === 1 ? "" : "s"}:
|
|
3602
|
+
`);
|
|
3603
|
+
for (const hit of hits) {
|
|
3604
|
+
const zone = hit.contents.zone || "not recorded";
|
|
3605
|
+
console.log(` ${hit.path}`);
|
|
3606
|
+
console.log(
|
|
3607
|
+
` events: ${hit.contents.events.length}, calendars: ${hit.contents.calendars.length}, zone: ${zone}, rev: ${hit.contents.rev}`
|
|
3608
|
+
);
|
|
3609
|
+
}
|
|
3610
|
+
const first = hits[0].path;
|
|
3611
|
+
const config = JSON.stringify(
|
|
3612
|
+
{ mcpServers: { circal: { command: "npx", args: ["-y", "circal-mcp", "--file", first] } } },
|
|
3613
|
+
null,
|
|
3614
|
+
2
|
|
3615
|
+
);
|
|
3616
|
+
console.log(
|
|
3617
|
+
"\nPaste this into your client's MCP config. Swap the path below if this picked up the wrong file.\n"
|
|
3618
|
+
);
|
|
3619
|
+
console.log("Claude Desktop (claude_desktop_config.json) / Cursor (.cursor/mcp.json):\n");
|
|
3620
|
+
console.log(config);
|
|
3621
|
+
console.log("\nClaude Code:\n");
|
|
3622
|
+
console.log(` claude mcp add circal -- npx -y circal-mcp --file ${first}`);
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
// mcp/tools.ts
|
|
3626
|
+
import * as z from "zod/v4";
|
|
3627
|
+
|
|
3628
|
+
// src/lib/describe.ts
|
|
3629
|
+
var POSITIONS = ["", "first", "second", "third", "fourth", "fifth"];
|
|
3630
|
+
function ordinal(n) {
|
|
3631
|
+
const tens = n % 100;
|
|
3632
|
+
const suffix = tens >= 11 && tens <= 13 ? "th" : ["th", "st", "nd", "rd"][n % 10] ?? "th";
|
|
3633
|
+
return `${n}${suffix}`;
|
|
3634
|
+
}
|
|
3635
|
+
function positionWord(n) {
|
|
3636
|
+
if (n > 0) return POSITIONS[n] ?? ordinal(n);
|
|
3637
|
+
return n === -1 ? "last" : `${POSITIONS[-n] ?? ordinal(-n)} from last`;
|
|
3638
|
+
}
|
|
3639
|
+
function monthDayWord(n) {
|
|
3640
|
+
return n > 0 ? ordinal(n) : `${ordinal(-n)} from last`;
|
|
3641
|
+
}
|
|
3642
|
+
function joinWords(items) {
|
|
3643
|
+
if (items.length < 2) return items[0] ?? "";
|
|
3644
|
+
return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
|
|
3645
|
+
}
|
|
3646
|
+
function describeRule(rule) {
|
|
3647
|
+
const every = rule.interval > 1 ? `${rule.interval} ` : "";
|
|
3648
|
+
const parts = [`Every ${every}${repeatUnit(rule.repeat, rule.interval)}`];
|
|
3649
|
+
if (rule.byMonth.length > 0) {
|
|
3650
|
+
parts.push(`in ${joinWords(rule.byMonth.map((m) => MONTHS[m - 1].slice(0, 3)))}`);
|
|
3651
|
+
}
|
|
3652
|
+
if (rule.byWeekNo.length > 0) {
|
|
3653
|
+
parts.push(
|
|
3654
|
+
`in week${rule.byWeekNo.length > 1 ? "s" : ""} ${joinWords(rule.byWeekNo.map(String))}`
|
|
3655
|
+
);
|
|
3656
|
+
}
|
|
3657
|
+
if (rule.byYearDay.length > 0) {
|
|
3658
|
+
parts.push(`on the ${joinWords(rule.byYearDay.map(monthDayWord))} day of the year`);
|
|
3659
|
+
}
|
|
3660
|
+
const days = rule.byDay.map(
|
|
3661
|
+
(w) => w.nth === 0 ? WEEKDAYS[w.day].slice(0, 3) : `${positionWord(w.nth)} ${WEEKDAYS[w.day].slice(0, 3)}`
|
|
3662
|
+
);
|
|
3663
|
+
const dates = rule.byMonthDay.map(monthDayWord);
|
|
3664
|
+
if (rule.bySetPos.length > 0 && days.length + dates.length > 0) {
|
|
3665
|
+
const pick = joinWords(rule.bySetPos.map(positionWord));
|
|
3666
|
+
parts.push(`on the ${pick} of ${joinWords([...dates, ...days])}`);
|
|
3667
|
+
} else {
|
|
3668
|
+
if (dates.length > 0) parts.push(`on the ${joinWords(dates)}`);
|
|
3669
|
+
if (days.length > 0) parts.push(`on ${joinWords(days)}`);
|
|
3670
|
+
if (rule.bySetPos.length > 0) {
|
|
3671
|
+
parts.push(`keeping the ${joinWords(rule.bySetPos.map(positionWord))}`);
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
if (rule.byHour.length > 0 || rule.byMinute.length > 0 || rule.bySecond.length > 0) {
|
|
3675
|
+
parts.push("at set times");
|
|
3676
|
+
}
|
|
3677
|
+
if (rule.until > 0) {
|
|
3678
|
+
const until = new Date(rule.until);
|
|
3679
|
+
parts.push(
|
|
3680
|
+
`until ${until.getDate()} ${MONTHS[until.getMonth()].slice(0, 3)} ${until.getFullYear()}`
|
|
3681
|
+
);
|
|
3682
|
+
}
|
|
3683
|
+
return parts.join(" ");
|
|
3684
|
+
}
|
|
3685
|
+
|
|
3686
|
+
// mcp/domain.ts
|
|
3687
|
+
var DATE_KEY_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
3688
|
+
function assertDateKey(value, field = "date") {
|
|
3689
|
+
if (!DATE_KEY_RE.test(value)) {
|
|
3690
|
+
throw new RefusalError(`"${field}" must be YYYY-MM-DD, local \u2014 got "${value}"`);
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
function parseDay(value, field = "date") {
|
|
3694
|
+
assertDateKey(value, field);
|
|
3695
|
+
return fromDayKey(value);
|
|
3696
|
+
}
|
|
3697
|
+
function parseTime(value, field) {
|
|
3698
|
+
const minutes = parseClock(value);
|
|
3699
|
+
if (minutes === null) {
|
|
3700
|
+
throw new RefusalError(
|
|
3701
|
+
`could not read "${field}": "${value}" is not a time \u2014 try "14:30" or "2:30pm"`
|
|
3702
|
+
);
|
|
3703
|
+
}
|
|
3704
|
+
return minutes;
|
|
3705
|
+
}
|
|
3706
|
+
function resolveInstant(event, occurrence) {
|
|
3707
|
+
if (!occurrence) return event.start;
|
|
3708
|
+
const at = keyToInstant(occurrence, new Date(event.start));
|
|
3709
|
+
if (at === null) {
|
|
3710
|
+
throw new RefusalError(`"${occurrence}" is not a date or instant this app understands`);
|
|
3711
|
+
}
|
|
3712
|
+
return at;
|
|
3713
|
+
}
|
|
3714
|
+
function requireScope(event, scope) {
|
|
3715
|
+
if (!repeats(event)) return "all";
|
|
3716
|
+
if (scope !== "one" && scope !== "future" && scope !== "all") {
|
|
3717
|
+
throw new RefusalError(
|
|
3718
|
+
`"${event.title || "Untitled"}" repeats \u2014 say which occurrences with "scope": "one" (just this occurrence), "future" (this one and everything after), or "all" (the whole series). "one" and "future" also need "occurrence" (a date or instant).`
|
|
3719
|
+
);
|
|
3720
|
+
}
|
|
3721
|
+
return scope;
|
|
3722
|
+
}
|
|
3723
|
+
function resolveCalendar(calendarId, calendars, settings) {
|
|
3724
|
+
if (calendarId) {
|
|
3725
|
+
const found2 = calendars.find((c) => c.id === calendarId);
|
|
3726
|
+
if (!found2) throw new RefusalError(`no calendar with id "${calendarId}"`);
|
|
3727
|
+
if (found2.feed) {
|
|
3728
|
+
throw new RefusalError(
|
|
3729
|
+
`"${found2.name}" is a subscription \u2014 its events are pulled from a feed, never written to`
|
|
3730
|
+
);
|
|
3731
|
+
}
|
|
3732
|
+
return found2;
|
|
3733
|
+
}
|
|
3734
|
+
const id = defaultCalendarId(calendars, settings.defaultCalendar);
|
|
3735
|
+
const found = calendars.find((c) => c.id === id);
|
|
3736
|
+
if (!found) throw new RefusalError("there is no calendar to put this on \u2014 create one first");
|
|
3737
|
+
return found;
|
|
3738
|
+
}
|
|
3739
|
+
function refuseIfFeed(event, calendars) {
|
|
3740
|
+
const calendar = calendars.find((c) => c.id === event.calendarId);
|
|
3741
|
+
if (calendar?.feed) {
|
|
3742
|
+
throw new RefusalError(
|
|
3743
|
+
`"${calendar.name}" is a subscription \u2014 "${event.title || "Untitled"}" is pulled from a feed and is read-only, the same as it is in the editor`
|
|
3744
|
+
);
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
function buildRecurrence(input) {
|
|
3748
|
+
if (!input) return { ...NEVER };
|
|
3749
|
+
const until = input.until ? addDays(fromDayKey(input.until), 1).getTime() - 1 : 0;
|
|
3750
|
+
return readRecurrence({ ...input, until, exdates: [], rdates: [] });
|
|
3751
|
+
}
|
|
3752
|
+
function resolveSpanForCreate(input, day, defaultDuration) {
|
|
3753
|
+
if (input.task === true) {
|
|
3754
|
+
if (input.start === void 0) throw new RefusalError('a task needs "start" (HH:MM)');
|
|
3755
|
+
const at = atMinutes(day, parseTime(input.start, "start")).getTime();
|
|
3756
|
+
return { start: at, end: at, allDay: false, task: true };
|
|
3757
|
+
}
|
|
3758
|
+
const noTimeGiven = input.start === void 0 && input.end === void 0 && input.duration === void 0;
|
|
3759
|
+
const allDay = input.allDay === true || input.allDay === void 0 && noTimeGiven;
|
|
3760
|
+
if (allDay) {
|
|
3761
|
+
const days = input.end ? Math.max(1, daysBetween(day, fromDayKey(input.end)) + 1) : 1;
|
|
3762
|
+
return {
|
|
3763
|
+
start: startOfDay(day).getTime(),
|
|
3764
|
+
end: addDays(startOfDay(day), days).getTime(),
|
|
3765
|
+
allDay: true,
|
|
3766
|
+
task: false
|
|
3767
|
+
};
|
|
3768
|
+
}
|
|
3769
|
+
if (input.start === void 0) throw new RefusalError('a timed event needs "start" (HH:MM)');
|
|
3770
|
+
const startMin = parseTime(input.start, "start");
|
|
3771
|
+
const endMin = input.end !== void 0 ? parseTime(input.end, "end") : startMin + (input.duration ?? defaultDuration);
|
|
3772
|
+
return {
|
|
3773
|
+
start: atMinutes(day, startMin).getTime(),
|
|
3774
|
+
end: atMinutes(day, endMin).getTime(),
|
|
3775
|
+
allDay: false,
|
|
3776
|
+
task: false
|
|
3777
|
+
};
|
|
3778
|
+
}
|
|
3779
|
+
function resolveSpanForPatch(input, base, anchorMs) {
|
|
3780
|
+
const day = input.date ? parseDay(input.date) : startOfDay(new Date(anchorMs));
|
|
3781
|
+
const task = input.task ?? base.task;
|
|
3782
|
+
if (task) {
|
|
3783
|
+
const startMin2 = input.start !== void 0 ? parseTime(input.start, "start") : minutesOfDay(new Date(anchorMs));
|
|
3784
|
+
const at = atMinutes(day, startMin2).getTime();
|
|
3785
|
+
return { start: at, end: at, allDay: false, task: true };
|
|
3786
|
+
}
|
|
3787
|
+
const allDay = input.allDay ?? base.allDay;
|
|
3788
|
+
if (allDay) {
|
|
3789
|
+
const baseDays = Math.max(
|
|
3790
|
+
1,
|
|
3791
|
+
daysBetween(startOfDay(new Date(base.start)), startOfDay(new Date(base.end)))
|
|
3792
|
+
);
|
|
3793
|
+
const days = input.end ? Math.max(1, daysBetween(day, fromDayKey(input.end)) + 1) : baseDays;
|
|
3794
|
+
return {
|
|
3795
|
+
start: startOfDay(day).getTime(),
|
|
3796
|
+
end: addDays(startOfDay(day), days).getTime(),
|
|
3797
|
+
allDay: true,
|
|
3798
|
+
task: false
|
|
3799
|
+
};
|
|
3800
|
+
}
|
|
3801
|
+
const baseDurationMin = Math.max(1, Math.round((base.end - base.start) / 6e4));
|
|
3802
|
+
const startMin = input.start !== void 0 ? parseTime(input.start, "start") : minutesOfDay(new Date(anchorMs));
|
|
3803
|
+
const endMin = input.end !== void 0 ? parseTime(input.end, "end") : startMin + (input.duration ?? baseDurationMin);
|
|
3804
|
+
return {
|
|
3805
|
+
start: atMinutes(day, startMin).getTime(),
|
|
3806
|
+
end: atMinutes(day, endMin).getTime(),
|
|
3807
|
+
allDay: false,
|
|
3808
|
+
task: false
|
|
3809
|
+
};
|
|
3810
|
+
}
|
|
3811
|
+
function validate(draft) {
|
|
3812
|
+
const validated = readEvent({ ...draft, id: "pending" });
|
|
3813
|
+
if (!validated)
|
|
3814
|
+
throw new RefusalError("the assembled event failed validation (check start, end and task)");
|
|
3815
|
+
const { id: _drop, ...rest } = validated;
|
|
3816
|
+
return rest;
|
|
3817
|
+
}
|
|
3818
|
+
function buildEventRecord(input, calendars, settings) {
|
|
3819
|
+
if (!input.date) throw new RefusalError('an event needs "date" (YYYY-MM-DD)');
|
|
3820
|
+
const day = parseDay(input.date);
|
|
3821
|
+
const calendar = resolveCalendar(input.calendarId, calendars, settings);
|
|
3822
|
+
const span = resolveSpanForCreate(input, day, settings.defaultDuration);
|
|
3823
|
+
return validate({
|
|
3824
|
+
title: input.title ?? "",
|
|
3825
|
+
calendarId: calendar.id,
|
|
3826
|
+
start: span.start,
|
|
3827
|
+
end: span.end,
|
|
3828
|
+
allDay: span.allDay,
|
|
3829
|
+
tzid: "",
|
|
3830
|
+
location: input.location ?? "",
|
|
3831
|
+
url: input.url ?? "",
|
|
3832
|
+
notes: input.notes ?? "",
|
|
3833
|
+
...buildRecurrence(input.recurrence),
|
|
3834
|
+
seriesId: "",
|
|
3835
|
+
seriesKey: "",
|
|
3836
|
+
color: input.color ?? "",
|
|
3837
|
+
icon: input.icon ?? "",
|
|
3838
|
+
task: span.task,
|
|
3839
|
+
doneKeys: []
|
|
3840
|
+
});
|
|
3841
|
+
}
|
|
3842
|
+
function applyPatchToEvent(base, input, calendars, settings, anchorMs = base.start) {
|
|
3843
|
+
const calendar = input.calendarId !== void 0 ? resolveCalendar(input.calendarId, calendars, settings) : calendars.find((c) => c.id === base.calendarId) ?? resolveCalendar(void 0, calendars, settings);
|
|
3844
|
+
const span = resolveSpanForPatch(input, base, anchorMs);
|
|
3845
|
+
return validate({
|
|
3846
|
+
title: input.title ?? base.title,
|
|
3847
|
+
calendarId: calendar.id,
|
|
3848
|
+
start: span.start,
|
|
3849
|
+
end: span.end,
|
|
3850
|
+
allDay: span.allDay,
|
|
3851
|
+
tzid: base.tzid,
|
|
3852
|
+
location: input.location ?? base.location,
|
|
3853
|
+
url: input.url ?? base.url,
|
|
3854
|
+
notes: input.notes ?? base.notes,
|
|
3855
|
+
...input.recurrence ? buildRecurrence(input.recurrence) : recurrenceOf(base),
|
|
3856
|
+
seriesId: base.seriesId,
|
|
3857
|
+
seriesKey: base.seriesKey,
|
|
3858
|
+
color: input.color ?? base.color,
|
|
3859
|
+
icon: input.icon ?? base.icon,
|
|
3860
|
+
task: span.task,
|
|
3861
|
+
doneKeys: base.doneKeys
|
|
3862
|
+
});
|
|
3863
|
+
}
|
|
3864
|
+
function calendarName(calendars, id) {
|
|
3865
|
+
return calendars.find((c) => c.id === id)?.name ?? "\u2014";
|
|
3866
|
+
}
|
|
3867
|
+
function eventSummaryLine(event, calendars, clock) {
|
|
3868
|
+
const when = event.task ? `${dayKey(new Date(event.start))} ${formatTimeShort(new Date(event.start), clock)}` : event.allDay ? `${dayKey(new Date(event.start))} \u2013 ${dayKey(addDays(new Date(event.end), -1))} (all day)` : `${dayKey(new Date(event.start))} ${formatTimeShort(new Date(event.start), clock)}\u2013${formatTimeShort(new Date(event.end), clock)}`;
|
|
3869
|
+
return `${event.title || "Untitled"} \u2014 [${calendarName(calendars, event.calendarId)}] \u2014 ${when} \u2014 id=${event.id}`;
|
|
3870
|
+
}
|
|
3871
|
+
function occurrenceLine(occ, calendars, clock) {
|
|
3872
|
+
const when = occ.event.allDay ? "all day" : occ.event.task ? `${occ.start.toISOString()} (${formatTimeShort(occ.start, clock)})` : `${occ.start.toISOString()} (${formatTimeShort(occ.start, clock)}) \u2013 ${occ.end.toISOString()} (${formatTimeShort(occ.end, clock)})`;
|
|
3873
|
+
const span = spanLabel(occ);
|
|
3874
|
+
const bits = [
|
|
3875
|
+
occ.event.title || "Untitled",
|
|
3876
|
+
`[${calendarName(calendars, occ.event.calendarId)}]`,
|
|
3877
|
+
when,
|
|
3878
|
+
...span ? [span] : [],
|
|
3879
|
+
...occ.event.task ? [occ.done ? "done" : "open"] : [],
|
|
3880
|
+
`id=${occ.event.id}`,
|
|
3881
|
+
`occurrence=${dayKey(occ.start)}`
|
|
3882
|
+
];
|
|
3883
|
+
return bits.join(" \xB7 ");
|
|
3884
|
+
}
|
|
3885
|
+
function listDay(contents, date) {
|
|
3886
|
+
assertDateKey(date);
|
|
3887
|
+
const day = fromDayKey(date);
|
|
3888
|
+
const agenda = dayAgenda(contents.events, contents.calendars, day);
|
|
3889
|
+
const clock = contents.settings.clock;
|
|
3890
|
+
return [
|
|
3891
|
+
`${date} \u2014 ${agenda.all.length} occurrence${agenda.all.length === 1 ? "" : "s"}`,
|
|
3892
|
+
"",
|
|
3893
|
+
"All day:",
|
|
3894
|
+
...agenda.allDay.length ? agenda.allDay.map((o) => ` ${occurrenceLine(o, contents.calendars, clock)}`) : [" (none)"],
|
|
3895
|
+
"",
|
|
3896
|
+
"Timed:",
|
|
3897
|
+
...agenda.timed.length ? agenda.timed.map((o) => ` ${occurrenceLine(o, contents.calendars, clock)}`) : [" (none)"]
|
|
3898
|
+
].join("\n");
|
|
3899
|
+
}
|
|
3900
|
+
var MAX_RANGE_DAYS = 366;
|
|
3901
|
+
var MAX_RANGE_OCCURRENCES = 500;
|
|
3902
|
+
function listRange(contents, from, to) {
|
|
3903
|
+
assertDateKey(from, "from");
|
|
3904
|
+
assertDateKey(to, "to");
|
|
3905
|
+
const fromDay = fromDayKey(from);
|
|
3906
|
+
const toDay = fromDayKey(to);
|
|
3907
|
+
const span = daysBetween(fromDay, toDay) + 1;
|
|
3908
|
+
if (span < 1) throw new RefusalError('"to" must not be before "from"');
|
|
3909
|
+
if (span > MAX_RANGE_DAYS) {
|
|
3910
|
+
throw new RefusalError(`the range is capped at ${MAX_RANGE_DAYS} days \u2014 asked for ${span}`);
|
|
3911
|
+
}
|
|
3912
|
+
const clock = contents.settings.clock;
|
|
3913
|
+
const lines = [];
|
|
3914
|
+
let count = 0;
|
|
3915
|
+
let truncated = false;
|
|
3916
|
+
for (let i = 0; i < span && !truncated; i++) {
|
|
3917
|
+
const day = addDays(fromDay, i);
|
|
3918
|
+
const occs = occurrencesOn(contents.events, contents.calendars, day);
|
|
3919
|
+
if (occs.length === 0) continue;
|
|
3920
|
+
lines.push(`${dayKey(day)}:`);
|
|
3921
|
+
for (const occ of occs) {
|
|
3922
|
+
if (count >= MAX_RANGE_OCCURRENCES) {
|
|
3923
|
+
truncated = true;
|
|
3924
|
+
break;
|
|
3925
|
+
}
|
|
3926
|
+
lines.push(` ${occurrenceLine(occ, contents.calendars, clock)}`);
|
|
3927
|
+
count++;
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
const header = `${from} to ${to} (${span} days) \u2014 ${count} occurrence${count === 1 ? "" : "s"}` + (truncated ? `, capped at ${MAX_RANGE_OCCURRENCES}` : "");
|
|
3931
|
+
return [header, ...lines.length ? lines : ["(nothing)"]].join("\n");
|
|
3932
|
+
}
|
|
3933
|
+
function searchEvents(contents, query, limit = 20) {
|
|
3934
|
+
const clock = contents.settings.clock;
|
|
3935
|
+
const scored = contents.events.map((event) => ({ event, score: scoreEvent(event, query) })).filter((s) => s.score !== null).sort((a, b) => b.score - a.score).slice(0, Math.max(0, limit));
|
|
3936
|
+
return [
|
|
3937
|
+
`"${query}" \u2014 ${scored.length} match${scored.length === 1 ? "" : "es"}`,
|
|
3938
|
+
...scored.length ? scored.map(({ event }) => eventSummaryLine(event, contents.calendars, clock)) : ["(none)"]
|
|
3939
|
+
].join("\n");
|
|
3940
|
+
}
|
|
3941
|
+
function freeSlots(contents, date, minutes, from, to) {
|
|
3942
|
+
assertDateKey(date);
|
|
3943
|
+
const day = fromDayKey(date);
|
|
3944
|
+
const occs = occurrencesOn(contents.events, contents.calendars, day);
|
|
3945
|
+
const fromMin = from !== void 0 ? parseTime(from, "from") : contents.settings.wakingFrom ?? DEFAULT_WAKING.from;
|
|
3946
|
+
const toMin = to !== void 0 ? parseTime(to, "to") : contents.settings.wakingTo ?? DEFAULT_WAKING.to;
|
|
3947
|
+
const clock = contents.settings.clock;
|
|
3948
|
+
const gaps = freeGaps(occs, minutes, fromMin, toMin);
|
|
3949
|
+
const load = dayLoad(occs, { from: fromMin, to: toMin });
|
|
3950
|
+
return [
|
|
3951
|
+
`${date} \u2014 free ${formatDuration(minutes)}+ slots between ${formatTimeShort(atMinutes(day, fromMin), clock)} and ${formatTimeShort(atMinutes(day, toMin), clock)}`,
|
|
3952
|
+
...gaps.length ? gaps.map(
|
|
3953
|
+
(g) => ` ${formatTimeShort(atMinutes(day, g.start), clock)}\u2013${formatTimeShort(atMinutes(day, g.end), clock)} (${formatDuration(g.end - g.start)})`
|
|
3954
|
+
) : [" (none)"],
|
|
3955
|
+
load ? `Booked ${formatDuration(load.booked)}, free ${formatDuration(load.free)} of the window.` : "Nothing booked today."
|
|
3956
|
+
].join("\n");
|
|
3957
|
+
}
|
|
3958
|
+
function listCalendars(contents) {
|
|
3959
|
+
return contents.calendars.map(
|
|
3960
|
+
(c) => `${c.name} \u2014 id=${c.id} \u2014 ${c.color}${c.feed ? " \u2014 subscription (read-only)" : ""}${c.visible ? "" : " \u2014 hidden"}`
|
|
3961
|
+
).join("\n");
|
|
3962
|
+
}
|
|
3963
|
+
function describeEvent(contents, id) {
|
|
3964
|
+
const event = contents.events.find((e) => e.id === id);
|
|
3965
|
+
if (!event) throw new RefusalError(`no event with id "${id}"`);
|
|
3966
|
+
const calendar = contents.calendars.find((c) => c.id === event.calendarId);
|
|
3967
|
+
const clock = contents.settings.clock;
|
|
3968
|
+
return [
|
|
3969
|
+
`${event.title || "Untitled"} (id=${event.id})`,
|
|
3970
|
+
`Calendar: ${calendarName(contents.calendars, event.calendarId)}${calendar?.feed ? " (subscription, read-only)" : ""}`,
|
|
3971
|
+
event.task ? `Task at ${dayKey(new Date(event.start))} ${formatTimeShort(new Date(event.start), clock)}` : event.allDay ? `All day, ${dayKey(new Date(event.start))} to ${dayKey(addDays(new Date(event.end), -1))}` : `${dayKey(new Date(event.start))} ${formatTimeShort(new Date(event.start), clock)}\u2013${formatTimeShort(new Date(event.end), clock)}`,
|
|
3972
|
+
`Repeats: ${repeats(event) ? describeRule(event) : "never"}`,
|
|
3973
|
+
...event.location ? [`Location: ${event.location}`] : [],
|
|
3974
|
+
...event.url ? [`URL: ${event.url}`] : [],
|
|
3975
|
+
...event.notes ? [`Notes: ${event.notes}`] : [],
|
|
3976
|
+
...event.seriesId ? [`Overrides one occurrence of id=${event.seriesId} (${event.seriesKey})`] : [],
|
|
3977
|
+
...event.task ? [
|
|
3978
|
+
`Done: ${event.doneKeys.length} occurrence${event.doneKeys.length === 1 ? "" : "s"} ticked`
|
|
3979
|
+
] : []
|
|
3980
|
+
].join("\n");
|
|
3981
|
+
}
|
|
3982
|
+
function performQuickAdd(contents, text, date) {
|
|
3983
|
+
if (!text.trim()) throw new RefusalError('"text" is empty');
|
|
3984
|
+
const reference = date ? parseDay(date) : startOfDay(/* @__PURE__ */ new Date());
|
|
3985
|
+
const parsed = parseQuickAdd(text, reference, /* @__PURE__ */ new Date(), contents.settings.defaultDuration);
|
|
3986
|
+
if (!parsed) {
|
|
3987
|
+
throw new RefusalError(
|
|
3988
|
+
`could not read "${text}" as an event \u2014 name something beyond a bare title, e.g. "lunch tomorrow 1pm 45m" or "gym every monday 7am"`
|
|
3989
|
+
);
|
|
3990
|
+
}
|
|
3991
|
+
const calendar = resolveCalendar(void 0, contents.calendars, contents.settings);
|
|
3992
|
+
const record = validate({
|
|
3993
|
+
...draftEvent(parsed.day, parsed.startMin, parsed.endMin, calendar.id),
|
|
3994
|
+
title: parsed.title,
|
|
3995
|
+
...parsed.repeat
|
|
3996
|
+
});
|
|
3997
|
+
const event = { ...record, id: newId() };
|
|
3998
|
+
return {
|
|
3999
|
+
mutated: {
|
|
4000
|
+
events: [...contents.events, event],
|
|
4001
|
+
calendars: contents.calendars,
|
|
4002
|
+
settings: contents.settings
|
|
4003
|
+
},
|
|
4004
|
+
message: `Created ${eventSummaryLine(event, contents.calendars, contents.settings.clock)}`
|
|
4005
|
+
};
|
|
4006
|
+
}
|
|
4007
|
+
function performCreateEvent(contents, input) {
|
|
4008
|
+
const record = buildEventRecord(input, contents.calendars, contents.settings);
|
|
4009
|
+
const event = { ...record, id: newId() };
|
|
4010
|
+
return {
|
|
4011
|
+
mutated: {
|
|
4012
|
+
events: [...contents.events, event],
|
|
4013
|
+
calendars: contents.calendars,
|
|
4014
|
+
settings: contents.settings
|
|
4015
|
+
},
|
|
4016
|
+
message: `Created ${eventSummaryLine(event, contents.calendars, contents.settings.clock)}`
|
|
4017
|
+
};
|
|
4018
|
+
}
|
|
4019
|
+
function performUpdateEvent(contents, id, scopeArg, occurrence, patch) {
|
|
4020
|
+
const original = contents.events.find((e) => e.id === id);
|
|
4021
|
+
if (!original) throw new RefusalError(`no event with id "${id}"`);
|
|
4022
|
+
refuseIfFeed(original, contents.calendars);
|
|
4023
|
+
const scope = requireScope(original, scopeArg);
|
|
4024
|
+
const clock = contents.settings.clock;
|
|
4025
|
+
if (scope === "all") {
|
|
4026
|
+
const record2 = applyPatchToEvent(original, patch, contents.calendars, contents.settings);
|
|
4027
|
+
const events = applyUpdate(contents.events, original.id, record2);
|
|
4028
|
+
const updated = events.find((e) => e.id === original.id);
|
|
4029
|
+
return {
|
|
4030
|
+
mutated: { events, calendars: contents.calendars, settings: contents.settings },
|
|
4031
|
+
message: `Updated ${eventSummaryLine(updated, contents.calendars, clock)}`
|
|
4032
|
+
};
|
|
4033
|
+
}
|
|
4034
|
+
const at = resolveOccurrenceRequired(original, occurrence, scope);
|
|
4035
|
+
if (scope === "one") {
|
|
4036
|
+
const record2 = applyPatchToEvent(original, patch, contents.calendars, contents.settings, at);
|
|
4037
|
+
const override = {
|
|
4038
|
+
...record2,
|
|
4039
|
+
...NEVER,
|
|
4040
|
+
seriesId: original.id,
|
|
4041
|
+
seriesKey: instantKey(at),
|
|
4042
|
+
id: newId()
|
|
4043
|
+
};
|
|
4044
|
+
return {
|
|
4045
|
+
mutated: {
|
|
4046
|
+
events: [...contents.events, override],
|
|
4047
|
+
calendars: contents.calendars,
|
|
4048
|
+
settings: contents.settings
|
|
4049
|
+
},
|
|
4050
|
+
message: `Updated one occurrence: ${eventSummaryLine(override, contents.calendars, clock)}`
|
|
4051
|
+
};
|
|
4052
|
+
}
|
|
4053
|
+
const record = applyPatchToEvent(original, patch, contents.calendars, contents.settings, at);
|
|
4054
|
+
const split = applySplit(contents.events, original.id, at, record, newId);
|
|
4055
|
+
if (!split.created) throw new RefusalError(`no event with id "${id}"`);
|
|
4056
|
+
return {
|
|
4057
|
+
mutated: { events: split.events, calendars: contents.calendars, settings: contents.settings },
|
|
4058
|
+
message: `Updated from ${dayKey(new Date(at))} on: ${eventSummaryLine(split.created, contents.calendars, clock)}`
|
|
4059
|
+
};
|
|
4060
|
+
}
|
|
4061
|
+
function performDeleteEvent(contents, id, scopeArg, occurrence) {
|
|
4062
|
+
const original = contents.events.find((e) => e.id === id);
|
|
4063
|
+
if (!original) throw new RefusalError(`no event with id "${id}"`);
|
|
4064
|
+
refuseIfFeed(original, contents.calendars);
|
|
4065
|
+
const scope = requireScope(original, scopeArg);
|
|
4066
|
+
const title = original.title || "Untitled";
|
|
4067
|
+
if (scope === "all") {
|
|
4068
|
+
return {
|
|
4069
|
+
mutated: {
|
|
4070
|
+
events: applyRemove(contents.events, original.id),
|
|
4071
|
+
calendars: contents.calendars,
|
|
4072
|
+
settings: contents.settings
|
|
4073
|
+
},
|
|
4074
|
+
message: `Deleted "${title}" (the whole series).`
|
|
4075
|
+
};
|
|
4076
|
+
}
|
|
4077
|
+
const at = resolveOccurrenceRequired(original, occurrence, scope);
|
|
4078
|
+
if (scope === "one") {
|
|
4079
|
+
const events = applyUpdate(contents.events, original.id, {
|
|
4080
|
+
exdates: [...original.exdates, instantKey(at)]
|
|
4081
|
+
});
|
|
4082
|
+
return {
|
|
4083
|
+
mutated: { events, calendars: contents.calendars, settings: contents.settings },
|
|
4084
|
+
message: `Deleted "${title}" on ${dayKey(new Date(at))} only \u2014 the series continues.`
|
|
4085
|
+
};
|
|
4086
|
+
}
|
|
4087
|
+
return {
|
|
4088
|
+
mutated: {
|
|
4089
|
+
events: applyTruncate(contents.events, original.id, at),
|
|
4090
|
+
calendars: contents.calendars,
|
|
4091
|
+
settings: contents.settings
|
|
4092
|
+
},
|
|
4093
|
+
message: `Deleted "${title}" from ${dayKey(new Date(at))} on.`
|
|
4094
|
+
};
|
|
4095
|
+
}
|
|
4096
|
+
function performToggleDone(contents, id, occurrence) {
|
|
4097
|
+
const original = contents.events.find((e) => e.id === id);
|
|
4098
|
+
if (!original) throw new RefusalError(`no event with id "${id}"`);
|
|
4099
|
+
if (!original.task) throw new RefusalError(`"${original.title || "Untitled"}" is not a task`);
|
|
4100
|
+
refuseIfFeed(original, contents.calendars);
|
|
4101
|
+
const at = resolveInstant(original, occurrence);
|
|
4102
|
+
const wasDone = original.doneKeys.includes(instantKey(at));
|
|
4103
|
+
const events = applyToggleDone(contents.events, original.id, at);
|
|
4104
|
+
return {
|
|
4105
|
+
mutated: { events, calendars: contents.calendars, settings: contents.settings },
|
|
4106
|
+
message: `${wasDone ? "Reopened" : "Completed"} "${original.title || "Untitled"}" on ${dayKey(new Date(at))}.`
|
|
4107
|
+
};
|
|
4108
|
+
}
|
|
4109
|
+
function performCreateCalendar(contents, name, color, icon) {
|
|
4110
|
+
const trimmed = name.trim();
|
|
4111
|
+
if (!trimmed) throw new RefusalError('a calendar needs a "name"');
|
|
4112
|
+
const calendar = readCalendar({
|
|
4113
|
+
id: newId(),
|
|
4114
|
+
name: trimmed,
|
|
4115
|
+
color: color || PALETTE[contents.calendars.length * 3 % PALETTE.length],
|
|
4116
|
+
icon: icon ?? "",
|
|
4117
|
+
visible: true
|
|
4118
|
+
});
|
|
4119
|
+
if (!calendar) throw new RefusalError("the assembled calendar failed validation");
|
|
4120
|
+
return {
|
|
4121
|
+
mutated: {
|
|
4122
|
+
events: contents.events,
|
|
4123
|
+
calendars: [...contents.calendars, calendar],
|
|
4124
|
+
settings: contents.settings
|
|
4125
|
+
},
|
|
4126
|
+
message: `Created calendar "${calendar.name}" \u2014 id=${calendar.id}`
|
|
4127
|
+
};
|
|
4128
|
+
}
|
|
4129
|
+
function resolveOccurrenceRequired(event, occurrence, scope) {
|
|
4130
|
+
if (!occurrence) {
|
|
4131
|
+
throw new RefusalError(
|
|
4132
|
+
`scope "${scope}" needs "occurrence" (a YYYY-MM-DD date or full instant)`
|
|
4133
|
+
);
|
|
4134
|
+
}
|
|
4135
|
+
return resolveInstant(event, occurrence);
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
// mcp/tools.ts
|
|
4139
|
+
function textResult(text) {
|
|
4140
|
+
return { content: [{ type: "text", text }] };
|
|
4141
|
+
}
|
|
4142
|
+
function withWarning(text, warning) {
|
|
4143
|
+
return warning ? `${warning}
|
|
4144
|
+
|
|
4145
|
+
${text}` : text;
|
|
4146
|
+
}
|
|
4147
|
+
async function guarded(run) {
|
|
4148
|
+
try {
|
|
4149
|
+
return textResult(await run());
|
|
4150
|
+
} catch (error) {
|
|
4151
|
+
if (!(error instanceof Error)) throw error;
|
|
4152
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
4155
|
+
var DATE_KEY_RE2 = /^\d{4}-\d{2}-\d{2}$/;
|
|
4156
|
+
var DATE_KEY_MESSAGE = "must be YYYY-MM-DD, local";
|
|
4157
|
+
function dateKey(description) {
|
|
4158
|
+
return z.string().regex(DATE_KEY_RE2, DATE_KEY_MESSAGE).describe(description);
|
|
4159
|
+
}
|
|
4160
|
+
var RECURRENCE_DESCRIPTION = "Replaces the event's whole recurrence rule, exceptions included. Omit entirely for a one-off event on create, or to leave an existing rule untouched on update.";
|
|
4161
|
+
var recurrenceSchema = z.strictObject({
|
|
4162
|
+
repeat: z.enum(["daily", "weekly", "monthly", "yearly"]),
|
|
4163
|
+
interval: z.number().int().min(1).describe("Every Nth period; 1 = every one.").optional(),
|
|
4164
|
+
until: dateKey(
|
|
4165
|
+
"YYYY-MM-DD, local, inclusive \u2014 the last day an occurrence may start. Omit for no end."
|
|
4166
|
+
).optional(),
|
|
4167
|
+
wkst: z.number().int().min(0).max(6).describe("Week-start weekday, 0=Sunday..6=Saturday.").optional(),
|
|
4168
|
+
byDay: z.array(
|
|
4169
|
+
z.strictObject({
|
|
4170
|
+
day: z.number().int().min(0).max(6),
|
|
4171
|
+
nth: z.number().int().min(-5).max(5).optional()
|
|
4172
|
+
})
|
|
4173
|
+
).describe(
|
|
4174
|
+
"Which weekdays, for a weekly or monthly rule. day: 0=Sunday..6=Saturday. nth (monthly only): 1st..5th from the start of the month, negative from the end, 0 or omitted = every one."
|
|
4175
|
+
).optional(),
|
|
4176
|
+
byMonthDay: z.array(z.number().int().min(-31).max(31)).describe("Days of the month, 1-31 or negative counted from the end.").optional(),
|
|
4177
|
+
byMonth: z.array(z.number().int().min(1).max(12)).optional(),
|
|
4178
|
+
byWeekNo: z.array(z.number().int().min(-53).max(53)).optional(),
|
|
4179
|
+
byYearDay: z.array(z.number().int().min(-366).max(366)).optional(),
|
|
4180
|
+
bySetPos: z.array(z.number().int()).describe("Which of the period's own occurrences to keep, e.g. -1 = the last one.").optional()
|
|
4181
|
+
}).describe(RECURRENCE_DESCRIPTION);
|
|
4182
|
+
function eventFieldsShape() {
|
|
4183
|
+
return {
|
|
4184
|
+
title: z.string().optional(),
|
|
4185
|
+
date: dateKey("YYYY-MM-DD, local.").optional(),
|
|
4186
|
+
start: z.string().describe("HH:MM, 24-hour local clock. Required for a timed event or a task.").optional(),
|
|
4187
|
+
end: z.string().describe(
|
|
4188
|
+
"HH:MM for a timed event, or YYYY-MM-DD (inclusive) for a multi-day all-day event. Alternative to duration."
|
|
4189
|
+
).optional(),
|
|
4190
|
+
duration: z.number().describe("Minutes \u2014 an alternative to end, for a timed event.").optional(),
|
|
4191
|
+
allDay: z.boolean().describe("Forces an all-day event. Inferred when start, end and duration are all omitted.").optional(),
|
|
4192
|
+
task: z.boolean().describe("A dated to-do (one moment, not a span) rather than an event.").optional(),
|
|
4193
|
+
calendarId: z.string().describe(
|
|
4194
|
+
"Defaults to the user's default calendar. Must not be a subscription \u2014 those are read-only."
|
|
4195
|
+
).optional(),
|
|
4196
|
+
color: z.string().describe("#rrggbb. Empty follows the calendar's own colour.").optional(),
|
|
4197
|
+
icon: z.string().describe("A Phosphor icon name or one emoji. Empty follows the calendar's own mark.").optional(),
|
|
4198
|
+
location: z.string().optional(),
|
|
4199
|
+
url: z.string().optional(),
|
|
4200
|
+
notes: z.string().optional(),
|
|
4201
|
+
recurrence: recurrenceSchema.optional()
|
|
4202
|
+
};
|
|
4203
|
+
}
|
|
4204
|
+
var createEventFieldsSchema = z.strictObject({
|
|
4205
|
+
...eventFieldsShape(),
|
|
4206
|
+
date: dateKey("YYYY-MM-DD, local.")
|
|
4207
|
+
});
|
|
4208
|
+
var patchEventFieldsSchema = z.strictObject(eventFieldsShape());
|
|
4209
|
+
var SCOPE_DESCRIPTION = 'Required when the target event repeats: "one" (just this occurrence), "future" (this one and everything after), or "all" (the whole series). Ignored when the event does not repeat.';
|
|
4210
|
+
var OCCURRENCE_DESCRIPTION = 'Which occurrence, as a YYYY-MM-DD date or a full "YYYY-MM-DDTHH:mm:ss" instant. Required with scope "one" or "future".';
|
|
4211
|
+
var scopeSchema = z.enum(["one", "future", "all"]).describe(SCOPE_DESCRIPTION);
|
|
4212
|
+
var occurrenceSchema = z.string().describe(OCCURRENCE_DESCRIPTION);
|
|
4213
|
+
function registerTools(server, ctx2) {
|
|
4214
|
+
server.registerTool(
|
|
4215
|
+
"list_day",
|
|
4216
|
+
{
|
|
4217
|
+
description: "The agenda for one local day: recurrence expanded, all-day and timed occurrences listed separately, each with its id (for update_event/delete_event/toggle_done), its occurrence key, and \u2014 for a multi-day span \u2014 its Day N/M position.",
|
|
4218
|
+
inputSchema: z.strictObject({ date: dateKey("YYYY-MM-DD, local.") }),
|
|
4219
|
+
annotations: { readOnlyHint: true }
|
|
4220
|
+
},
|
|
4221
|
+
async ({ date }) => guarded(async () => {
|
|
4222
|
+
const { contents, warning } = await loadForRead(ctx2);
|
|
4223
|
+
return withWarning(listDay(contents, date), warning);
|
|
4224
|
+
})
|
|
4225
|
+
);
|
|
4226
|
+
server.registerTool(
|
|
4227
|
+
"list_range",
|
|
4228
|
+
{
|
|
4229
|
+
description: `The agenda across several local days, one section per day that has something on it. Capped at ${MAX_RANGE_DAYS} days of span and ${MAX_RANGE_OCCURRENCES} occurrences total \u2014 a wide range may come back truncated, which the response says.`,
|
|
4230
|
+
inputSchema: z.strictObject({
|
|
4231
|
+
from: dateKey("YYYY-MM-DD, local, inclusive."),
|
|
4232
|
+
to: dateKey("YYYY-MM-DD, local, inclusive.")
|
|
4233
|
+
}),
|
|
4234
|
+
annotations: { readOnlyHint: true }
|
|
4235
|
+
},
|
|
4236
|
+
async ({ from, to }) => guarded(async () => {
|
|
4237
|
+
const { contents, warning } = await loadForRead(ctx2);
|
|
4238
|
+
return withWarning(listRange(contents, from, to), warning);
|
|
4239
|
+
})
|
|
4240
|
+
);
|
|
4241
|
+
server.registerTool(
|
|
4242
|
+
"search",
|
|
4243
|
+
{
|
|
4244
|
+
description: "Ranked title/location/notes search over the user's own events (never a subscription's).",
|
|
4245
|
+
inputSchema: z.strictObject({
|
|
4246
|
+
query: z.string(),
|
|
4247
|
+
limit: z.number().int().min(1).describe("Default 20.").optional()
|
|
4248
|
+
}),
|
|
4249
|
+
annotations: { readOnlyHint: true }
|
|
4250
|
+
},
|
|
4251
|
+
async ({ query, limit }) => guarded(async () => {
|
|
4252
|
+
const { contents, warning } = await loadForRead(ctx2);
|
|
4253
|
+
return withWarning(searchEvents(contents, query, limit), warning);
|
|
4254
|
+
})
|
|
4255
|
+
);
|
|
4256
|
+
server.registerTool(
|
|
4257
|
+
"free_slots",
|
|
4258
|
+
{
|
|
4259
|
+
description: "Gaps of at least `minutes` on one day, inside a window that defaults to the user's own waking hours setting. Also reports the day's booked and free minutes across that window.",
|
|
4260
|
+
inputSchema: z.strictObject({
|
|
4261
|
+
date: dateKey("YYYY-MM-DD, local."),
|
|
4262
|
+
minutes: z.number().describe("Minimum slot length, in minutes."),
|
|
4263
|
+
from: z.string().describe("HH:MM, local. Defaults to the user's waking-hours start.").optional(),
|
|
4264
|
+
to: z.string().describe("HH:MM, local. Defaults to the user's waking-hours end.").optional()
|
|
4265
|
+
}),
|
|
4266
|
+
annotations: { readOnlyHint: true }
|
|
4267
|
+
},
|
|
4268
|
+
async ({ date, minutes, from, to }) => guarded(async () => {
|
|
4269
|
+
const { contents, warning } = await loadForRead(ctx2);
|
|
4270
|
+
return withWarning(freeSlots(contents, date, minutes, from, to), warning);
|
|
4271
|
+
})
|
|
4272
|
+
);
|
|
4273
|
+
server.registerTool(
|
|
4274
|
+
"list_calendars",
|
|
4275
|
+
{
|
|
4276
|
+
description: "Every calendar: id, name, colour, whether it is a read-only subscription, and visibility.",
|
|
4277
|
+
inputSchema: z.strictObject({}),
|
|
4278
|
+
annotations: { readOnlyHint: true }
|
|
4279
|
+
},
|
|
4280
|
+
async () => guarded(async () => {
|
|
4281
|
+
const { contents, warning } = await loadForRead(ctx2);
|
|
4282
|
+
return withWarning(listCalendars(contents), warning);
|
|
4283
|
+
})
|
|
4284
|
+
);
|
|
4285
|
+
server.registerTool(
|
|
4286
|
+
"describe_event",
|
|
4287
|
+
{
|
|
4288
|
+
description: "One event in full, including its recurrence rendered as an English sentence.",
|
|
4289
|
+
inputSchema: z.strictObject({ id: z.string() }),
|
|
4290
|
+
annotations: { readOnlyHint: true }
|
|
4291
|
+
},
|
|
4292
|
+
async ({ id }) => guarded(async () => {
|
|
4293
|
+
const { contents, warning } = await loadForRead(ctx2);
|
|
4294
|
+
return withWarning(describeEvent(contents, id), warning);
|
|
4295
|
+
})
|
|
4296
|
+
);
|
|
4297
|
+
server.registerTool(
|
|
4298
|
+
"quick_add",
|
|
4299
|
+
{
|
|
4300
|
+
description: `Creates an event from one line of text, through circal's own quick-add grammar \u2014 the same parser the app's own command palette uses. Understands things like "lunch tomorrow 1pm 45m" and "gym every monday 7am". Returns the resolved event for confirmation.`,
|
|
4301
|
+
inputSchema: z.strictObject({
|
|
4302
|
+
text: z.string(),
|
|
4303
|
+
date: dateKey(
|
|
4304
|
+
'YYYY-MM-DD, local \u2014 what "tomorrow" etc. is relative to. Defaults to today.'
|
|
4305
|
+
).optional()
|
|
4306
|
+
}),
|
|
4307
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
4308
|
+
},
|
|
4309
|
+
async ({ text, date }) => guarded(
|
|
4310
|
+
() => casWrite(ctx2, (contents) => {
|
|
4311
|
+
const outcome = performQuickAdd(contents, text, date);
|
|
4312
|
+
return { result: outcome.mutated, summary: outcome.message };
|
|
4313
|
+
})
|
|
4314
|
+
)
|
|
4315
|
+
);
|
|
4316
|
+
server.registerTool(
|
|
4317
|
+
"create_event",
|
|
4318
|
+
{
|
|
4319
|
+
description: "Creates an event from explicit fields, for anything quick_add's grammar cannot spell \u2014 a full recurrence, a specific calendar, notes. Every field is validated the same way an imported backup is.",
|
|
4320
|
+
inputSchema: createEventFieldsSchema,
|
|
4321
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
4322
|
+
},
|
|
4323
|
+
async (input) => guarded(
|
|
4324
|
+
() => casWrite(ctx2, (contents) => {
|
|
4325
|
+
const outcome = performCreateEvent(contents, input);
|
|
4326
|
+
return { result: outcome.mutated, summary: outcome.message };
|
|
4327
|
+
})
|
|
4328
|
+
)
|
|
4329
|
+
);
|
|
4330
|
+
server.registerTool(
|
|
4331
|
+
"update_event",
|
|
4332
|
+
{
|
|
4333
|
+
description: "Patches an existing event. Every field in `patch` is optional and left alone when omitted. " + SCOPE_DESCRIPTION,
|
|
4334
|
+
inputSchema: z.strictObject({
|
|
4335
|
+
id: z.string(),
|
|
4336
|
+
scope: scopeSchema.optional(),
|
|
4337
|
+
occurrence: occurrenceSchema.optional(),
|
|
4338
|
+
patch: patchEventFieldsSchema
|
|
4339
|
+
}),
|
|
4340
|
+
annotations: { destructiveHint: true, idempotentHint: false }
|
|
4341
|
+
},
|
|
4342
|
+
async ({ id, scope, occurrence, patch }) => guarded(
|
|
4343
|
+
() => casWrite(ctx2, (contents) => {
|
|
4344
|
+
const outcome = performUpdateEvent(contents, id, scope, occurrence, patch);
|
|
4345
|
+
return { result: outcome.mutated, summary: outcome.message };
|
|
4346
|
+
})
|
|
4347
|
+
)
|
|
4348
|
+
);
|
|
4349
|
+
server.registerTool(
|
|
4350
|
+
"delete_event",
|
|
4351
|
+
{
|
|
4352
|
+
description: `Deletes an event, or one occurrence, or a series' tail. ${SCOPE_DESCRIPTION}`,
|
|
4353
|
+
inputSchema: z.strictObject({
|
|
4354
|
+
id: z.string(),
|
|
4355
|
+
scope: scopeSchema.optional(),
|
|
4356
|
+
occurrence: occurrenceSchema.optional()
|
|
4357
|
+
}),
|
|
4358
|
+
annotations: { destructiveHint: true, idempotentHint: false }
|
|
4359
|
+
},
|
|
4360
|
+
async ({ id, scope, occurrence }) => guarded(
|
|
4361
|
+
() => casWrite(ctx2, (contents) => {
|
|
4362
|
+
const outcome = performDeleteEvent(contents, id, scope, occurrence);
|
|
4363
|
+
return { result: outcome.mutated, summary: outcome.message };
|
|
4364
|
+
})
|
|
4365
|
+
)
|
|
4366
|
+
);
|
|
4367
|
+
server.registerTool(
|
|
4368
|
+
"toggle_done",
|
|
4369
|
+
{
|
|
4370
|
+
description: "Ticks a task off, or back on, one occurrence at a time \u2014 never the whole series at once.",
|
|
4371
|
+
inputSchema: z.strictObject({
|
|
4372
|
+
id: z.string(),
|
|
4373
|
+
occurrence: z.string().describe("YYYY-MM-DD or a full instant. Required for a repeating task.").optional()
|
|
4374
|
+
}),
|
|
4375
|
+
annotations: { destructiveHint: false, idempotentHint: false }
|
|
4376
|
+
},
|
|
4377
|
+
async ({ id, occurrence }) => guarded(
|
|
4378
|
+
() => casWrite(ctx2, (contents) => {
|
|
4379
|
+
const outcome = performToggleDone(contents, id, occurrence);
|
|
4380
|
+
return { result: outcome.mutated, summary: outcome.message };
|
|
4381
|
+
})
|
|
4382
|
+
)
|
|
4383
|
+
);
|
|
4384
|
+
server.registerTool(
|
|
4385
|
+
"create_calendar",
|
|
4386
|
+
{
|
|
4387
|
+
description: "Adds a calendar of the user's own \u2014 never a subscription; those come from the app's Settings.",
|
|
4388
|
+
inputSchema: z.strictObject({
|
|
4389
|
+
name: z.string(),
|
|
4390
|
+
color: z.string().describe("#rrggbb. Defaults to the next palette colour.").optional(),
|
|
4391
|
+
icon: z.string().describe("A Phosphor icon name or one emoji.").optional()
|
|
4392
|
+
}),
|
|
4393
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
4394
|
+
},
|
|
4395
|
+
async ({ name, color, icon }) => guarded(
|
|
4396
|
+
() => casWrite(ctx2, (contents) => {
|
|
4397
|
+
const outcome = performCreateCalendar(contents, name, color, icon);
|
|
4398
|
+
return { result: outcome.mutated, summary: outcome.message };
|
|
4399
|
+
})
|
|
4400
|
+
)
|
|
4401
|
+
);
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
// mcp/index.ts
|
|
4405
|
+
var HELP = `circal-mcp \u2014 reads and writes circal's mirror file over stdio (MCP)
|
|
4406
|
+
|
|
4407
|
+
Usage:
|
|
4408
|
+
circal-mcp --file <path> Start the server against a mirror file
|
|
4409
|
+
circal-mcp --find Search common locations for a mirror file and
|
|
4410
|
+
print a ready-to-paste client config (read-only,
|
|
4411
|
+
does not start the server)
|
|
4412
|
+
circal-mcp --help Print this message
|
|
4413
|
+
|
|
4414
|
+
Environment:
|
|
4415
|
+
CIRCAL_FILE Mirror file path, same as --file
|
|
4416
|
+
CIRCAL_ALLOW_ZONE_MISMATCH Set to 1 to allow writes when this process's
|
|
4417
|
+
zone does not match the file's recorded zone
|
|
4418
|
+
`;
|
|
4419
|
+
var argv = process.argv.slice(2);
|
|
4420
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
4421
|
+
console.log(HELP);
|
|
4422
|
+
process.exit(0);
|
|
4423
|
+
}
|
|
4424
|
+
if (argv.includes("--find")) {
|
|
4425
|
+
await runFind();
|
|
4426
|
+
process.exit(0);
|
|
4427
|
+
}
|
|
4428
|
+
var path;
|
|
4429
|
+
try {
|
|
4430
|
+
path = resolveFilePath(argv, process.env);
|
|
4431
|
+
} catch (error) {
|
|
4432
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4433
|
+
console.error(`${message} Run \`circal-mcp --find\` to search common locations for one.`);
|
|
4434
|
+
process.exit(1);
|
|
4435
|
+
}
|
|
4436
|
+
var ctx = { path, env: process.env };
|
|
4437
|
+
function createServer() {
|
|
4438
|
+
const server = new McpServer({ name: "circal", version: "1.0.0" });
|
|
4439
|
+
registerTools(server, ctx);
|
|
4440
|
+
return server;
|
|
4441
|
+
}
|
|
4442
|
+
void serveStdio(createServer);
|
|
4443
|
+
console.error("circal MCP server running on stdio");
|
|
4444
|
+
/*! Bundled license information:
|
|
4445
|
+
|
|
4446
|
+
react/cjs/react.production.js:
|
|
4447
|
+
(**
|
|
4448
|
+
* @license React
|
|
4449
|
+
* react.production.js
|
|
4450
|
+
*
|
|
4451
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4452
|
+
*
|
|
4453
|
+
* This source code is licensed under the MIT license found in the
|
|
4454
|
+
* LICENSE file in the root directory of this source tree.
|
|
4455
|
+
*)
|
|
4456
|
+
|
|
4457
|
+
react/cjs/react.development.js:
|
|
4458
|
+
(**
|
|
4459
|
+
* @license React
|
|
4460
|
+
* react.development.js
|
|
4461
|
+
*
|
|
4462
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4463
|
+
*
|
|
4464
|
+
* This source code is licensed under the MIT license found in the
|
|
4465
|
+
* LICENSE file in the root directory of this source tree.
|
|
4466
|
+
*)
|
|
4467
|
+
*/
|