diff --git a/Editor/editorfe/src/scriptWidgetAPI.js b/Editor/editorfe/src/scriptWidgetAPI.js index d489dc99..492fa819 100644 --- a/Editor/editorfe/src/scriptWidgetAPI.js +++ b/Editor/editorfe/src/scriptWidgetAPI.js @@ -6,7 +6,7 @@ import { dataAPICatalog } from "./dataAPICatalog.js"; // authority; tests verify that this metadata stays aligned with it. export const scriptWidgetAPI = { "schemaVersion": 1, - "runtimeVersion": "1.4", + "runtimeVersion": "1.5", "documentation": "Public JSX surface shared by ScriptWidget Runtime and Studio.", "platforms": { "ios": "16.0", @@ -71,6 +71,11 @@ export const scriptWidgetAPI = { arguments: api.arguments, returns: api.asynchronous ? "Promise" : typeof api.value === "object" ? "json" : typeof api.value, }])), + "$icloud.readJSON": {"documentation": "Read cached JSON and revision; conflicts preserve all heads. Requires icloud permission and a native connection. Never certifies upload.", "arguments": ["name"], "returns": "Promise", "minimumRuntimeVersion": "1.5"}, + "$icloud.writeJSON": {"documentation": "Save a new immutable JSON revision locally from a user action. Requires the revision from readJSON. Background exchange is eventual.", "arguments": ["name", "value", "expectedRevision"], "returns": "Promise", "minimumRuntimeVersion": "1.5"}, + "$icloud.resolveJSON": {"documentation": "Explicitly resolve all observed conflicting heads into a new revision. Stale tokens fail; originals remain.", "arguments": ["name", "value", "expectedRevision"], "returns": "Promise", "minimumRuntimeVersion": "1.5"}, + "$icloud.sync": {"documentation": "Exchange materialized revisions with iCloud; requires a user action. Upload completion is unknown.", "arguments": [], "returns": "Promise", "minimumRuntimeVersion": "1.5"}, + "$icloud.status": {"documentation": "Read native connection state. Cloud store IDs are granted in the app, never by the script. Web uses preview mocks.", "arguments": [], "returns": "Promise", "minimumRuntimeVersion": "1.5"}, "$file.exists": { "documentation": "Return whether a regular file is locally present inside this package, including binary assets. Missing files, directories, unsafe paths and links return false. Does not download iCloud files or read outside the package. Web preview uses workspace file presence.", "arguments": ["path"], "returns": "boolean", "minimumRuntimeVersion": "1.3" }, "$file.read": { "documentation": "Read package-relative UTF-8 text. Missing or unavailable files return an empty string.", "arguments": ["path"], "returns": "string" }, "$file.readJSON": { "documentation": "Read a package-relative JSON object.", "arguments": ["path"], "returns": "json" }, diff --git a/Scripts/release-readiness.sh b/Scripts/release-readiness.sh index 7f2306c2..ff0ae4a6 100755 --- a/Scripts/release-readiness.sh +++ b/Scripts/release-readiness.sh @@ -36,6 +36,7 @@ if [ "${SCRIPTWIDGET_SKIP_XCODE:-0}" != "1" ]; then node Scripts/test-runtime-delivery.mjs node Scripts/test-service-authorization.mjs node Scripts/test-storage-atomic.mjs + node Scripts/test-icloud-json.mjs node Scripts/test-storage-files.mjs sh Scripts/macos-widget-link-tests.sh xcodebuild -quiet -project macOS/ScriptWidgetMac.xcodeproj -scheme ScriptWidgetRuntimeTests \ diff --git a/Scripts/test-icloud-json.mjs b/Scripts/test-icloud-json.mjs new file mode 100644 index 00000000..3a97a768 --- /dev/null +++ b/Scripts/test-icloud-json.mjs @@ -0,0 +1,45 @@ +// Run the actual CloudJSON XCTest cases without an app/testmanagerd session. +// Compile unchanged production file helpers and storage services; all fixtures +// are temporary directories. This supplements, never replaces, app-host tests. +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { mkdtemp, readFile, writeFile, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +const exec = promisify(execFile); +const repo = fileURLToPath(new URL("..", import.meta.url)); +const temporary = await mkdtemp(join(tmpdir(), "scriptwidget-cloud-json-")); +try { + const developer = (await exec("xcode-select", ["-p"])).stdout.trim(); + const platform = join(developer, "Platforms/MacOSX.platform/Developer"); + const frameworks = join(platform, "Library/Frameworks"), libraries = join(platform, "usr/lib"); + const source = await readFile(join(repo, "Shared/ScriptWidgetRuntime/Common/ScriptWidgetPackage.swift"), "utf8"); + const start = source.indexOf("enum ScriptWidgetMetadataFile {"), end = source.indexOf("/// Keeps private execution resources"); + if (start < 0 || end <= start) throw new Error("Production file-helper boundaries changed; update the probe."); + await writeFile(join(temporary, "helpers.swift"), "import Foundation\nimport Darwin\n" + source.slice(start, end)); + const tests = await readFile(join(repo, "Tests/ScriptWidgetRuntimeTests/PackageCacheTests.swift"), "utf8"); + const testStart = tests.indexOf("final class CloudJSONTests: XCTestCase {"); + if (testStart < 0) throw new Error("Missing CloudJSON fixture tests."); + await writeFile(join(temporary, "main.swift"), "import XCTest\n" + tests.slice(testStart) + ` +let suite = CloudJSONTests.defaultTestSuite +suite.run() +guard let result = suite.testRun, result.executionCount >= 10, result.totalFailureCount == 0 else { exit(1) } +`); + const binary = join(temporary, "tests"); + await exec("xcrun", ["swiftc", "-F", frameworks, "-I", libraries, "-L", libraries, + ...[frameworks, libraries, join(platform, "Library/PrivateFrameworks"), resolve(developer, "../SharedFrameworks")] + .flatMap(path => ["-Xlinker", "-rpath", "-Xlinker", path]), + ...["Shared/ScriptWidgetRuntime/Widget/API/ScriptWidgetStorageLock.swift", + "Shared/ScriptWidgetRuntime/Widget/API/ScriptWidgetStorageNamespaces.swift", + "Shared/ScriptWidgetRuntime/Common/ScriptWidgetCloudJSON.swift"].map(path => resolve(repo, path)), + join(temporary, "helpers.swift"), join(temporary, "main.swift"), "-o", binary], { timeout: 60000 }); + const result = await exec(binary, [], { timeout: 30000 }); + process.stdout.write(result.stdout + result.stderr); +} catch (error) { + process.stderr.write(error.stdout ?? ""); + process.stderr.write(error.stderr ?? ""); + throw error; +} finally { + await rm(temporary, { recursive: true, force: true }); +} diff --git a/Shared/ScriptWidgetRuntime/AI/PromptBuilder.swift b/Shared/ScriptWidgetRuntime/AI/PromptBuilder.swift index 13be80d6..32740135 100644 --- a/Shared/ScriptWidgetRuntime/AI/PromptBuilder.swift +++ b/Shared/ScriptWidgetRuntime/AI/PromptBuilder.swift @@ -124,6 +124,13 @@ enum PromptBuilder { `$storage.getString(key)` for strings; `$storage.setJSON(key, object)` and `$storage.getJSON(key)` for objects. Check write success. Do not invent `$storage.set` or `$storage.get`. + `$storage` stays local. Opt-in `$icloud` requires permission `icloud`, + minimumRuntimeVersion 1.5 and an explicit native app connection. + readJSON(name) returns {value,revision,state,conflicts}; user actions + can writeJSON(name,value,revision) or explicitly resolveJSON after + reviewing every head. Never claim a local save is already uploaded, + silently migrate existing records or choose a store ID in scripts. + Browser previews use isolated mocks, not real iCloud. 7. Only use tags, props, and APIs that appear in the REFERENCE section below. Do not invent new ones. 8. When calling `fetch`, always wrap it in try/catch so the diff --git a/Shared/ScriptWidgetRuntime/Common/ScriptMetadata.swift b/Shared/ScriptWidgetRuntime/Common/ScriptMetadata.swift index 0a295907..a15c8f62 100644 --- a/Shared/ScriptWidgetRuntime/Common/ScriptMetadata.swift +++ b/Shared/ScriptWidgetRuntime/Common/ScriptMetadata.swift @@ -59,6 +59,7 @@ enum WidgetPackagePermission: String, Codable, CaseIterable, Identifiable { case services case music case storage + case icloud case files var id: String { rawValue } @@ -305,6 +306,9 @@ enum WidgetPackageManifestValidator { error("unsupported_minimum_runtime", "This package requires a newer or incompatible ScriptWidget Runtime. Update the app before running it.") } } + if manifest.permissions.contains(.icloud), !WidgetRuntimeRequirement.supports("1.5", current: manifest.minimumRuntimeVersion ?? "0.0") { + error("icloud_runtime", "iCloud JSON requires minimumRuntimeVersion 1.5 or later.") + } if let timeline = manifest.timeline, !timeline.isValid { error("invalid_timeline", "timeline requires 1–4 sorted unique dailyTimes in HH:mm and a valid timeZone.") } diff --git a/Shared/ScriptWidgetRuntime/Common/ScriptWidgetCloudJSON.swift b/Shared/ScriptWidgetRuntime/Common/ScriptWidgetCloudJSON.swift new file mode 100644 index 00000000..99f1dc68 --- /dev/null +++ b/Shared/ScriptWidgetRuntime/Common/ScriptWidgetCloudJSON.swift @@ -0,0 +1,295 @@ +import Foundation +import CryptoKit +import Darwin + +/// Immutable, content-addressed JSON revisions. Never overwrite a remote head: +/// disconnected writers produce two heads which require explicit resolution. +enum ScriptWidgetCloudJSONError: String, Error, LocalizedError { + case unavailable, notConnected, accountChanged, invalidName, invalidData, limitExceeded, conflict, staleRevision, identityChanged + var errorDescription: String? { "[icloud:\(rawValue)] iCloud JSON operation could not complete. Existing records are preserved." } +} + +struct ScriptWidgetCloudJSONEnvironment { + let account: String? + let documents: URL? + static func live() -> Self { + // CLI and test hosts must never discover the user's real iCloud. + guard ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] == nil, + ProcessInfo.processInfo.environment["XCTestBundlePath"] == nil, + ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != "1", + NSClassFromString("XCTestCase") == nil, NSClassFromString("XCTest.XCTestCase") == nil, + Bundle.main.bundleIdentifier == "com.everettjf.scriptwidget" || Bundle.main.bundleIdentifier == "com.everettjf.scriptwidget.widget" || Bundle.main.bundleIdentifier == "com.everettjf.scriptwidget.macwidget" else { return .init(account: nil, documents: nil) } + guard let token = FileManager.default.ubiquityIdentityToken, + let bytes = try? NSKeyedArchiver.archivedData(withRootObject: token, requiringSecureCoding: false) else { return .init(account: nil, documents: nil) } + return .init(account: ScriptWidgetCloudJSONStore.hash(bytes), documents: FileManager.default.url(forUbiquityContainerIdentifier: "iCloud.ScriptWidget")?.appendingPathComponent("Documents")) + } +} + +struct ScriptWidgetCloudJSONGrant: Codable { + let version: Int + let store: String + let account: String +} + +/// Uses the same descriptor-relative, no-follow helpers as authored packages. +/// Authority is a native installation UUID + a local native grant, not a name, +/// manifest ID, script environment, exported package or cloud metadata file. +final class ScriptWidgetCloudJSONStore { + static let maximumValueBytes = 64 * 1024 + static let maximumRevisions = 256 + static let maximumBytes = 2 * 1024 * 1024 + static let queue = DispatchQueue(label: "ScriptWidget.CloudJSON", qos: .utility) + private let container: URL + private let owner: String + private let environment: () -> ScriptWidgetCloudJSONEnvironment + private let validate: () throws -> Void + private let rootReceipt: ScriptWidgetStoragePublicationReceipt + + init(container: URL, owner: String, environment: @escaping () -> ScriptWidgetCloudJSONEnvironment = ScriptWidgetCloudJSONEnvironment.live, + validate: @escaping () throws -> Void) throws { + guard UUID(uuidString: owner)?.uuidString.lowercased() == owner, + let receipt = try ScriptWidgetPackageFiles.directoryReceipt(root: container.deletingLastPathComponent(), relative: container.lastPathComponent) else { throw ScriptWidgetCloudJSONError.unavailable } + self.container = container; self.owner = owner; self.environment = environment; self.validate = validate; self.rootReceipt = receipt + } + + static func hash(_ bytes: Data) -> String { SHA256.hash(data: bytes).map { String(format: "%02x", $0) }.joined() } + private static func canonical(_ value: Any) throws -> Data { + guard JSONSerialization.isValidJSONObject(value) else { throw ScriptWidgetCloudJSONError.invalidData } + return try JSONSerialization.data(withJSONObject: value, options: [.sortedKeys, .withoutEscapingSlashes]) + } + static func validName(_ name: String) -> Bool { name.range(of: #"^[a-z][a-z0-9_-]{0,58}\.json$"#, options: .regularExpression) != nil } + private var grantPath: String { "CloudJSON.v1/grants/\(owner).json" } + private func check() throws { + try validate() + guard try ScriptWidgetPackageFiles.directoryReceipt(root: container.deletingLastPathComponent(), relative: container.lastPathComponent) == rootReceipt else { throw ScriptWidgetCloudJSONError.identityChanged } + } + private func transaction(_ operation: () throws -> T) throws -> T { + try check() + guard let result = ScriptWidgetStorageLock.withExclusiveAccess(container: container, namespace: "CloudJSON.v1.\(owner)", operation: { Result { try check(); return try operation() } }) else { throw ScriptWidgetCloudJSONError.unavailable } + return try result.get() + } + /// A native grant lock protects reconnects; the account/store lock also + /// serializes two installed widgets explicitly connected to the same cache. + /// Lock order is always owner, then store, across app and extension processes. + private func connectedTransaction(_ operation: (ScriptWidgetCloudJSONGrant) throws -> T) throws -> T { + try transaction { + let value = try grant() + guard let result = ScriptWidgetStorageLock.withExclusiveAccess(container: container, namespace: "CloudJSON.v1.store.\(value.account).\(value.store)", operation: { Result { try check(); return try operation(value) } }) else { throw ScriptWidgetCloudJSONError.unavailable } + return try result.get() + } + } + private func grant() throws -> ScriptWidgetCloudJSONGrant { + guard ScriptWidgetPackageFiles.kind(root: container, relative: grantPath) != nil else { throw ScriptWidgetCloudJSONError.notConnected } + guard let data = ScriptWidgetPackageFiles.read(root: container, relative: grantPath, maximumBytes: 2048), + let value = try? JSONDecoder().decode(ScriptWidgetCloudJSONGrant.self, from: data), value.version == 1, + UUID(uuidString: value.store)?.uuidString.lowercased() == value.store, + value.account.range(of: "^[a-f0-9]{64}$", options: .regularExpression) != nil, + try Self.canonical(JSONSerialization.jsonObject(with: data)) == Self.canonical(JSONSerialization.jsonObject(with: JSONEncoder().encode(value))) else { throw ScriptWidgetCloudJSONError.invalidData } + guard environment().account == value.account else { throw ScriptWidgetCloudJSONError.accountChanged } + return value + } + private func local(_ grant: ScriptWidgetCloudJSONGrant) -> String { "CloudJSON.v1/cache/\(grant.account)/\(grant.store)" } + private func cloud(_ grant: ScriptWidgetCloudJSONGrant) throws -> (URL, String) { + let current = environment() + guard current.account == grant.account else { throw ScriptWidgetCloudJSONError.accountChanged } + guard let root = current.documents else { throw ScriptWidgetCloudJSONError.unavailable } + return (root, "WidgetData/\(grant.store)") + } + + /// App UI only. A package never supplies a store ID to a JavaScript API. + func connect(existingStore: String? = nil) throws { + try transaction { + let env = environment() + guard let account = env.account, account.range(of: "^[a-f0-9]{64}$", options: .regularExpression) != nil, let root = env.documents else { throw ScriptWidgetCloudJSONError.unavailable } + let id = existingStore ?? UUID().uuidString.lowercased() + guard UUID(uuidString: id)?.uuidString.lowercased() == id else { throw ScriptWidgetCloudJSONError.invalidName } + let path = "WidgetData/\(id)" + if existingStore != nil { + guard ScriptWidgetPackageFiles.kind(root: root, relative: path) == S_IFDIR else { throw ScriptWidgetCloudJSONError.unavailable } + } else { + guard ScriptWidgetPackageFiles.ensureDirectory(root: root, relative: path) else { throw ScriptWidgetCloudJSONError.unavailable } + } + let value = ScriptWidgetCloudJSONGrant(version: 1, store: id, account: account) + try check() + guard environment().account == account, + ScriptWidgetPackageFiles.ensureDirectory(root: container, relative: local(value)), + ScriptWidgetPackageFiles.write(try JSONEncoder().encode(value), root: container, relative: grantPath) else { throw ScriptWidgetCloudJSONError.unavailable } + } + } + /// Revoke only this installation's authority; preserve all record files. + func disconnect() throws { + try transaction { + guard ScriptWidgetPackageFiles.removeTree(root: container, relative: grantPath) else { throw ScriptWidgetCloudJSONError.unavailable } + } + } + func connection() throws -> String { try transaction { try grant().store } } + func availableStores() throws -> [String] { + try check() + guard let root = environment().documents else { throw ScriptWidgetCloudJSONError.unavailable } + if ScriptWidgetPackageFiles.kind(root: root, relative: "WidgetData") == nil { return [] } + guard let names = ScriptWidgetPackageFiles.list(root: root, relative: "WidgetData", maximumCount: 128) else { throw ScriptWidgetCloudJSONError.limitExceeded } + return names.filter { UUID(uuidString: $0)?.uuidString.lowercased() == $0 && ScriptWidgetPackageFiles.kind(root: root, relative: "WidgetData/\($0)") == S_IFDIR }.sorted() + } + + private struct Revision { + let id: String + let name: String + let parents: [String] + let value: [String: Any] + let bytes: Data + } + private func decode(_ bytes: Data, id: String, name: String) throws -> Revision { + guard id.range(of: "^[a-f0-9]{64}$", options: .regularExpression) != nil, + bytes.count <= Self.maximumValueBytes + 64 * 1024, Self.hash(bytes) == id, + let raw = try JSONSerialization.jsonObject(with: bytes) as? [String: Any], Set(raw.keys) == ["formatVersion", "name", "parents", "value"], + String(data: try Self.canonical(raw), encoding: .utf8) == String(data: bytes, encoding: .utf8), + (raw["formatVersion"] as? Int) == 1, raw["name"] as? String == name, + let parents = raw["parents"] as? [String], parents.count <= Self.maximumRevisions, parents == parents.sorted(), Set(parents).count == parents.count, + parents.allSatisfy({ $0 != id && $0.range(of: "^[a-f0-9]{64}$", options: .regularExpression) != nil }), + let value = raw["value"] as? [String: Any], try Self.canonical(value).count <= Self.maximumValueBytes else { throw ScriptWidgetCloudJSONError.invalidData } + guard try Self.canonical(["formatVersion": 1, "name": name, "parents": parents, "value": value]) == bytes else { throw ScriptWidgetCloudJSONError.invalidData } + return .init(id: id, name: name, parents: parents, value: value, bytes: bytes) + } + private func inventory(root: URL, path: String) throws -> [Revision] { + if ScriptWidgetPackageFiles.kind(root: root, relative: path) == nil { + // A dangling link/placeholder is not an absent empty store. + guard ScriptWidgetPackageFiles.safeURL(root: root, relative: path) != nil else { throw ScriptWidgetCloudJSONError.invalidData } + return [] + } + guard let files = ScriptWidgetPackageFiles.list(root: root, relative: path, maximumCount: 128) else { throw ScriptWidgetCloudJSONError.limitExceeded } + var result: [Revision] = []; var total = 0 + for name in files { + if name == ".DS_Store", ScriptWidgetPackageFiles.kind(root: root, relative: path + "/" + name) == S_IFREG { continue } + guard Self.validName(name), ScriptWidgetPackageFiles.kind(root: root, relative: path + "/" + name) == S_IFDIR, + let revisions = ScriptWidgetPackageFiles.list(root: root, relative: path + "/" + name, maximumCount: Self.maximumRevisions) else { throw ScriptWidgetCloudJSONError.invalidData } + for file in revisions { + if file == ".DS_Store", ScriptWidgetPackageFiles.kind(root: root, relative: path + "/" + name + "/" + file) == S_IFREG { continue } + if file.hasPrefix(".revision-"), UUID(uuidString: String(file.dropFirst(10))) != nil { continue } + if file.range(of: "^\\.[a-f0-9]{64}\\.json\\.icloud$", options: .regularExpression) != nil { + if let url = ScriptWidgetPackageFiles.safeURL(root: root, relative: path + "/" + name + "/" + String(file.dropFirst().dropLast(7))) { + try? FileManager.default.startDownloadingUbiquitousItem(at: url) + } + throw ScriptWidgetCloudJSONError.unavailable + } + guard result.count < Self.maximumRevisions, file.hasSuffix(".json"), + let data = ScriptWidgetPackageFiles.read(root: root, relative: path + "/" + name + "/" + file, maximumBytes: Self.maximumValueBytes + 64 * 1024) else { throw ScriptWidgetCloudJSONError.unavailable } + total += data.count + guard total <= Self.maximumBytes else { throw ScriptWidgetCloudJSONError.limitExceeded } + result.append(try decode(data, id: String(file.dropLast(5)), name: name)) + } + } + return result + } + private func heads(_ revisions: [Revision], name: String) throws -> [Revision] { + let selected = revisions.filter { $0.name == name }; let ids = Set(selected.map(\.id)) + guard selected.allSatisfy({ Set($0.parents).isSubset(of: ids) }) else { throw ScriptWidgetCloudJSONError.unavailable } + // Every parent predates its descendant by construction; hostile graphs + // cannot create a cycle without a SHA-256 fixed point. + let parents = Set(selected.flatMap(\.parents)) + return selected.filter { !parents.contains($0.id) }.sorted { $0.id < $1.id } + } + private func token(_ heads: [Revision]) -> String { heads.isEmpty ? "" : Self.hash(Data(heads.map(\.id).joined(separator: "\n").utf8)) } + private func publish(_ revision: Revision, root: URL, path: String, createStore: Bool = true) throws { + let target = path + "/" + revision.name + "/" + revision.id + ".json" + if let existing = ScriptWidgetPackageFiles.read(root: root, relative: target, maximumBytes: Self.maximumValueBytes + 64 * 1024) { + guard existing == revision.bytes else { throw ScriptWidgetCloudJSONError.invalidData }; return + } + // Open the existing store before creating its document directory. + // Remote stores are never recreated after deletion, including a race + // between coordination and publication. No ancestor follows a link. + let written = ScriptWidgetPackageFiles.withParent(root: root, relative: path + "/" + revision.name, create: createStore) { store, document -> Bool? in + guard mkdirat(store, document, 0o700) == 0 || errno == EEXIST else { return false } + let directory = openat(store, document, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC) + guard directory >= 0 else { return false } + defer { close(directory) } + guard ScriptWidgetPackageFiles.preserveWidgetProtection(directory) else { return false } + let name = revision.id + ".json" + let temporary = ".revision-" + UUID().uuidString + let fd = openat(directory, temporary, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, 0o600) + guard fd >= 0 else { return false } + defer { close(fd); _ = unlinkat(directory, temporary, 0) } + let saved = revision.bytes.withUnsafeBytes { bytes -> Bool in + var offset = 0 + while offset < bytes.count { + let count = Darwin.write(fd, bytes.baseAddress!.advanced(by: offset), bytes.count - offset) + if count < 0 && errno == EINTR { continue } + guard count > 0 else { return false }; offset += count + } + return true + } + guard ScriptWidgetPackageFiles.preserveWidgetProtection(fd), saved, fsync(fd) == 0 else { return false } + // Atomic, exclusive publication: never overwrite an existing revision. + if renameatx_np(directory, temporary, directory, name, UInt32(RENAME_EXCL)) == 0 { return fsync(directory) == 0 } + return errno == EEXIST && ScriptWidgetPackageFiles.read(root: root, relative: target, maximumBytes: Self.maximumValueBytes + 64 * 1024) == revision.bytes + } + guard written == true else { throw ScriptWidgetCloudJSONError.unavailable } + } + func status() throws -> [String: Any] { + try transaction { + let g = try grant() + return ["state": environment().documents == nil ? "unavailable" : "connected", "store": g.store, + "uploaded": NSNull(), "uploadState": "unknown", "conflictPolicy": "preserveAllVersions"] + } + } + private func response(_ items: [Revision], name: String, pending: Bool) throws -> [String: Any] { + let values = try heads(items, name: name) + return ["value": values.count == 1 ? values[0].value : NSNull(), "revision": token(values), + "state": values.count > 1 ? "conflict" : pending ? "pending" : "cached", + "conflicts": values.count > 1 ? values.map { ["id": $0.id, "value": $0.value] as [String: Any] } : []] + } + func read(_ name: String) throws -> [String: Any] { + guard Self.validName(name) else { throw ScriptWidgetCloudJSONError.invalidName } + return try connectedTransaction { g in + let items = try inventory(root: container, path: local(g)) + // cached is deliberately NOT a claim of upload or convergence. + return try response(items, name: name, pending: false) + } + } + func write(_ name: String, value: [String: Any], expected: String, resolving: Bool = false) throws -> [String: Any] { + guard Self.validName(name) else { throw ScriptWidgetCloudJSONError.invalidName } + return try connectedTransaction { g in + let path = local(g); let items = try inventory(root: container, path: path); let current = try heads(items, name: name) + guard token(current) == expected else { throw ScriptWidgetCloudJSONError.staleRevision } + guard resolving || current.count <= 1 else { throw ScriptWidgetCloudJSONError.conflict } + guard try Self.canonical(value).count <= Self.maximumValueBytes, items.count < Self.maximumRevisions, + items.contains(where: { $0.name == name }) || Set(items.map(\.name)).count < 128, + items.reduce(0, { $0 + $1.bytes.count }) + Self.maximumValueBytes + 64 * 1024 <= Self.maximumBytes else { throw ScriptWidgetCloudJSONError.limitExceeded } + let data = try Self.canonical(["formatVersion": 1, "name": name, "parents": current.map(\.id), "value": value]) + let revision = try decode(data, id: Self.hash(data), name: name) + try check(); try publish(revision, root: container, path: path) + return try response(items + [revision], name: name, pending: true) + } + } + /// Explicit bounded synchronization. The file daemon uploads asynchronously; + /// this result means the currently materialized files were exchanged only. + func synchronize() throws -> [String: Any] { + try connectedTransaction { g in + let (root, remote) = try cloud(g); let path = local(g) + guard ScriptWidgetPackageFiles.kind(root: root, relative: remote) == S_IFDIR else { throw ScriptWidgetCloudJSONError.unavailable } + let cached = try inventory(root: container, path: path) + let incoming = try inventory(root: root, path: remote) + let merged = Dictionary((cached + incoming).map { ($0.id, $0) }, uniquingKeysWith: { a, _ in a }).values.sorted { $0.id < $1.id } + guard merged.count <= Self.maximumRevisions, Set(merged.map(\.name)).count <= 128, merged.reduce(0, { $0 + $1.bytes.count }) <= Self.maximumBytes else { throw ScriptWidgetCloudJSONError.limitExceeded } + try check() + guard environment().account == g.account else { throw ScriptWidgetCloudJSONError.accountChanged } + // Copy remote revisions locally before publishing any outgoing data. + // A malformed or oversized remote revision aborts without replacing it. + for revision in merged { try publish(revision, root: container, path: path) } + let coordinator = NSFileCoordinator() + var coordinationError: NSError?; var failure: Error? + coordinator.coordinate(writingItemAt: root.appendingPathComponent(remote), options: [], error: &coordinationError) { _ in + do { + try self.check() + guard self.environment().account == g.account else { throw ScriptWidgetCloudJSONError.accountChanged } + for revision in merged { + try self.check() + guard self.environment().account == g.account else { throw ScriptWidgetCloudJSONError.accountChanged } + try self.publish(revision, root: root, path: remote, createStore: false) + } + } catch { failure = error } + } + if let failure { throw failure }; if coordinationError != nil { throw ScriptWidgetCloudJSONError.unavailable } + return ["state": "pending", "materializedRevisions": merged.count, "downloadedRevisions": incoming.filter { incoming in !cached.contains { $0.id == incoming.id } }.count, "uploaded": NSNull()] + } + } +} diff --git a/Shared/ScriptWidgetRuntime/Common/ScriptWidgetCloudJSONView.swift b/Shared/ScriptWidgetRuntime/Common/ScriptWidgetCloudJSONView.swift new file mode 100644 index 00000000..a764bb79 --- /dev/null +++ b/Shared/ScriptWidgetRuntime/Common/ScriptWidgetCloudJSONView.swift @@ -0,0 +1,95 @@ +import SwiftUI +import WidgetKit + +/// Explicit native grants are independent of scripts, manifests and exports. +struct ScriptWidgetCloudJSONView: View { + let package: ScriptWidgetPackage + @Environment(\.dismiss) private var dismiss + @State private var stores: [String] = [] + @State private var connected = "" + @State private var message = "" + @State private var busy = false + @State private var pendingStore: String? + @State private var confirmConnection = false + + static func store(for package: ScriptWidgetPackage) throws -> ScriptWidgetCloudJSONStore { + guard let scope = package.managedScope, let binding = scope.binding, + package.readManifest()?.permissions.contains(.icloud) == true else { throw ScriptWidgetCloudJSONError.notConnected } + try scope.validate(name: package.name) + return try ScriptWidgetCloudJSONStore(container: scope.context.container, owner: binding.ownerID) { try scope.validate(name: package.name) } + } + + private static let refreshLock = NSLock() + private static var lastRefresh = Date.distantPast + static func refreshConnectedPackages() { + refreshLock.lock() + guard Date().timeIntervalSince(lastRefresh) >= 15 else { refreshLock.unlock(); return } + lastRefresh = Date(); refreshLock.unlock() + ScriptWidgetCloudJSONStore.queue.async { + guard !ScriptWidgetApplicationTestHost.isActive else { return } + for model in sharedScriptManager.listScripts().prefix(64) { + guard model.package.readManifest()?.permissions.contains(.icloud) == true, + let store = try? Self.store(for: model.package), (try? store.connection()) != nil else { continue } + if let result = try? store.synchronize(), (result["downloadedRevisions"] as? Int ?? 0) > 0 { WidgetCenter.shared.reloadAllTimelines() } + } + } + } + + var body: some View { + NavigationStack { + Form { + Section("iCloud JSON Files") { + Text(package.name).font(.headline) + Text("Only this installed widget can use its connected data store. Existing local records are not moved. Copies require their own connection.") + if !connected.isEmpty { + Text("Connected folder: WidgetData/\(connected)").textSelection(.enabled) + Button("Disconnect", role: .destructive) { run { try $0.disconnect(); return "Disconnected. Cloud files and cached records are preserved." } } + Button("Sync Now") { run { _ = try $0.synchronize(); return "Local revisions exchanged. iCloud uploads asynchronously; this does not confirm upload or another device receiving them." } } + } + Button("Create a New Data Store") { pendingStore = nil; confirmConnection = true } + } + Section("Connect an Existing Data Store") { + Text("To share records with another device, select the same folder ID on both devices. Connecting grants this widget read and write access to that store. Imported widgets never connect automatically.") + ForEach(stores, id: \.self) { id in + Button(id) { pendingStore = id; confirmConnection = true }.font(.caption.monospaced()) + } + Button("Refresh Stores") { refresh() } + } + Section("Files and Conflicts") { + Text("Find JSON revisions in Files → iCloud Drive → ScriptWidget → WidgetData. Each named document has immutable revision files containing value and parents. You may inspect or copy them; editing or deleting revision files directly is not supported. Conflicting versions remain available to readJSON and must be resolved explicitly.") + Text("The API never promises immediate sync. No iCloud account, offline files, quotas and system background limits may delay delivery. Keep a backup before changing records.") + if !message.isEmpty { Text(message).accessibilityIdentifier("icloud-json-message") } + if busy { ProgressView() } + } + } + .disabled(busy) + .navigationTitle("iCloud Data") + .toolbar { ToolbarItem(placement: .confirmationAction) { Button("Done") { dismiss() } } } + .confirmationDialog("Allow this widget to access this iCloud data store?", isPresented: $confirmConnection, titleVisibility: .visible) { + Button("Connect") { + let selected = pendingStore + run { try $0.connect(existingStore: selected); return "Connected. Existing local records have not been migrated." } + } + Button("Cancel", role: .cancel) { } + } message: { Text(pendingStore ?? "A new empty store will be created. No existing local records will be moved or deleted.") } + .task { refresh() } + } + #if os(macOS) + .frame(minWidth: 560, minHeight: 520) + #endif + } + private func refresh() { run { _ in "" } } + private func run(_ operation: @escaping (ScriptWidgetCloudJSONStore) throws -> String) { + guard !busy else { return }; busy = true + ScriptWidgetCloudJSONStore.queue.async { + var items: [String] = []; var connection = ""; var result = "" + do { + let store = try Self.store(for: package) + result = try operation(store) + items = try store.availableStores() + connection = (try? store.connection()) ?? "" + } catch { result = (error as? LocalizedError)?.errorDescription ?? "iCloud data is unavailable. Existing records are preserved." } + DispatchQueue.main.async { stores = items; connected = connection; message = result; busy = false } + } + } +} diff --git a/Shared/ScriptWidgetRuntime/Common/ScriptWidgetPackage.swift b/Shared/ScriptWidgetRuntime/Common/ScriptWidgetPackage.swift index e7c44c8c..6fcfa8d5 100644 --- a/Shared/ScriptWidgetRuntime/Common/ScriptWidgetPackage.swift +++ b/Shared/ScriptWidgetRuntime/Common/ScriptWidgetPackage.swift @@ -433,7 +433,7 @@ struct ScriptWidgetBuildManifest: Codable, Equatable { enum ScriptWidgetBuildContract { static let runtimeAPIVersion = "1.0" - static let nativeRuntimeAPIVersion = "1.4" + static let nativeRuntimeAPIVersion = "1.5" static let compilerFingerprint = "scriptwidget-babel-v3" } @@ -671,7 +671,7 @@ enum ScriptWidgetPackageFiles { }() #endif - private static func preserveWidgetProtection(_ fd: Int32) -> Bool { + static func preserveWidgetProtection(_ fd: Int32) -> Bool { #if os(iOS) && !targetEnvironment(simulator) guard let value = unprotectedClass else { return false } return fcntl(fd, F_SETPROTECTIONCLASS, value) == 0 diff --git a/Shared/ScriptWidgetRuntime/Common/ScriptWidgetStorageUpgradeView.swift b/Shared/ScriptWidgetRuntime/Common/ScriptWidgetStorageUpgradeView.swift index b4ca33f4..4f3508b9 100644 --- a/Shared/ScriptWidgetRuntime/Common/ScriptWidgetStorageUpgradeView.swift +++ b/Shared/ScriptWidgetRuntime/Common/ScriptWidgetStorageUpgradeView.swift @@ -366,6 +366,7 @@ struct ScriptWidgetLibraryLaunchView: View { guard scenePhase == .active else { return } while !Task.isCancelled { await model.refreshRestoredPackages() + ScriptWidgetCloudJSONView.refreshConnectedPackages() do { try await Task.sleep(nanoseconds: 3_000_000_000) } catch { return } } diff --git a/Shared/ScriptWidgetRuntime/Resource/widget.schema.json b/Shared/ScriptWidgetRuntime/Resource/widget.schema.json index 042c3b9b..afb4b19a 100644 --- a/Shared/ScriptWidgetRuntime/Resource/widget.schema.json +++ b/Shared/ScriptWidgetRuntime/Resource/widget.schema.json @@ -27,7 +27,7 @@ }, "permissions": { "type": "array", "uniqueItems": true, - "items": { "enum": ["network", "location", "health", "calendar", "reminders", "remindersWrite", "photos", "services", "music", "storage", "files"] } + "items": { "enum": ["network", "location", "health", "calendar", "reminders", "remindersWrite", "photos", "services", "music", "storage", "icloud", "files"] } }, "networkDomains": { "type": "array", "uniqueItems": true, diff --git a/Shared/ScriptWidgetRuntime/Widget/API/ScriptWidgetRuntimeCloudJSON.swift b/Shared/ScriptWidgetRuntime/Widget/API/ScriptWidgetRuntimeCloudJSON.swift new file mode 100644 index 00000000..66062553 --- /dev/null +++ b/Shared/ScriptWidgetRuntime/Widget/API/ScriptWidgetRuntimeCloudJSON.swift @@ -0,0 +1,68 @@ +import Foundation +import JavaScriptCore +import WidgetKit + +@objc protocol ScriptWidgetRuntimeCloudJSONExports: JSExport { + static func readJSON(_ name: String) -> ScriptWidgetRuntimePromise + static func writeJSON(_ name: String, _ value: [String: Any], _ expectedRevision: String) -> ScriptWidgetRuntimePromise + static func resolveJSON(_ name: String, _ value: [String: Any], _ expectedRevision: String) -> ScriptWidgetRuntimePromise + static func sync() -> ScriptWidgetRuntimePromise + static func status() -> ScriptWidgetRuntimePromise +} + +@objc final class ScriptWidgetRuntimeCloudJSON: NSObject, ScriptWidgetRuntimeCloudJSONExports { + static func readJSON(_ name: String) -> ScriptWidgetRuntimePromise { perform { try $0.read(name) } } + static func writeJSON(_ name: String, _ value: [String: Any], _ expectedRevision: String) -> ScriptWidgetRuntimePromise { + perform(writing: true) { try $0.write(name, value: value, expected: expectedRevision) } + } + static func resolveJSON(_ name: String, _ value: [String: Any], _ expectedRevision: String) -> ScriptWidgetRuntimePromise { + perform(writing: true) { try $0.write(name, value: value, expected: expectedRevision, resolving: true) } + } + static func sync() -> ScriptWidgetRuntimePromise { perform(writing: true, synchronizeAfter: false) { try $0.synchronize() } } + static func status() -> ScriptWidgetRuntimePromise { perform { try $0.status() } } + + private static func perform(writing: Bool = false, synchronizeAfter: Bool = true, operation: @escaping (ScriptWidgetCloudJSONStore) throws -> [String: Any]) -> ScriptWidgetRuntimePromise { + let captured = JSContext.current()?.scriptWidgetRunningState + return ScriptWidgetRuntimePromise { resolve, reject in + guard let state = captured, state.isCurrentExecution, state.previewStorage == nil, let snapshot = state.executionSnapshot else { + reject.call(withArguments: ["[icloud:unavailable] Install the widget and connect iCloud JSON in the app. Native CLI and template previews cannot access iCloud."]) + return + } + guard let manifest = state.package.readManifest(), manifest.permissions.contains(.icloud), + WidgetPackageManifestValidator.validate(manifest, package: state.package, checkEntryExists: false).isValid else { + reject.call(withArguments: ["[permission_denied] Declare icloud in widget.json and connect this installation in the app."]); return + } + guard !state.isTimelineProjection, !state.isLiveActivityPresentation, + !writing || state.isUserInitiatedAction, state.executionSession.registerDataQuery() else { + reject.call(withArguments: ["[icloud:read_only] Writes and sync require a user action; future/Live Activity presentations cannot access iCloud JSON."]); return + } + // The bridge copies JSON-compatible arguments; validation and bounded I/O + // run on the native serial worker. + // All results return through the existing guarded promise delivery. + ScriptWidgetCloudJSONStore.queue.async { + let result = Result { + let store = try ScriptWidgetCloudJSONStore(container: snapshot.context.container, owner: snapshot.binding.ownerID) { + guard state.isCurrentExecution else { throw ScriptWidgetCloudJSONError.identityChanged } + } + let value = try operation(store) + // Once a local operation has committed, a separate native + // job may exchange immutable revisions after JS finishes. + // Its authority remains the captured installation, never a + // newly installed package with the same display name. + if synchronizeAfter { ScriptWidgetCloudJSONStore.queue.async { + guard let background = try? ScriptWidgetCloudJSONStore(container: snapshot.context.container, owner: snapshot.binding.ownerID, validate: { try snapshot.validateIdentity() }) else { return } + if let result = try? background.synchronize(), (result["downloadedRevisions"] as? Int ?? 0) > 0 { WidgetCenter.shared.reloadAllTimelines() } + } } + return value + } + state.executionSession.callbackQueue.async { + guard state.isCurrentExecution else { return } + switch result { + case .success(let value): resolve.call(withArguments: [value]) + case .failure(let error): reject.call(withArguments: [(error as? ScriptWidgetCloudJSONError)?.errorDescription ?? "[icloud:unavailable] Existing records are preserved."]) + } + } + } + } + } +} diff --git a/Shared/ScriptWidgetRuntime/Widget/Runtime/ScriptWidgetRuntime.swift b/Shared/ScriptWidgetRuntime/Widget/Runtime/ScriptWidgetRuntime.swift index e9678902..cff9968e 100644 --- a/Shared/ScriptWidgetRuntime/Widget/Runtime/ScriptWidgetRuntime.swift +++ b/Shared/ScriptWidgetRuntime/Widget/Runtime/ScriptWidgetRuntime.swift @@ -82,7 +82,7 @@ enum ScriptWidgetRuntimeContract { static let globalAPIs = [ "$calendar", "$component", "$console", "$device", "$dynamic_island", "$element", - "$error", "$fetch", "$file", "$getenv", "$health", "$http", "$import", + "$error", "$fetch", "$file", "$icloud", "$getenv", "$health", "$http", "$import", "$location", "$music", "$photos", "$reminders", "$render", "$runtime", "$services", "$storage", "$system", "$xml", "console", "fetch", ] @@ -525,6 +525,7 @@ class ScriptWidgetRuntime { runtimeContext["$device"] = ScriptWidgetRuntimeDevice.self runtimeContext["$http"] = ScriptWidgetRuntimeHttp.self runtimeContext["$file"] = ScriptWidgetRuntimeFile.self + runtimeContext["$icloud"] = ScriptWidgetRuntimeCloudJSON.self runtimeContext["$system"] = ScriptWidgetRuntimeSystem.self runtimeContext["$calendar"] = ScriptWidgetRuntimeCalendar.self runtimeContext["$photos"] = ScriptWidgetRuntimePhotos.self diff --git a/Skills/scriptwidget-creator/references/runtime-api.md b/Skills/scriptwidget-creator/references/runtime-api.md index 19c1d226..cd8a13cb 100644 --- a/Skills/scriptwidget-creator/references/runtime-api.md +++ b/Skills/scriptwidget-creator/references/runtime-api.md @@ -1,6 +1,6 @@ # ScriptWidget Runtime API -> Generated from Studio's static API metadata (schema v1, runtime 1.4). The native runtime switch is authoritative. Do not edit by hand. +> Generated from Studio's static API metadata (schema v1, runtime 1.5). The native runtime switch is authoritative. Do not edit by hand. ## Components @@ -885,6 +885,11 @@ Interactive widget toggle. A declared actionID requires a package-relative stora - `$location.isAuthorizedForWidgetUpdates`: Package 2.0 requires location permission. Request system access in the app. Widgets also require isAuthorizedForWidgetUpdates. Cache is rechecked against authorization and precision, discarded after 24 hours or revocation, and reports age/isStale. - `$location.requestAuthorization`: Package 2.0 requires location permission. Request system access in the app. Widgets also require isAuthorizedForWidgetUpdates. Cache is rechecked against authorization and precision, discarded after 24 hours or revocation, and reports age/isStale. - `$location.current`: Package 2.0 requires location permission. Request system access in the app. Widgets also require isAuthorizedForWidgetUpdates. Cache is rechecked against authorization and precision, discarded after 24 hours or revocation, and reports age/isStale. +- `$icloud.readJSON`: Read cached JSON and revision; conflicts preserve all heads. Requires icloud permission and a native connection. Never certifies upload. +- `$icloud.writeJSON`: Save a new immutable JSON revision locally from a user action. Requires the revision from readJSON. Background exchange is eventual. +- `$icloud.resolveJSON`: Explicitly resolve all observed conflicting heads into a new revision. Stale tokens fail; originals remain. +- `$icloud.sync`: Exchange materialized revisions with iCloud; requires a user action. Upload completion is unknown. +- `$icloud.status`: Read native connection state. Cloud store IDs are granted in the app, never by the script. Web uses preview mocks. - `$file.exists`: Return whether a regular file is locally present inside this package, including binary assets. Missing files, directories, unsafe paths and links return false. Does not download iCloud files or read outside the package. Web preview uses workspace file presence. - `$file.read`: Read package-relative UTF-8 text. Missing or unavailable files return an empty string. - `$file.readJSON`: Read a package-relative JSON object. diff --git a/Tests/ScriptWidgetRuntimeTests/PackageCacheTests.swift b/Tests/ScriptWidgetRuntimeTests/PackageCacheTests.swift index fd66d015..1dbe7113 100644 --- a/Tests/ScriptWidgetRuntimeTests/PackageCacheTests.swift +++ b/Tests/ScriptWidgetRuntimeTests/PackageCacheTests.swift @@ -618,3 +618,177 @@ final class PackageBoundaryTests: XCTestCase { XCTAssertEqual(ScriptWidgetPackageFiles.read(root: root, relative: "Resource.txt"), Data("safe".utf8)) } } + +final class CloudJSONTests: XCTestCase { + private var root: URL! + private var cloud: URL! + private var account = String(repeating: "a", count: 64) + private var cloudAvailable = true + private var active = true + override func setUpWithError() throws { + root = FileManager.default.temporaryDirectory.appendingPathComponent("CloudJSONTests-" + UUID().uuidString) + cloud = root.appendingPathComponent("Cloud") + try FileManager.default.createDirectory(at: cloud, withIntermediateDirectories: true) + } + override func tearDownWithError() throws { try FileManager.default.removeItem(at: root) } + private func device(_ name: String) throws -> ScriptWidgetCloudJSONStore { + let local = root.appendingPathComponent(name) + try FileManager.default.createDirectory(at: local, withIntermediateDirectories: true) + return try ScriptWidgetCloudJSONStore(container: local, owner: UUID().uuidString.lowercased(), environment: { [self] in + .init(account: account, documents: cloudAvailable ? cloud : nil) + }, validate: { [self] in if !active { throw ScriptWidgetCloudJSONError.identityChanged } }) + } + private func object(_ response: [String: Any]) throws -> [String: Any] { try XCTUnwrap(response["value"] as? [String: Any]) } + func testLocalFirstOfflineCacheAndTwoDeviceRoundTrip() throws { + let a = try device("A"), b = try device("B") + XCTAssertThrowsError(try a.read("habits.json")) + try a.connect(); try b.connect(existingStore: a.connection()) + cloudAvailable = false + let written = try a.write("habits.json", value: ["read": ["2026-10-26"]], expected: "") + XCTAssertEqual(written["state"] as? String, "pending") + XCTAssertEqual(try object(a.read("habits.json"))["read"] as? [String], ["2026-10-26"]) + XCTAssertThrowsError(try a.synchronize()) + cloudAvailable = true + _ = try a.synchronize(); _ = try b.synchronize() + XCTAssertEqual(try object(b.read("habits.json"))["read"] as? [String], ["2026-10-26"]) + XCTAssertTrue(try a.status()["uploaded"] is NSNull) + } + func testDisconnectedWritersRetainBothVersionsAndExplicitResolution() throws { + let a = try device("A"), b = try device("B") + try a.connect(); try b.connect(existingStore: a.connection()) + let original = try a.write("habits.json", value: ["days": []], expected: "") + _ = try a.synchronize(); _ = try b.synchronize() + let base = try XCTUnwrap(original["revision"] as? String) + cloudAvailable = false + _ = try a.write("habits.json", value: ["days": ["2026-10-25"]], expected: base) + _ = try b.write("habits.json", value: ["days": ["2026-10-26"]], expected: base) + cloudAvailable = true + _ = try a.synchronize(); _ = try b.synchronize(); _ = try a.synchronize() + let conflict = try a.read("habits.json") + XCTAssertEqual(conflict["state"] as? String, "conflict") + XCTAssertEqual((conflict["conflicts"] as? [Any])?.count, 2) + let revision = try XCTUnwrap(conflict["revision"] as? String) + XCTAssertThrowsError(try a.write("habits.json", value: ["lost": true], expected: revision)) + XCTAssertThrowsError(try a.write("habits.json", value: [:], expected: base, resolving: true)) + _ = try a.write("habits.json", value: ["days": ["2026-10-25", "2026-10-26"]], expected: revision, resolving: true) + _ = try a.synchronize(); _ = try b.synchronize() + XCTAssertEqual((try object(b.read("habits.json"))["days"] as? [String])?.count, 2) + let files = try FileManager.default.contentsOfDirectory(atPath: cloud.appendingPathComponent("WidgetData/\(try a.connection())/habits.json").path) + XCTAssertEqual(files.count, 4, "Original, both branches and resolution must all remain") + } + func testNamesQuotasStaleWritesAndIdentityRetirement() throws { + let a = try device("A"); try a.connect() + for name in ["../x.json", "/x.json", "X.json", "a/b.json", "a\\b.json", "a.JSON", ".json"] { + XCTAssertThrowsError(try a.write(name, value: [:], expected: ""), name) + } + let first = try a.write("note.json", value: ["keep": true], expected: "") + XCTAssertThrowsError(try a.write("note.json", value: ["lost": true], expected: "")) + XCTAssertThrowsError(try a.write("huge.json", value: ["huge": String(repeating: "日", count: 100_000)], expected: "")) + XCTAssertEqual(try a.read("note.json")["revision"] as? String, first["revision"] as? String) + active = false + XCTAssertThrowsError(try a.read("note.json")); XCTAssertThrowsError(try a.synchronize()) + } + func testAccountSwitchAndNewInstallationCannotReadOldCache() throws { + let a = try device("A"); try a.connect(); _ = try a.write("private.json", value: ["keep": true], expected: "") + let fresh = try device("A") + XCTAssertThrowsError(try fresh.read("private.json")) + account = String(repeating: "b", count: 64) + XCTAssertThrowsError(try a.read("private.json")); XCTAssertThrowsError(try a.synchronize()) + account = String(repeating: "a", count: 64) + XCTAssertEqual(try object(a.read("private.json"))["keep"] as? Bool, true) + } + func testTamperedRemoteRevisionAndSymlinkNeverOverwriteHistory() throws { + let a = try device("A"); try a.connect(); _ = try a.write("note.json", value: ["keep": true], expected: ""); _ = try a.synchronize() + let id = try a.connection() + let directory = cloud.appendingPathComponent("WidgetData/\(id)/note.json") + let revision = try XCTUnwrap(FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil).first) + let original = try Data(contentsOf: revision) + try Data("{}".utf8).write(to: revision) + XCTAssertThrowsError(try a.synchronize()) + XCTAssertEqual(try object(a.read("note.json"))["keep"] as? Bool, true) + XCTAssertEqual(try Data(contentsOf: revision), Data("{}".utf8)) + try original.write(to: revision) + try FileManager.default.removeItem(at: revision) + let external = root.appendingPathComponent("outside.json"); try Data("private".utf8).write(to: external) + try FileManager.default.createSymbolicLink(at: revision, withDestinationURL: external) + XCTAssertThrowsError(try a.synchronize()); XCTAssertEqual(try String(contentsOf: external, encoding: .utf8), "private") + } + func testCloudFolderRemovalDoesNotResurrectDeletedStore() throws { + let a = try device("A"); try a.connect(); _ = try a.write("note.json", value: ["keep": true], expected: "") + let folder = cloud.appendingPathComponent("WidgetData/\(try a.connection())") + try FileManager.default.removeItem(at: folder) + XCTAssertThrowsError(try a.synchronize()) + XCTAssertFalse(FileManager.default.fileExists(atPath: folder.path)) + XCTAssertEqual(try object(a.read("note.json"))["keep"] as? Bool, true) + } +} + + +extension CloudJSONTests { + func testDisconnectRevokesOnlyGrantAndReconnectionPreservesRecords() throws { + let a = try device("A"); try a.connect() + let id = try a.connection() + _ = try a.write("note.json", value: ["keep": true], expected: "") + _ = try a.synchronize() + try a.disconnect() + XCTAssertThrowsError(try a.read("note.json")) + XCTAssertThrowsError(try a.write("note.json", value: [:], expected: "")) + try a.connect(existingStore: id) + XCTAssertEqual(try object(a.read("note.json"))["keep"] as? Bool, true) + } +} + + +extension CloudJSONTests { + func testEvictedLocalCacheIsRestoredFromCloudWithoutChangingGrant() throws { + let a = try device("A"); try a.connect() + _ = try a.write("note.json", value: ["keep": true], expected: "") + _ = try a.synchronize() + try FileManager.default.removeItem(at: root.appendingPathComponent("A/CloudJSON.v1/cache")) + XCTAssertTrue(try a.read("note.json")["value"] is NSNull) + _ = try a.synchronize() + XCTAssertEqual(try object(a.read("note.json"))["keep"] as? Bool, true) + } + func testHardLinkedRevisionFailsClosedWithoutChangingExternalFile() throws { + let a = try device("A"); try a.connect() + _ = try a.write("note.json", value: ["keep": true], expected: "") + _ = try a.synchronize() + let directory = cloud.appendingPathComponent("WidgetData/\(try a.connection())/note.json") + let revision = try XCTUnwrap(FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil).first) + let bytes = try Data(contentsOf: revision) + let external = root.appendingPathComponent("hardlink.json") + try FileManager.default.linkItem(at: revision, to: external) + XCTAssertThrowsError(try a.synchronize()) + XCTAssertEqual(try Data(contentsOf: external), bytes) + XCTAssertEqual(try object(a.read("note.json"))["keep"] as? Bool, true) + } + func testTwoNativeGrantsShareOneStoreLock() throws { + let a = try device("A"), b = try device("A") + try a.connect(); try b.connect(existingStore: a.connection()) + let container = root.appendingPathComponent("A") + let id = try a.connection() + let held = ScriptWidgetStorageLock.withExclusiveAccess(container: container, namespace: "CloudJSON.v1.store.\(account).\(id)") { + let result = Result { try b.write("note.json", value: ["lost": true], expected: "") } + if case .success = result { XCTFail("A second native grant must respect the shared store lock") } + return true + } + XCTAssertEqual(held, true) + _ = try a.write("note.json", value: ["keep": true], expected: "") + XCTAssertThrowsError(try b.write("note.json", value: ["lost": true], expected: "")) + XCTAssertEqual(try object(b.read("note.json"))["keep"] as? Bool, true) + } +} + + +extension CloudJSONTests { + func testFinderMetadataDoesNotBlockInspectableCloudFiles() throws { + let a = try device("A"); try a.connect() + _ = try a.write("note.json", value: ["keep": true], expected: "") + _ = try a.synchronize() + let directory = cloud.appendingPathComponent("WidgetData/\(try a.connection())") + try Data([0, 1]).write(to: directory.appendingPathComponent(".DS_Store")) + try Data([0, 1]).write(to: directory.appendingPathComponent("note.json/.DS_Store")) + _ = try a.synchronize() + XCTAssertEqual(try object(a.read("note.json"))["keep"] as? Bool, true) + } +} diff --git a/Tests/ScriptWidgetRuntimeTests/RuntimeExecutionTests.swift b/Tests/ScriptWidgetRuntimeTests/RuntimeExecutionTests.swift index 19dbe847..a0476154 100644 --- a/Tests/ScriptWidgetRuntimeTests/RuntimeExecutionTests.swift +++ b/Tests/ScriptWidgetRuntimeTests/RuntimeExecutionTests.swift @@ -5806,3 +5806,19 @@ extension LibrarySessionExecutionTests { } #endif } + +extension RuntimeExecutionTests { + func testICloudAPICannotAccessUnmanagedOrPreviewPackages() throws { + for readonly in [false, true] { + let temp = makeTempPackage() + defer { try? FileManager.default.removeItem(at: temp.path) } + let package = ScriptWidgetPackage(path: temp.path, readonly: readonly) + let runtime = ScriptWidgetRuntime(package: package, environments: ["widget-size": "medium"]) + let result = runtime.executeJSXSyncForWidget("try { await $icloud.readJSON('note.json'); $render(unexpected); } catch(e) { $render({String(e)}); }") + XCTAssertNil(result.1) + let tree = try XCTUnwrap(result.0) + XCTAssertTrue(snapshot(tree).contains("icloud:unavailable")) + XCTAssertFalse(snapshot(tree).contains("unexpected")) + } + } +} diff --git a/Tests/ScriptWidgetRuntimeTests/RuntimeUnitTests.swift b/Tests/ScriptWidgetRuntimeTests/RuntimeUnitTests.swift index 282fc4e6..68150bef 100644 --- a/Tests/ScriptWidgetRuntimeTests/RuntimeUnitTests.swift +++ b/Tests/ScriptWidgetRuntimeTests/RuntimeUnitTests.swift @@ -390,7 +390,7 @@ final class ScriptWidgetErrorTests: XCTestCase { final class RuntimeContractTests: XCTestCase { func testPublicAPIVersionAndGlobalsAreFixed() { - XCTAssertEqual(ScriptWidgetRuntimeContract.apiVersion, "1.4") + XCTAssertEqual(ScriptWidgetRuntimeContract.apiVersion, "1.5") XCTAssertEqual(ScriptWidgetRuntimeContract.globalAPIs, ScriptWidgetRuntimeContract.globalAPIs.sorted()) XCTAssertTrue(ScriptWidgetRuntimeContract.globalAPIs.contains("$render")) XCTAssertTrue(ScriptWidgetRuntimeContract.globalAPIs.contains("$runtime")) @@ -2150,8 +2150,8 @@ final class CalendarSourceSelectionTests: XCTestCase { final class WidgetRuntimeRequirementTests: XCTestCase { func testOnlyCanonicalSameMajorRequirementsAreSupported() { - for value in ["1.0", "1.1", "1.2", "1.3", "1.4"] { XCTAssertTrue(WidgetRuntimeRequirement.supports(value), value) } - for value in ["1.5", "1.10", "2.0", "0.9", "01.3", "1.03", "1.3.0", "1", "1.9999", ">=1.3", " 1.3"] { + for value in ["1.0", "1.1", "1.2", "1.3", "1.4", "1.5"] { XCTAssertTrue(WidgetRuntimeRequirement.supports(value), value) } + for value in ["1.6", "1.10", "2.0", "0.9", "01.3", "1.03", "1.3.0", "1", "1.9999", ">=1.3", " 1.3"] { XCTAssertFalse(WidgetRuntimeRequirement.supports(value), value) } XCTAssertTrue(WidgetRuntimeRequirement.supports("1.9", current: "1.10")) diff --git a/Tools/ScriptWidgetCLI/ScriptWidgetCLI.xcodeproj/project.pbxproj b/Tools/ScriptWidgetCLI/ScriptWidgetCLI.xcodeproj/project.pbxproj index 5b0f249e..3156bfce 100644 --- a/Tools/ScriptWidgetCLI/ScriptWidgetCLI.xcodeproj/project.pbxproj +++ b/Tools/ScriptWidgetCLI/ScriptWidgetCLI.xcodeproj/project.pbxproj @@ -56,10 +56,12 @@ 727350AEDCC719A184D5995C /* ScriptWidgetRuntimeFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66B36B4A85B7D7A8BCA1082B /* ScriptWidgetRuntimeFetch.swift */; }; 72AA114107506BE3AE708C13 /* ScriptModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC799313C3197689F1A906C /* ScriptModel.swift */; }; 72DC6D622A75391C9582ECB9 /* ScriptWidgetRecordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B05AB13CF7EEB977AF0102 /* ScriptWidgetRecordsView.swift */; }; + F0EF7680531C44E78A77ECAB /* ScriptWidgetCloudJSONView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C95F627B7AAA4A96AF8FD652 /* ScriptWidgetCloudJSONView.swift */; }; 768C0215C3BB8EF7DA908EC6 /* ScriptWidgetElementTagDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F28D26C0C5131742555232 /* ScriptWidgetElementTagDate.swift */; }; 77CDFEBAA10BF9D8D2B34F84 /* ScriptWidgetAttributeRotation3DEffectModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16C9FD4B4DC6C668AF39EE39 /* ScriptWidgetAttributeRotation3DEffectModifier.swift */; }; 7BADA9DF2237397473BA33C3 /* ScriptWidgetStorageProfiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C6D05A624D8D0DB750AF9E /* ScriptWidgetStorageProfiles.swift */; }; 7C15B0E6C184D7473773C48A /* ScriptWidgetRuntimeFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4CA81CF5D27A626C845BA4 /* ScriptWidgetRuntimeFile.swift */; }; + 4765703F555240FD8B843727 /* ScriptWidgetRuntimeCloudJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C16994AEAC4DC7B56EBBB3 /* ScriptWidgetRuntimeCloudJSON.swift */; }; 7C1E07AEE5B4391EDE276A26 /* ScriptWidgetRuntimeConsole.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7718541A936C5B6158CEC828 /* ScriptWidgetRuntimeConsole.swift */; }; 7C26F00AB29E25F691A20494 /* ScriptWidgetAttributeGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28D158D6BC46CC44E9E461AE /* ScriptWidgetAttributeGeneral.swift */; }; 7DAEC073E680BD60C3FBABAB /* api-catalog.json in Resources */ = {isa = PBXBuildFile; fileRef = 5011ED35898ED75BA7F1A1F2 /* api-catalog.json */; }; @@ -102,6 +104,7 @@ F39A3930C8EAB66F701C8391 /* AppConfigFrequency.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB6BEBC3683C90625859290B /* AppConfigFrequency.swift */; }; F4050D05A6E6E480EF3E0913 /* ScriptWidgetAttributeTextAlignmentModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D089792767B8B7DA80ECD818 /* ScriptWidgetAttributeTextAlignmentModifier.swift */; }; F4AE8E8FA3E8632765628B63 /* ScriptWidgetRecordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12B8321A654B24CD074AFFF /* ScriptWidgetRecordStore.swift */; }; + 311E16A1025A41A991EB15DF /* ScriptWidgetCloudJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81F85786A10341E097E9DE73 /* ScriptWidgetCloudJSON.swift */; }; F504231C3A820C5D33559618 /* ScriptWidgetRuntimeDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 991BCC4040B01E653DCEC24E /* ScriptWidgetRuntimeDevice.swift */; }; F817B9F3BAA27FC30C2CB638 /* ScriptWidgetElementTagZStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD8F1EC9AEA3879763F939F4 /* ScriptWidgetElementTagZStack.swift */; }; FC3294E8EC609D1FFB342A5D /* ScriptWidgetElementPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFF61E2368ADA15729BAA89 /* ScriptWidgetElementPoint.swift */; }; @@ -167,6 +170,7 @@ 52B1B2736C7B55C7BE7C2F52 /* ScriptWidgetAppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetAppState.swift; sourceTree = ""; }; 52F28D26C0C5131742555232 /* ScriptWidgetElementTagDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetElementTagDate.swift; sourceTree = ""; }; 56B05AB13CF7EEB977AF0102 /* ScriptWidgetRecordsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRecordsView.swift; sourceTree = ""; }; + C95F627B7AAA4A96AF8FD652 /* ScriptWidgetCloudJSONView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetCloudJSONView.swift; sourceTree = ""; }; 58A58E5429F7356FA9F5C203 /* ScriptManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptManager.swift; sourceTree = ""; }; 5BF63F313011E594BD60F288 /* ScriptWidgetCLI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScriptWidgetCLI.app; sourceTree = BUILT_PRODUCTS_DIR; }; 66B36B4A85B7D7A8BCA1082B /* ScriptWidgetRuntimeFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeFetch.swift; sourceTree = ""; }; @@ -196,6 +200,7 @@ 991BCC4040B01E653DCEC24E /* ScriptWidgetRuntimeDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeDevice.swift; sourceTree = ""; }; 9D60B046B70D59D4F539B7E7 /* ScriptWidgetRuntimeReminders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeReminders.swift; sourceTree = ""; }; 9E4CA81CF5D27A626C845BA4 /* ScriptWidgetRuntimeFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeFile.swift; sourceTree = ""; }; + 52C16994AEAC4DC7B56EBBB3 /* ScriptWidgetRuntimeCloudJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeCloudJSON.swift; sourceTree = ""; }; 9FCA745E5877B5491EF6AD86 /* WorkerMain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkerMain.swift; sourceTree = ""; }; A0BE8865811BC2E348AFB47A /* ScriptWidgetAttributeShadowModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetAttributeShadowModifier.swift; sourceTree = ""; }; A1F360ABE599E880EB4925ED /* ButtonActionAppIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonActionAppIntent.swift; sourceTree = ""; }; @@ -221,6 +226,7 @@ DC3E966BD127060336A2C4BC /* support.bundle */ = {isa = PBXFileReference; lastKnownFileType = wrapper.cfbundle; path = support.bundle; sourceTree = ""; }; DD66F0D5D5A8268A8FBD3344 /* ScriptWidgetRuntimeLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeLocation.swift; sourceTree = ""; }; E12B8321A654B24CD074AFFF /* ScriptWidgetRecordStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRecordStore.swift; sourceTree = ""; }; + 81F85786A10341E097E9DE73 /* ScriptWidgetCloudJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetCloudJSON.swift; sourceTree = ""; }; E2A1805571ADB208EF91A111 /* ScriptWidgetStorageFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetStorageFiles.swift; sourceTree = ""; }; E37571D6847A4E73CEB54C43 /* components.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = components.json; sourceTree = ""; }; E4AB88C833B5E4D9F460F79F /* ScriptWidgetStorageLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetStorageLock.swift; sourceTree = ""; }; @@ -305,7 +311,9 @@ 3AC799313C3197689F1A906C /* ScriptModel.swift */, 0B579A896F623D25DDC8395C /* ScriptWidgetPackage.swift */, E12B8321A654B24CD074AFFF /* ScriptWidgetRecordStore.swift */, + 81F85786A10341E097E9DE73 /* ScriptWidgetCloudJSON.swift */, 56B05AB13CF7EEB977AF0102 /* ScriptWidgetRecordsView.swift */, + C95F627B7AAA4A96AF8FD652 /* ScriptWidgetCloudJSONView.swift */, 66C6D05A624D8D0DB750AF9E /* ScriptWidgetStorageProfiles.swift */, 88FBB82C566EF24581DBB278 /* ScriptWidgetStorageUpgrade.swift */, 4898BF5AB25F5560504579BE /* ScriptWidgetStorageUpgradeView.swift */, @@ -559,6 +567,7 @@ 991BCC4040B01E653DCEC24E /* ScriptWidgetRuntimeDevice.swift */, 66B36B4A85B7D7A8BCA1082B /* ScriptWidgetRuntimeFetch.swift */, 9E4CA81CF5D27A626C845BA4 /* ScriptWidgetRuntimeFile.swift */, + 52C16994AEAC4DC7B56EBBB3 /* ScriptWidgetRuntimeCloudJSON.swift */, 72999E36BA973789D4B9833E /* ScriptWidgetRuntimeHealth.swift */, DD66F0D5D5A8268A8FBD3344 /* ScriptWidgetRuntimeLocation.swift */, 91E69BD0DC00230A3E1BF379 /* ScriptWidgetRuntimeMusic.swift */, @@ -759,7 +768,9 @@ C596ADCCACAC75B64D739AB4 /* ScriptWidgetElementView.swift in Sources */, 5B1EC3DF3E7DD621EF1DA039 /* ScriptWidgetPackage.swift in Sources */, F4AE8E8FA3E8632765628B63 /* ScriptWidgetRecordStore.swift in Sources */, + 311E16A1025A41A991EB15DF /* ScriptWidgetCloudJSON.swift in Sources */, 72DC6D622A75391C9582ECB9 /* ScriptWidgetRecordsView.swift in Sources */, + F0EF7680531C44E78A77ECAB /* ScriptWidgetCloudJSONView.swift in Sources */, 96FF84D543C3926DD61FBDCF /* ScriptWidgetRunningState.swift in Sources */, 56B7B77DE398881D7CF732EC /* ScriptWidgetRuntime.swift in Sources */, 2619A4F58CFC814CEC1AD878 /* ScriptWidgetRuntimeCalendar.swift in Sources */, @@ -770,6 +781,7 @@ BA3091820D3F8EFFACF32D8E /* ScriptWidgetRuntimeElement.swift in Sources */, 727350AEDCC719A184D5995C /* ScriptWidgetRuntimeFetch.swift in Sources */, 7C15B0E6C184D7473773C48A /* ScriptWidgetRuntimeFile.swift in Sources */, + 4765703F555240FD8B843727 /* ScriptWidgetRuntimeCloudJSON.swift in Sources */, B94F843A0032914002F41258 /* ScriptWidgetRuntimeHealth.swift in Sources */, 85E640C7E6A10F44B81BAE0C /* ScriptWidgetRuntimeLocation.swift in Sources */, AF7AD22F3F68DBF68AD9C1B0 /* ScriptWidgetRuntimeMusic.swift in Sources */, diff --git a/Web/preview-engine/package.json b/Web/preview-engine/package.json index fe504ccf..2d678ce4 100644 --- a/Web/preview-engine/package.json +++ b/Web/preview-engine/package.json @@ -1,6 +1,6 @@ { "name": "@scriptwidget/preview-engine", - "version": "0.18.0", + "version": "0.19.0", "private": true, "type": "module", "description": "Browser and Node execution of ScriptWidget JSX packages that mirrors the native JavaScriptCore runtime contract.", diff --git a/Web/preview-engine/src/contract.js b/Web/preview-engine/src/contract.js index e403ba33..1b662394 100644 --- a/Web/preview-engine/src/contract.js +++ b/Web/preview-engine/src/contract.js @@ -5,7 +5,7 @@ export const runtimeContract = Object.freeze({ // Native 1.3 uses cooperative checks and disallows dynamic code. The web // host keeps its disposable Worker; both expose Calendar, with distinct caches. - nativeAPIVersion: "1.4", + nativeAPIVersion: "1.5", nativeCompilerFingerprint: "scriptwidget-babel-v3", apiVersion: "1.2", compilerFingerprint: "scriptwidget-babel-v2", @@ -19,13 +19,13 @@ export const runtimeContract = Object.freeze({ maximumDataQueries: 8, globalAPIs: Object.freeze([ "$calendar", "$component", "$console", "$device", "$dynamic_island", "$element", - "$error", "$fetch", "$file", "$getenv", "$health", "$http", "$import", + "$error", "$fetch", "$file", "$icloud", "$getenv", "$health", "$http", "$import", "$location", "$music", "$photos", "$reminders", "$render", "$runtime", "$services", "$storage", "$system", "$xml", "console", "fetch", ]), }); /// Bumped whenever the web host changes observable execution semantics. -export const engineVersion = "0.18.0"; +export const engineVersion = "0.19.0"; export const errorCategories = Object.freeze({ undefinedRender: Object.freeze({ category: "Render", recovery: "Call $render(...) once from the main script." }), diff --git a/Web/preview-engine/src/kernel.js b/Web/preview-engine/src/kernel.js index ff9001f3..ddb2b015 100644 --- a/Web/preview-engine/src/kernel.js +++ b/Web/preview-engine/src/kernel.js @@ -427,6 +427,36 @@ export function scriptWidgetKernel(global, host, configJSON) { } const storage = new Map(Object.entries(config.storage ?? fixtures.storage ?? {})); + // iCloud is always an isolated preview model. No network, account or native + // connection is acquired by a fixture or a package-supplied store ID. + const cloudFixture = fixtures.icloud ?? {}; + const cloudDocuments = new Map(Object.entries(cloudFixture.documents ?? {})); + let cloudRevision = 0; + function cloudJSON(method, name, value, expected) { + return nativePromise((resolve, reject) => { + const fail = (reason) => reject(`[icloud:${reason}] Preview operation rejected; existing data is preserved.`); + if (!config.hasManifest || !config.manifest?.permissions?.includes("icloud")) { reject("[permission_denied] Declare icloud in widget.json"); return; } + if (timelineProjection || liveActivityPresentation || (["write", "resolve", "sync"].includes(method) && !actionMode)) { fail("read_only"); return; } + if (++dataQueries > contract.maximumDataQueries) { reject("[query_limit] Too many system data queries"); return; } + if (cloudFixture.connected !== true) { fail("notConnected"); return; } + if (method === "status") { resolve({ state: cloudFixture.available === false ? "unavailable" : "connected", store: "preview-only", uploaded: null, uploadState: "unknown", conflictPolicy: "preserveAllVersions" }); return; } + if (method === "sync") { if (cloudFixture.available === false) fail("unavailable"); else resolve({ state: "pending", materializedRevisions: cloudDocuments.size, downloadedRevisions: 0, uploaded: null }); return; } + if (typeof name !== "string" || !/^[a-z][a-z0-9_-]{0,58}\.json$/.test(name)) { fail("invalidName"); return; } + const previous = cloudDocuments.get(name) ?? { value: null, revision: "", state: "cached", conflicts: [] }; + if (method === "read") { resolve(bridgeValue(previous, 0)); return; } + if (expected !== previous.revision) { fail("staleRevision"); return; } + if (method !== "resolve" && previous.state === "conflict") { fail("conflict"); return; } + const valid = (item, depth = 0) => depth <= 32 && (item === null || typeof item === "string" || typeof item === "boolean" || (typeof item === "number" && Number.isFinite(item)) || (typeof item === "object" && Object.values(item).every(child => valid(child, depth + 1)))); + if (!value || typeof value !== "object" || ArrayIsArray(value) || !valid(value)) { fail("invalidData"); return; } + const encoded = JSONStringify(value); + if (utf8ByteLength(encoded) > 64 * 1024) { fail("limitExceeded"); return; } + cloudRevision += 1; + if (cloudRevision > 256) { fail("limitExceeded"); return; } + const current = { value: JSON.parse(encoded), revision: `preview-${cloudRevision}`, state: "pending", conflicts: [] }; + cloudDocuments.set(name, current); resolve(bridgeValue(current, 0)); + }); + } + const validStorageKey = (key) => key !== "" && utf8ByteLength(key) <= 256; function storageJSON(value) { if (value === null || typeof value !== "object") return null; @@ -551,6 +581,13 @@ export function scriptWidgetKernel(global, host, configJSON) { requestAuthorization: unavailablePromise("location.authorization"), current: unavailablePromise("location"), }, + $icloud: { + readJSON: (name) => cloudJSON("read", name), + writeJSON: (name, value, expectedRevision) => cloudJSON("write", name, value, expectedRevision), + resolveJSON: (name, value, expectedRevision) => cloudJSON("resolve", name, value, expectedRevision), + sync: () => cloudJSON("sync"), + status: () => cloudJSON("status"), + }, $storage: { getString(key) { const name = nativeString(key); const value = validStorageKey(name) ? storage.get(name) : undefined; return typeof value === "string" ? value : ""; }, setString(key, value) { diff --git a/Web/preview-engine/src/package/manifest.js b/Web/preview-engine/src/package/manifest.js index 04c96d7a..d226d35e 100644 --- a/Web/preview-engine/src/package/manifest.js +++ b/Web/preview-engine/src/package/manifest.js @@ -6,7 +6,7 @@ import { normalizePackagePath } from "../packageFiles.js"; export const runtimeAPIVersion = "1.0"; export const families = ["systemSmall", "systemMedium", "systemLarge", "systemExtraLarge", "systemExtraLargePortrait", "accessoryCircular", "accessoryRectangular", "accessoryInline"]; -export const permissions = ["network", "location", "health", "calendar", "reminders", "remindersWrite", "photos", "services", "music", "storage", "files"]; +export const permissions = ["network", "location", "health", "calendar", "reminders", "remindersWrite", "photos", "services", "music", "storage", "icloud", "files"]; const manifestKeys = new Set(["formatVersion", "id", "name", "version", "runtimeVersion", "minimumRuntimeVersion", "timeline", "entry", "supportedFamilies", "permissions", "networkDomains", "plugins", "actions", "controls", "pushUpdates", "description", "category", "tags", "icon", "preview", "author", "license"]); const actionKeys = new Set(["id", "title", "description", "systemImage", "function"]); const controlKeys = new Set(["id", "type", "title", "subtitle", "systemImage", "action", "actionID", "stateKey"]); @@ -142,6 +142,7 @@ export function validateManifest(manifest, paths, { checkEntryExists = true } = if (manifest.supportedFamilies.length === 0) error("missing_families", "At least one supported widget family is required."); if (new Set(manifest.supportedFamilies).size !== manifest.supportedFamilies.length) error("duplicate_families", "supportedFamilies must not contain duplicates."); if (new Set(manifest.permissions).size !== manifest.permissions.length) error("duplicate_permissions", "permissions must not contain duplicates."); + if (manifest.permissions.includes("icloud") && (!/^1\.[0-9]+$/.test(manifest.minimumRuntimeVersion ?? "") || Number(manifest.minimumRuntimeVersion.split(".")[1]) < 5)) error("icloud_runtime", "iCloud JSON requires minimumRuntimeVersion 1.5 or later."); const network = manifest.permissions.includes("network"); if (manifest.networkDomains.length && !network) error("undeclared_network", "networkDomains requires the network permission."); if (network && manifest.networkDomains.length === 0) error("missing_network_domains", "Network access must declare at least one host."); diff --git a/Web/preview-engine/src/types.d.ts b/Web/preview-engine/src/types.d.ts index cfd10a9e..e4475c72 100644 --- a/Web/preview-engine/src/types.d.ts +++ b/Web/preview-engine/src/types.d.ts @@ -5,7 +5,7 @@ export type WidgetFamilyName = "systemSmall" | "systemMedium" | "systemLarge" | "systemExtraLarge" | "systemExtraLargePortrait" | "accessoryCircular" | "accessoryRectangular" | "accessoryInline"; export type WidgetSize = "small" | "medium" | "large" | "extraLarge" | "extraLargePortrait" | "accessoryCircular" | "accessoryRectangular" | "accessoryInline"; -export type Permission = "network" | "location" | "health" | "calendar" | "reminders" | "remindersWrite" | "photos" | "services" | "music" | "storage" | "files"; +export type Permission = "network" | "location" | "health" | "calendar" | "reminders" | "remindersWrite" | "photos" | "services" | "music" | "storage" | "icloud" | "files"; export type Severity = "error" | "warning" | "info"; export type Appearance = "light" | "dark"; export type RenderingMode = "fullColor" | "accented" | "vibrant"; diff --git a/Web/preview-engine/test/icloud.test.js b/Web/preview-engine/test/icloud.test.js new file mode 100644 index 00000000..09fe4612 --- /dev/null +++ b/Web/preview-engine/test/icloud.test.js @@ -0,0 +1,51 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { executePackageInNode } from "../src/node.js"; +const manifest = { formatVersion: 2, id: "example.cloud-json", name: "Cloud JSON", version: "1.0.0", runtimeVersion: "1.0", minimumRuntimeVersion: "1.5", entry: "main.jsx", supportedFamilies: ["systemMedium"], permissions: ["icloud"], networkDomains: [], actions: [{ id: "save", title: "Save", systemImage: "icloud", function: "save" }] }; +const run = (source, options = {}) => executePackageInNode({ files: { "main.jsx": source, "widget.json": JSON.stringify(manifest) }, fixtures: { icloud: { connected: true, available: true, documents: {} } }, ...options }); +const value = (result) => { assert.equal(result.error, null); return JSON.parse(JSON.parse(result.tree).children[0]); }; + +test("cloud preview is cache-only, does not claim upload and requires a native grant model", async () => { + assert.equal(value(await run(`$render({JSON.stringify(await $icloud.status())});`)).uploaded, null); + const denied = await run(`$render({JSON.stringify(await $icloud.status())});`, { fixtures: { icloud: { connected: false } } }); + assert.match(denied.error.message, /notConnected/); +}); +test("ordinary rendering cannot change cloud records or sync", async () => { + for (const source of [`await $icloud.writeJSON('a.json',{},'');`, `await $icloud.resolveJSON('a.json',{},'');`, `await $icloud.sync();`]) { + assert.match((await run(source + '$render(never);')).error.message, /read_only/); + } +}); +test("an action can save, reread, and reject a stale revision without overwriting", async () => { + const result = await run(`async function save(){ + const written = await $icloud.writeJSON('a.json',{keep:true},''); + let stale=false; try {await $icloud.writeJSON('a.json',{lost:true},'');}catch(e){stale=String(e).includes('staleRevision');} + console.log(JSON.stringify({written,read:await $icloud.readJSON('a.json'),stale})); + } $render(Action);`, { actionID: "save" }); + assert.equal(result.error, null); + const saved = JSON.parse(result.logs.find(log => log.level === "log").message); + assert.equal(saved.stale, true); + assert.deepEqual(saved.read.value, { keep: true }); + assert.equal(saved.read.revision, saved.written.revision); +}); +test("conflicts are visible and only explicit resolution may replace heads", async () => { + const fixtures = { icloud: { connected: true, documents: { "a.json": { value: null, revision: "both", state: "conflict", conflicts: [{ id: "a", value: { n: 1 } }, { id: "b", value: { n: 2 } }] } } } }; + const read = value(await run(`$render({JSON.stringify(await $icloud.readJSON('a.json'))});`, { fixtures })); + assert.equal(read.conflicts.length, 2); + const result = await run(`async function save(){let blocked=false;try{await $icloud.writeJSON('a.json',{},'both');}catch(e){blocked=true;}const r=await $icloud.resolveJSON('a.json',{n:3},'both');console.log(JSON.stringify({blocked,r}));}$render(Action);`, { fixtures, actionID: "save" }); + assert.equal(result.error, null); + const resolved = JSON.parse(result.logs.find(log => log.level === "log").message); + assert.equal(resolved.blocked, true); + assert.deepEqual(resolved.r.value, { n: 3 }); + assert.equal(resolved.r.state, "pending"); +}); +test("missing permission and too-old minimum runtime fail closed", async () => { + for (const m of [{ ...manifest, permissions: [] }, { ...manifest, minimumRuntimeVersion: "1.4" }]) { + const result = await run(`await $icloud.status();$render(never);`, { files: { "main.jsx": `await $icloud.status();$render(never);`, "widget.json": JSON.stringify(m) } }); + assert.ok(result.error); + } +}); + +test("cloud calls retain the shared data-query budget", async () => { + const result = await run(`for(let i=0;i<9;i++) await $icloud.status();$render(never);`); + assert.match(result.error.message, /query_limit/); +}); diff --git a/Web/preview-engine/test/packagePreflight.test.js b/Web/preview-engine/test/packagePreflight.test.js index 92837708..ef8983c0 100644 --- a/Web/preview-engine/test/packagePreflight.test.js +++ b/Web/preview-engine/test/packagePreflight.test.js @@ -7,8 +7,8 @@ import { decodeManifest, newManifest, supportsMinimumRuntime, validateManifest } const files = (manifest, source = '$render(ok);') => ({ "widget.json": JSON.stringify(manifest), "main.jsx": source }); test("minimum API versions use canonical numeric same-major comparison", () => { - for (const version of ["1.0", "1.1", "1.2", "1.3", "1.4"]) assert.equal(supportsMinimumRuntime(version), true, version); - for (const version of ["1.5", "1.10", "2.0", "0.9", "01.3", "1.03", "1.3.0", "1", "1.9999", ">=1.3", " 1.3"]) assert.equal(supportsMinimumRuntime(version), false, version); + for (const version of ["1.0", "1.1", "1.2", "1.3", "1.4", "1.5"]) assert.equal(supportsMinimumRuntime(version), true, version); + for (const version of ["1.6", "1.10", "2.0", "0.9", "01.3", "1.03", "1.3.0", "1", "1.9999", ">=1.3", " 1.3"]) assert.equal(supportsMinimumRuntime(version), false, version); assert.equal(supportsMinimumRuntime("1.9", "1.10"), true); assert.equal(supportsMinimumRuntime("1.10", "1.9"), false); for (const minimum of [undefined, null, "1.0", "1.3", "1.4", "2.0", "01.3"]) { @@ -22,7 +22,7 @@ test("minimum API versions use canonical numeric same-major comparison", () => { test("invalid packages are rejected before the compiler or script can run", () => { const compiler = new Proxy({}, { get() { throw new Error("Compiler must not run"); } }); - for (const patch of [{ minimumRuntimeVersion: "1.5" }, { minimumRuntimeVersion: "01.3" }, { formatVersion: 99 }, { extraCapability: true }]) { + for (const patch of [{ minimumRuntimeVersion: "1.6" }, { minimumRuntimeVersion: "01.3" }, { formatVersion: 99 }, { extraCapability: true }]) { const result = preparePackage(compiler, { files: files({ ...newManifest("Preflight"), ...patch }, "while(true) {}") }); assert.equal(result.ok, false); assert.equal(result.error.kind, "scriptError"); @@ -32,7 +32,7 @@ test("invalid packages are rejected before the compiler or script can run", () = test("file manifest is authoritative over a permissive host object", async () => { const host = { ...newManifest("Host grant"), permissions: ["calendar"] }; - for (const text of ["{bad", JSON.stringify({ ...host, formatVersion: 999 }), JSON.stringify({ ...host, minimumRuntimeVersion: "1.5" })]) { + for (const text of ["{bad", JSON.stringify({ ...host, formatVersion: 999 }), JSON.stringify({ ...host, minimumRuntimeVersion: "1.6" })]) { const result = await executePackageInNode({ files: { "widget.json": text, "main.jsx": 'throw new Error("executed");' }, manifest: host }); assert.equal(result.error.kind, "scriptError"); assert.match(result.error.message, /\[package:/); diff --git a/Web/studio/src/components/MockDataPanel.tsx b/Web/studio/src/components/MockDataPanel.tsx index d3b8f8f4..d6319c52 100644 --- a/Web/studio/src/components/MockDataPanel.tsx +++ b/Web/studio/src/components/MockDataPanel.tsx @@ -5,8 +5,8 @@ import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { addSampleMocks, isObject, mockAPIs, musicScenario, parseMocks, permissionScenario, responseFor, sampleFixtures, serviceScenario, setResponse, type MockObject, type MockValue } from "@/mockData.ts"; -const groups = ["services", "music", "calendar", "reminders", "photos", "health", "location", "device", "system", "network", "storage", "dataSource", "json"]; -const labels: Record = { services: "Connected services", music: "Music playback", calendar: "Calendar", reminders: "Reminders", photos: "Photos", health: "Health", location: "Location", device: "Device", system: "System", network: "Network responses", storage: "Storage", dataSource: "Data Source Plugins", json: "Full JSON" }; +const groups = ["services", "music", "calendar", "reminders", "photos", "health", "location", "device", "system", "network", "storage", "icloud", "dataSource", "json"]; +const labels: Record = { services: "Connected services", music: "Music playback", calendar: "Calendar", reminders: "Reminders", photos: "Photos", health: "Health", location: "Location", device: "Device", system: "System", network: "Network responses", storage: "Storage", icloud: "iCloud JSON", dataSource: "Data Source Plugins", json: "Full JSON" }; // Primitive leaves use normal form fields. Arrays and uncommon payloads can // always be edited in the return JSON without losing unknown properties. @@ -37,8 +37,8 @@ export function MockDataPanel({ fixtures, onChange }: { fixtures: string; onChan const response = responseFor(data, api); const writeResponse = api.operation === "write"; const special = group === "network" ? "fetch" : group; - const structured = ["network", "storage", "dataSource"].includes(group); - const encoded = structured ? JSON.stringify(data[special] ?? (group === "storage" ? {} : []), null, 2) + const structured = ["network", "storage", "icloud", "dataSource"].includes(group); + const encoded = structured ? JSON.stringify(data[special] ?? (["storage", "icloud"].includes(group) ? {} : []), null, 2) : JSON.stringify("error" in response ? response.error : response.value, null, 2); useEffect(() => { try { setCalendarSelectionDraft(JSON.stringify(parseMocks(fixtures)[group === "reminders" ? "remindersSelection" : "calendarSelection"] ?? null, null, 2)); } catch { /* Preserve malformed Full JSON for repair. */ } @@ -96,6 +96,7 @@ export function MockDataPanel({ fixtures, onChange }: { fixtures: string; onChan parseMocks(next); return next; })}>Apply source selection } + {group === "icloud" &&

Preview only. Set connected/available and documents. Each document has value, revision, state and conflicts. No Apple account or real iCloud files are accessed. New writes require a declared action; cloud upload completion is unknown.

} {group === "network" &&

Match an exact URL and method. body is response text (JSON as a string); error simulates failure. Unmatched requests show a warning. Package network permissions still apply.

} {group === "storage" &&
} {group === "storage" &&

Initial key/value pairs for each run. Reads and writes work within that run; the next preview starts from these values. Edit package files in the file tree to mock $file data.

} diff --git a/Web/studio/src/mockData.ts b/Web/studio/src/mockData.ts index 0f60ee0a..b8dcbf6b 100644 --- a/Web/studio/src/mockData.ts +++ b/Web/studio/src/mockData.ts @@ -25,14 +25,14 @@ export function sampleFixtures(): MockObject { api: Object.fromEntries(mockAPIs.filter((api) => api.id !== "$device.isdarkmode").map((api) => [api.id, { value: api.group === "health" && isObject(api.value) ? healthSample(api.id,api.value) : api.group === "photos" && api.id.endsWith(".items") ? {...api.value as MockObject,fetchedAt:new Date(now).toISOString()} : api.group === "reminders" && api.id.endsWith(".items") ? {...api.value as MockObject, fetchedAt: new Date(now).toISOString(), items: [{id:"sample.reminder",listID:"sample.reminders",title:"Read a chapter",due:new Date(dayStart.getTime()+16*3600000).toISOString(),hasTime:true,completed:false,completedAt:null,priority:0}]} : api.value }])), fetch: [{url:"https://api.github.com/repos/everettjf/scriptwidget",method:"GET",status:200,body:JSON.stringify({full_name:"everettjf/ScriptWidget",private:false,stargazers_count:1280,forks_count:96,open_issues_count:18}),headers:{"content-type":"application/json","cache-control":"public, max-age=3600"}}, { url: "https://api.example.com/data", method: "GET", body: '{"value":42}', status: 200, headers: {"content-type":"application/json"} }, {url:"https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=37.33&lon=-122.01",method:"GET",status:200,body:JSON.stringify(weather),headers:{"content-type":"application/json",expires:new Date(now+3600000).toUTCString(),"last-modified":new Date(start).toUTCString()}}], - storage: {}, storageAtomicAvailable: true, dataSource: [], calendarSelection: null, remindersSelection: null, photosSelection: ["sample.photo", "sample.photo.2", "sample.photo.3", "sample.photo.4"], photosSnapshotPrepared: true, + storage: {}, icloud: { connected: true, available: true, documents: {} }, storageAtomicAvailable: true, dataSource: [], calendarSelection: null, remindersSelection: null, photosSelection: ["sample.photo", "sample.photo.2", "sample.photo.3", "sample.photo.4"], photosSnapshotPrepared: true, }; } export const defaultFixtures = JSON.stringify(sampleFixtures(), null, 2); export function parseMocks(text: string): MockObject { const value: unknown = JSON.parse(text || "{}"); if (!isObject(value)) throw new Error("Mock data must be a JSON object."); - for (const key of ["api", "device", "system", "storage"]) { + for (const key of ["api", "device", "system", "storage", "icloud"]) { if (value[key] !== undefined && !isObject(value[key])) throw new Error(`${key} must be a JSON object.`); } if (value.remindersState !== undefined && (!isObject(value.remindersState) || value.remindersState.formatVersion !== 1 || diff --git a/docs/README.md b/docs/README.md index 3d841b2d..bf0836a4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -52,3 +52,5 @@ Build native widgets for iPhone, iPad, and Mac with JavaScript and JSX. The quic ## Supported platforms ScriptWidget focuses on iOS, iPadOS, and macOS. A single `main.jsx` can adapt its layout with `$getenv("widget-size")`. Native watchOS is unsupported and outside the current [A/B scope](widget-capabilities-plan.md). visionOS is outside this plan. + +- [Opt-in iCloud JSON API](icloud-json-api.md) — native grants, immutable revisions, conflicts and Web mocks. diff --git a/docs/browser-preview-coverage.md b/docs/browser-preview-coverage.md index 465e384b..b7b3efa8 100644 --- a/docs/browser-preview-coverage.md +++ b/docs/browser-preview-coverage.md @@ -1,6 +1,6 @@ # Browser preview coverage -Generated by `Web/preview-engine/scripts/coverage-report.mjs` for preview engine 0.18.0. Do not edit by hand. +Generated by `Web/preview-engine/scripts/coverage-report.mjs` for preview engine 0.19.0. Do not edit by hand. States: **supported** renders like iPhone within measured tolerances; **approximated** renders with the noted difference; **unsupported** shows a placeholder and a diagnostic. Properties not listed for a tag are ignored by the native runtime and reported as such. ## Tags diff --git a/docs/examples/icloud-json-note/main.jsx b/docs/examples/icloud-json-note/main.jsx new file mode 100644 index 00000000..bd48479b --- /dev/null +++ b/docs/examples/icloud-json-note/main.jsx @@ -0,0 +1,27 @@ +// Connect this installed package through the native "iCloud Data" menu first. +// Writes are actions only; normal render never creates a cloud record. +async function syncNote() { await $icloud.sync(); } +async function saveNote() { + const before = await $icloud.readJSON("note.json"); + if (before.state === "conflict") throw new Error("Review and resolve every conflicting version before saving."); + await $icloud.writeJSON("note.json", {text: "Read a chapter"}, before.revision); +} +if ($getenv("widget-size") !== "action") { + try { + const note = await $icloud.readJSON("note.json"); + $render( + + iCloud Note + {note.state === "conflict" ? "Conflicting versions need review" : note.value?.text ?? "No cached note yet"} + Cached data · upload status unknown + + + + + + ); + } catch (error) { + console.error(String(error)); + $render(Connect iCloud Data in ScriptWidget, then sync.); + } +} diff --git a/docs/examples/icloud-json-note/widget.json b/docs/examples/icloud-json-note/widget.json new file mode 100644 index 00000000..626d290e --- /dev/null +++ b/docs/examples/icloud-json-note/widget.json @@ -0,0 +1,16 @@ +{ + "formatVersion": 2, + "id": "app.scriptwidget.example.icloud-json-note", + "name": "iCloud JSON Note", + "version": "1.0.0", + "runtimeVersion": "1.0", + "minimumRuntimeVersion": "1.5", + "entry": "main.jsx", + "supportedFamilies": ["systemSmall", "systemMedium", "systemLarge"], + "permissions": ["icloud"], + "networkDomains": [], + "actions": [ + {"id":"sync","title":"Sync","systemImage":"arrow.triangle.2.circlepath","function":"syncNote"}, + {"id":"save","title":"Save note","systemImage":"square.and.pencil","function":"saveNote"} + ] +} diff --git a/docs/icloud-json-api.md b/docs/icloud-json-api.md new file mode 100644 index 00000000..b1f3dec2 --- /dev/null +++ b/docs/icloud-json-api.md @@ -0,0 +1,82 @@ +# iCloud JSON API (Runtime 1.5) + +`$icloud` is an opt-in JSON revision store in the existing `iCloud.ScriptWidget` iCloud Drive container. It adds no third-party SDK or application server. `$storage` remains local; existing records are never automatically moved. Web Studio uses mocks; native CLI/template previews cannot access real iCloud. + +## Explicit native connection + +A Package 2.0 manifest must declare `permissions: ["icloud"]` and `minimumRuntimeVersion: "1.5"` (or a supported later API). Older apps fail closed on the new permission/version. + +In the native widget list, open the context menu → **iCloud Data**. Create a new store or select an existing folder ID, then confirm read/write access. On another device explicitly connect its installed widget to the same store ID. The manifest, package name, Gallery, backups and JavaScript cannot choose that ID or grant permission. Native rename retains the connection; remix/reinstall does not inherit it. **Disconnect** revokes only this widget’s grant and preserves record files. Connecting a different store preserves the previous store and cache. This feature does not migrate Habit Heatmap automatically. + +Grants and caches are bound to a native installation UUID and current iCloud identity. Sign-out/account change denies old-account cached reads and writes. Reconnect explicitly for a new account. A system change to the identity token's archive can require reconnection rather than guessing authority. + +## Methods + +Every method returns a Promise. `name` is a flat lowercase ASCII filename matching `/^[a-z][a-z0-9_-]{0,58}\.json$/`. No directories, absolute paths, URLs or arbitrary iCloud access. + +| Method | Behavior | +| --- | --- | +| `readJSON(name)` | Returns cached `{value,revision,state,conflicts}`. No cached revision returns `value:null, revision:""`; this does not prove remote data is absent. | +| `writeJSON(name, object, expectedRevision)` | Create an immutable local revision, using the token from the latest read. Returns `state:"pending"`, then attempts background exchange. Stale tokens reject without overwriting history. | +| `resolveJSON(name, object, expectedRevision)` | Explicitly create a revision descending from all observed heads after reviewing/merging conflicts. Stale conflict tokens reject. Originals remain. | +| `sync()` | Exchange currently materialized revision files. Returns `{state:"pending",materializedRevisions,downloadedRevisions,uploaded:null}`. | +| `status()` | Returns `{state,store,uploaded:null,uploadState:"unknown",conflictPolicy:"preserveAllVersions"}` or rejects on account/connection errors. | + +Reads use `state:"cached"`; writes use `"pending"`. A fork returns `state:"conflict", value:null, conflicts:[{id,value},…]`. Ordinary `writeJSON` rejects conflicts; only `resolveJSON` can explicitly consume all observed heads. **A local save or successful sync is never proof of upload, convergence or a global cross-device compare-and-swap.** Apple controls delivery and background execution. + +Writes, resolution and explicit sync require a user-initiated declared action. Normal rendering never changes records. Future timeline and Live Activity presentations cannot use this API. Native calls share the data-query limit (8/execution). Following an authorized local operation, a separate native exchange revalidates the captured installation even after JS ends; it cannot follow a removed/replaced same-name widget. App foreground recovery retries connected stores at most every 15 seconds. Newly received revisions request a WidgetKit reload; the system chooses its timing. + +## Example + +The complete [iCloud JSON Note package](examples/icloud-json-note) includes a manifest and declared Sync / Save actions. Import it, then connect it through the native menu. + +```jsx +const snapshot = await $icloud.readJSON("note.json"); +$render({snapshot.state === "conflict" + ? "Review conflicting versions" + : snapshot.value?.text ?? "No cached note yet"}); + +async function saveNote() { + const before = await $icloud.readJSON("note.json"); + if (before.state === "conflict") throw new Error("Resolve the conflict first"); + await $icloud.writeJSON("note.json", {text:"Read a chapter"}, before.revision); +} +``` + +A cold/offline device can initially read no cached value, then discover remote records later. An independent write becomes a retained fork, not last-writer-wins deletion. To make a habit merge, validate habit IDs/dates, union completed dates across **all** conflicts, and explicitly call `resolveJSON` with the observed token. Counters, deletions, reset and undo need product-specific event/merge rules; unioning arbitrary JSON would lose user intent. + +## Files, format and bounds + +In Files/Finder: **iCloud Drive → ScriptWidget → WidgetData → → note.json → .json**. `note.json` is a document directory. Each canonical sorted UTF-8 file contains: + +```json +{"formatVersion":1,"name":"note.json","parents":[],"value":{"text":"Read a chapter"}} +``` + +The filename hashes its exact canonical bytes. Revisions reference parent hashes. Two offline writers create two distinct heads; neither overwrites or deletes the other's file. Exchange is additive/idempotent. Users can inspect/copy these JSON files outside the app. **Direct editing, renaming or deleting revision files is unsupported**: checksum/parent/schema errors preserve the cache and block exchange. To edit, read/copy the value and create a new revision through the API. A removed cloud store is not automatically recreated from cache. + +Initial bounded contract: 64 KiB per JSON object, 128 document names, 256 revisions and 2 MiB of encoded revisions per store; discovery lists at most 128 stores. Capacity exhaustion fails without pruning history. Archive/export the previous store and explicitly connect a new store when needed. Automatic compaction and unlimited history are not implemented. These limits bound scans/allocation, not a claim of measured physical extension memory. + +The native service uses descriptor-relative no-follow path checks, exact-case names, installation/root receipts and process locks. Revisions publish by atomic **exclusive** rename, never overwriting an existing version. Links, hard links, invalid JSON, unknown fields, invalid parent references and quota excess fail closed. Cloud publication uses `NSFileCoordinator`. Regular Finder `.DS_Store` metadata is ignored without being read or exchanged. Placeholders request download and preserve cached data; missing parents block reliable reads until downloaded. Diagnostics exclude account tokens, file contents, absolute device paths and other widgets' data. + +Errors include `notConnected`, `accountChanged`, `unavailable`, `invalidName`, `invalidData`, `limitExceeded`, `staleRevision`, `conflict`, `identityChanged` with prefix `[icloud:…]`. Never clear local records as recovery from a cloud failure. Corrupt remote files need repair/restore rather than discarding cached history. + +## Web Studio mock + +![Web Studio iCloud JSON mock editor](images/icloud-json/web-mock.png) + +Mock Data → **iCloud JSON** edits `fixtures.icloud`: + +```json +{"connected":true,"available":true,"documents":{"note.json":{"value":{"text":"Sample note"},"revision":"fixture-1","state":"cached","conflicts":[]}}} +``` + +`connected:false` denies access. `available:false` rejects sync but leaves cache/local writes usable. Seed `state:"conflict"`, `value:null` and two `{id,value}` entries to exercise resolution. Writes enforce manifest permission, action-only mutation, stale tokens, names and JSON size. They affect only the current preview execution, never actual iCloud; mocks are not exported. Preview tokens are opaque model tokens, not native revision hashes. + +## Verification and physical release gate + +`node Scripts/test-icloud-json.mjs` runs the production service and its XCTest fixtures directly without an App host. It supplements the full application/extension test schemes. Deterministic tests use two separate temporary device caches and one isolated temporary cloud. They cover offline round trips, concurrent forks, explicit resolution, stale writes, retirement, account switch, new same-name installations, malformed/oversized JSON, links and remote-store removal. Browser tests cover permission and action denial and conflict behavior. They never use the user's real iCloud account. + +Before release, still verify on two signed-in physical devices: native grant, offline edits/reconnect, conflict resolution, file eviction/download, account switching, foreground retry and extension memory/refresh. Fixture tests and simulator builds are not signed real-container acceptance. + +Apple guidance: https://developer.apple.com/documentation/uikit/synchronizing-documents-in-the-icloud-environment diff --git a/docs/images/icloud-json/web-mock.png b/docs/images/icloud-json/web-mock.png new file mode 100644 index 00000000..e3a67ea9 Binary files /dev/null and b/docs/images/icloud-json/web-mock.png differ diff --git a/docs/scriptwidget-runtime-api.md b/docs/scriptwidget-runtime-api.md index 19c1d226..cd8a13cb 100644 --- a/docs/scriptwidget-runtime-api.md +++ b/docs/scriptwidget-runtime-api.md @@ -1,6 +1,6 @@ # ScriptWidget Runtime API -> Generated from Studio's static API metadata (schema v1, runtime 1.4). The native runtime switch is authoritative. Do not edit by hand. +> Generated from Studio's static API metadata (schema v1, runtime 1.5). The native runtime switch is authoritative. Do not edit by hand. ## Components @@ -885,6 +885,11 @@ Interactive widget toggle. A declared actionID requires a package-relative stora - `$location.isAuthorizedForWidgetUpdates`: Package 2.0 requires location permission. Request system access in the app. Widgets also require isAuthorizedForWidgetUpdates. Cache is rechecked against authorization and precision, discarded after 24 hours or revocation, and reports age/isStale. - `$location.requestAuthorization`: Package 2.0 requires location permission. Request system access in the app. Widgets also require isAuthorizedForWidgetUpdates. Cache is rechecked against authorization and precision, discarded after 24 hours or revocation, and reports age/isStale. - `$location.current`: Package 2.0 requires location permission. Request system access in the app. Widgets also require isAuthorizedForWidgetUpdates. Cache is rechecked against authorization and precision, discarded after 24 hours or revocation, and reports age/isStale. +- `$icloud.readJSON`: Read cached JSON and revision; conflicts preserve all heads. Requires icloud permission and a native connection. Never certifies upload. +- `$icloud.writeJSON`: Save a new immutable JSON revision locally from a user action. Requires the revision from readJSON. Background exchange is eventual. +- `$icloud.resolveJSON`: Explicitly resolve all observed conflicting heads into a new revision. Stale tokens fail; originals remain. +- `$icloud.sync`: Exchange materialized revisions with iCloud; requires a user action. Upload completion is unknown. +- `$icloud.status`: Read native connection state. Cloud store IDs are granted in the app, never by the script. Web uses preview mocks. - `$file.exists`: Return whether a regular file is locally present inside this package, including binary assets. Missing files, directories, unsafe paths and links return false. Does not download iCloud files or read outside the package. Web preview uses workspace file presence. - `$file.read`: Read package-relative UTF-8 text. Missing or unavailable files return an empty string. - `$file.readJSON`: Read a package-relative JSON object. diff --git a/docs/storage-api.md b/docs/storage-api.md index 5694b12f..e8504ac7 100644 --- a/docs/storage-api.md +++ b/docs/storage-api.md @@ -287,3 +287,7 @@ The `healthCard` kind also manages only `recordTemplate.config.v1`, containing ` The `repoStats` kind backs up only saved `repository` and `accent` configuration. Its transient `github.stats.v1` cache and daily public-count history are excluded. It grants no network permission and cannot contain credentials; the built-in template declares only api.github.com. Unknown repositories, settings and accents fail validation. See [Public data cards](public-data-cards.md). The `launcher` configuration-only kind saves title, accent and ordered link definitions (including explicitly supplied shortcut text). It has a 32 KiB configuration allowance; other kinds retain 4 KiB. Backups can contain personal link/input data. Neither restore nor URL validation opens a target app. See [Launchers and system actions](launcher-actions.md). + +## Optional iCloud JSON + +`$storage` remains local and is not migrated automatically. New packages can opt into the separate [iCloud JSON API](icloud-json-api.md) (Runtime 1.5), with explicit native user connection, cache-first reads and preserved conflicting revisions. diff --git a/iOS/ScriptWidget.xcodeproj/project.pbxproj b/iOS/ScriptWidget.xcodeproj/project.pbxproj index 1ecbdcfd..f209d037 100644 --- a/iOS/ScriptWidget.xcodeproj/project.pbxproj +++ b/iOS/ScriptWidget.xcodeproj/project.pbxproj @@ -406,7 +406,9 @@ F2C6AC7B26004B0F009CECE9 /* ScriptWidgetShare.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F2C6AC7126004B0F009CECE9 /* ScriptWidgetShare.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; F2D738C925D0CA8900021E08 /* SettingTemplatesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D738C825D0CA8900021E08 /* SettingTemplatesView.swift */; }; F2F0A7AE27F6841700D7911B /* ScriptWidgetRuntimeFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F0A7AD27F6841700D7911B /* ScriptWidgetRuntimeFile.swift */; }; + FBABB490DFDA409D8D266385 /* ScriptWidgetRuntimeCloudJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EC92A21DC4745FB8B1F582E /* ScriptWidgetRuntimeCloudJSON.swift */; }; F2F0A7AF27F6841700D7911B /* ScriptWidgetRuntimeFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F0A7AD27F6841700D7911B /* ScriptWidgetRuntimeFile.swift */; }; + 0F853DA1A57C46DCB7F10E8E /* ScriptWidgetRuntimeCloudJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EC92A21DC4745FB8B1F582E /* ScriptWidgetRuntimeCloudJSON.swift */; }; F2F8398E27CBBC8800D74288 /* HomeHelloView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F8398D27CBBC8800D74288 /* HomeHelloView.swift */; }; F2FC062327DC5D9000A6A99D /* MirrorEditorScriptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2FC062227DC5D9000A6A99D /* MirrorEditorScriptView.swift */; }; F2FC062827DC5DB300A6A99D /* WKWebViewJavascriptBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2FC062527DC5DB300A6A99D /* WKWebViewJavascriptBridge.swift */; }; @@ -418,9 +420,12 @@ F2FE0A3525ED3ABF00B4B6B2 /* EmptyListBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2FE0A3425ED3ABF00B4B6B2 /* EmptyListBackgroundView.swift */; }; F2FE0A3725ED3BFA00B4B6B2 /* ScriptWidgetPlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2FE0A3625ED3BFA00B4B6B2 /* ScriptWidgetPlaceholderView.swift */; }; D8A3713A02C54DAFB492D491 /* ScriptWidgetRecordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C847E9CCBC140F6B53AC772 /* ScriptWidgetRecordStore.swift */; }; + A815305D31B24EFE9AE72321 /* ScriptWidgetCloudJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113B591460C0428199E1B2D3 /* ScriptWidgetCloudJSON.swift */; }; 4635FD7C47944227987DB767 /* ScriptWidgetRecordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B96A79828AD84572AB1D6170 /* ScriptWidgetRecordsView.swift */; }; + 6E5ACA07639849578EAF9019 /* ScriptWidgetCloudJSONView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5802CF1E2FEF4FFCAAC3BEC9 /* ScriptWidgetCloudJSONView.swift */; }; A666715063344E9EBBE6E4E9 /* ScriptWidgetStorageUpgradeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE3501C2E4244E879AD942D7 /* ScriptWidgetStorageUpgradeView.swift */; }; D96A3CEA7A35471B83EE7BD1 /* ScriptWidgetRecordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C847E9CCBC140F6B53AC772 /* ScriptWidgetRecordStore.swift */; }; + 65E61B2E0949425591993A22 /* ScriptWidgetCloudJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 113B591460C0428199E1B2D3 /* ScriptWidgetCloudJSON.swift */; }; A99A85B69E4E431491CEBB28 /* ScriptWidgetStorageNamespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = A804AC8F6C5F4242BBA72345 /* ScriptWidgetStorageNamespaces.swift */; }; 6867766C531D41E7ACF91FE6 /* ScriptWidgetStorageFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 903FF2D5FB884143B154AA20 /* ScriptWidgetStorageFiles.swift */; }; EC5474191DA74B96B7A5A7A2 /* ScriptWidgetStorageNamespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = A804AC8F6C5F4242BBA72345 /* ScriptWidgetStorageNamespaces.swift */; }; @@ -791,6 +796,7 @@ F2C6AC7826004B0F009CECE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F2D738C825D0CA8900021E08 /* SettingTemplatesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingTemplatesView.swift; sourceTree = ""; }; F2F0A7AD27F6841700D7911B /* ScriptWidgetRuntimeFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeFile.swift; sourceTree = ""; }; + 8EC92A21DC4745FB8B1F582E /* ScriptWidgetRuntimeCloudJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeCloudJSON.swift; sourceTree = ""; }; F2F8398D27CBBC8800D74288 /* HomeHelloView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeHelloView.swift; sourceTree = ""; }; F2FC062227DC5D9000A6A99D /* MirrorEditorScriptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MirrorEditorScriptView.swift; sourceTree = ""; }; F2FC062527DC5DB300A6A99D /* WKWebViewJavascriptBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKWebViewJavascriptBridge.swift; sourceTree = ""; }; @@ -802,7 +808,9 @@ F2FE0A3425ED3ABF00B4B6B2 /* EmptyListBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyListBackgroundView.swift; sourceTree = ""; }; F2FE0A3625ED3BFA00B4B6B2 /* ScriptWidgetPlaceholderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetPlaceholderView.swift; sourceTree = ""; }; 6C847E9CCBC140F6B53AC772 /* ScriptWidgetRecordStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRecordStore.swift; sourceTree = ""; }; + 113B591460C0428199E1B2D3 /* ScriptWidgetCloudJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetCloudJSON.swift; sourceTree = ""; }; B96A79828AD84572AB1D6170 /* ScriptWidgetRecordsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRecordsView.swift; sourceTree = ""; }; + 5802CF1E2FEF4FFCAAC3BEC9 /* ScriptWidgetCloudJSONView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetCloudJSONView.swift; sourceTree = ""; }; BE3501C2E4244E879AD942D7 /* ScriptWidgetStorageUpgradeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetStorageUpgradeView.swift; sourceTree = ""; }; A804AC8F6C5F4242BBA72345 /* ScriptWidgetStorageNamespaces.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetStorageNamespaces.swift; sourceTree = ""; }; 903FF2D5FB884143B154AA20 /* ScriptWidgetStorageFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetStorageFiles.swift; sourceTree = ""; }; @@ -1292,7 +1300,9 @@ children = ( F29119922793223300B860B0 /* ScriptWidgetPackage.swift */, 6C847E9CCBC140F6B53AC772 /* ScriptWidgetRecordStore.swift */, + 113B591460C0428199E1B2D3 /* ScriptWidgetCloudJSON.swift */, B96A79828AD84572AB1D6170 /* ScriptWidgetRecordsView.swift */, + 5802CF1E2FEF4FFCAAC3BEC9 /* ScriptWidgetCloudJSONView.swift */, BE3501C2E4244E879AD942D7 /* ScriptWidgetStorageUpgradeView.swift */, F29118EC2793034D00B860B0 /* ScriptModel.swift */, D1A0000000E0000000000001 /* ScriptMetadata.swift */, @@ -1450,6 +1460,7 @@ F291192F2793035E00B860B0 /* ScriptWidgetRuntimeConsole.swift */, F29119302793035E00B860B0 /* ScriptWidgetRuntimeDevice.swift */, F2F0A7AD27F6841700D7911B /* ScriptWidgetRuntimeFile.swift */, + 8EC92A21DC4745FB8B1F582E /* ScriptWidgetRuntimeCloudJSON.swift */, 1AE63BF6B17D4C800EFF80E9 /* ScriptWidgetRuntimeSystem.swift */, 654916CAF3464C1D5149BB77 /* ScriptWidgetRuntimeHealth.swift */, 28960B338B1C363177987F8C /* ScriptWidgetRuntimeCalendar.swift */, @@ -2019,6 +2030,7 @@ F277CBA427CB5A2C003AB97D /* ScriptWidgetAttributeOpacityModifier.swift in Sources */, F29119582793035E00B860B0 /* ScriptWidgetAttributeShadowModifier.swift in Sources */, F2F0A7AE27F6841700D7911B /* ScriptWidgetRuntimeFile.swift in Sources */, + FBABB490DFDA409D8D266385 /* ScriptWidgetRuntimeCloudJSON.swift in Sources */, 7D960C5FE159D8D1CB823654 /* ScriptWidgetRuntimeSystem.swift in Sources */, 8B3078FA29098AA7314D1B74 /* ScriptWidgetRuntimeHealth.swift in Sources */, 08A34358AD98E4A7706B116B /* ScriptWidgetRuntimeCalendar.swift in Sources */, @@ -2038,7 +2050,9 @@ A99A85B69E4E431491CEBB28 /* ScriptWidgetStorageNamespaces.swift in Sources */, 6867766C531D41E7ACF91FE6 /* ScriptWidgetStorageFiles.swift in Sources */, D8A3713A02C54DAFB492D491 /* ScriptWidgetRecordStore.swift in Sources */, + A815305D31B24EFE9AE72321 /* ScriptWidgetCloudJSON.swift in Sources */, 4635FD7C47944227987DB767 /* ScriptWidgetRecordsView.swift in Sources */, + 6E5ACA07639849578EAF9019 /* ScriptWidgetCloudJSONView.swift in Sources */, A666715063344E9EBBE6E4E9 /* ScriptWidgetStorageUpgradeView.swift in Sources */, D58FF19E4AF865ACBDAF306A /* ScriptWidgetElementTagExtras.swift in Sources */, F2BA1DA725D500B400363EEA /* EditAttributesView.swift in Sources */, @@ -2191,6 +2205,7 @@ F28D681D27CE8874000C2D03 /* ScriptWidgetElementTagStack.swift in Sources */, F28D681327CE764B000C2D03 /* ScriptWidgetAttributeClippedModifier.swift in Sources */, F2F0A7AF27F6841700D7911B /* ScriptWidgetRuntimeFile.swift in Sources */, + 0F853DA1A57C46DCB7F10E8E /* ScriptWidgetRuntimeCloudJSON.swift in Sources */, 219AD8869A85F18C81A30804 /* ScriptWidgetRuntimeSystem.swift in Sources */, D84E5F5BA6A8C145C140773A /* ScriptWidgetRuntimeHealth.swift in Sources */, 379480111C85C446898E519E /* ScriptWidgetRuntimeCalendar.swift in Sources */, @@ -2210,6 +2225,7 @@ EC5474191DA74B96B7A5A7A2 /* ScriptWidgetStorageNamespaces.swift in Sources */, A3B7A5EF82604B99B3DB4707 /* ScriptWidgetStorageFiles.swift in Sources */, D96A3CEA7A35471B83EE7BD1 /* ScriptWidgetRecordStore.swift in Sources */, + 65E61B2E0949425591993A22 /* ScriptWidgetCloudJSON.swift in Sources */, 683E7A63FCBC7EA6D91BE81D /* ScriptWidgetElementTagExtras.swift in Sources */, F0C7ECF82AD0759F0039D30D /* ScriptWidgetAppIntent.swift in Sources */, F291194D2793035E00B860B0 /* ScriptWidgetRuntimeElement.swift in Sources */, diff --git a/iOS/ScriptWidget/App/Scripts/ScriptWidgetHomeView.swift b/iOS/ScriptWidget/App/Scripts/ScriptWidgetHomeView.swift index 076c64e1..5154564b 100644 --- a/iOS/ScriptWidget/App/Scripts/ScriptWidgetHomeView.swift +++ b/iOS/ScriptWidget/App/Scripts/ScriptWidgetHomeView.swift @@ -68,6 +68,7 @@ struct ScriptWidgetHomeView: View { @State private var selectedEditItem: ScriptModel? @State private var selectedShareItem: ScriptModel? @State private var selectedRecordItem: ScriptModel? + @State private var selectedCloudItem: ScriptModel? @State private var selectedDeleteItem: ScriptModel? @State private var isShowingDeleteAlert = false @@ -95,6 +96,7 @@ struct ScriptWidgetHomeView: View { } message: { item in Text("“\(item.name)” and its project files will be removed. This can’t be undone.") } + .sheet(item: $selectedCloudItem) { item in ScriptWidgetCloudJSONView(package: item.package) } .sheet(item: $selectedRecordItem) { item in ScriptWidgetRecordsView(package: item.package) } @@ -226,6 +228,9 @@ struct ScriptWidgetHomeView: View { WidgetRowView(model: item) } .contextMenu { + if item.package.readManifest()?.permissions.contains(.icloud) == true { + Button("iCloud Data", systemImage: "icloud") { selectedCloudItem = item } + } if (try? ScriptWidgetRecordKind.load(package: item.package)) != nil { Button { selectedRecordItem = item } label: { Label("Records & Configuration", systemImage: "list.bullet.rectangle") } } diff --git a/iOS/ScriptWidget/View/CodeEditor/MirrorEditor/StudioEditor.bundle/assets/index-BmJkN3Xp.js b/iOS/ScriptWidget/View/CodeEditor/MirrorEditor/StudioEditor.bundle/assets/index-BaJfekQk.js similarity index 99% rename from iOS/ScriptWidget/View/CodeEditor/MirrorEditor/StudioEditor.bundle/assets/index-BmJkN3Xp.js rename to iOS/ScriptWidget/View/CodeEditor/MirrorEditor/StudioEditor.bundle/assets/index-BaJfekQk.js index 3e5e71a6..0f976695 100644 --- a/iOS/ScriptWidget/View/CodeEditor/MirrorEditor/StudioEditor.bundle/assets/index-BmJkN3Xp.js +++ b/iOS/ScriptWidget/View/CodeEditor/MirrorEditor/StudioEditor.bundle/assets/index-BaJfekQk.js @@ -21,7 +21,7 @@ \${} } }`,{label:"class",detail:"definition",type:"keyword"}),Ye('import {${names}} from "${module}"\n${}',{label:"import",detail:"named",type:"keyword"}),Ye('import ${name} from "${module}"\n${}',{label:"import",detail:"default",type:"keyword"})],hb=od.concat([Ye("interface ${name} {\n ${}\n}",{label:"interface",detail:"definition",type:"keyword"}),Ye("type ${name} = ${type}",{label:"type",detail:"definition",type:"keyword"}),Ye("enum ${name} {\n ${}\n}",{label:"enum",detail:"definition",type:"keyword"})]),Gc=new dg,ld=new Set(["Script","Block","FunctionExpression","FunctionDeclaration","ArrowFunction","MethodDeclaration","ForStatement"]);function cn(n){return(e,t)=>{let i=e.node.getChild("VariableDefinition");return i&&t(i,n),!0}}const ub=["FunctionDeclaration"],fb={FunctionDeclaration:cn("function"),ClassDeclaration:cn("class"),ClassExpression:()=>!0,EnumDeclaration:cn("constant"),TypeAliasDeclaration:cn("type"),NamespaceDeclaration:cn("namespace"),VariableDefinition(n,e){n.matchContext(ub)||e(n,"variable")},TypeDefinition(n,e){e(n,"type")},__proto__:null};function ad(n,e){let t=Gc.get(e);if(t)return t;let i=[],r=!0;function s(o,l){let a=n.sliceString(o.from,o.to);i.push({label:a,type:l})}return e.cursor(be.IncludeAnonymous).iterate(o=>{if(r)r=!1;else if(o.name){let l=fb[o.name];if(l&&l(o,s)||ld.has(o.name))return!1}else if(o.to-o.from>8192){for(let l of ad(n,o.node))i.push(l);return!1}}),Gc.set(e,i),i}const Hc=/^[\w$\xa1-\uffff][\w$\d\xa1-\uffff]*$/,cd=["TemplateString","String","RegExp","LineComment","BlockComment","VariableDefinition","TypeDefinition","Label","PropertyDefinition","PropertyName","PrivatePropertyDefinition","PrivatePropertyName","JSXText","JSXAttributeValue","JSXOpenTag","JSXCloseTag","JSXSelfClosingTag",".","?."];function db(n){let e=Ce(n.state).resolveInner(n.pos,-1);if(cd.indexOf(e.name)>-1)return null;let t=e.name=="VariableName"||e.to-e.from<20&&Hc.test(n.state.sliceDoc(e.from,e.to));if(!t&&!n.explicit)return null;let i=[];for(let r=e;r;r=r.parent)ld.has(r.name)&&(i=i.concat(ad(n.state.doc,r)));return{options:i,from:t?e.from:n.pos,validFor:Hc}}const Lt=Lr.define({name:"javascript",parser:cb.configure({props:[ef.add({IfStatement:Ns({except:/^\s*({|else\b)/}),TryStatement:Ns({except:/^\s*({|catch\b|finally\b)/}),LabeledStatement:Mg,SwitchBody:n=>{let e=n.textAfter,t=/^\s*\}/.test(e),i=/^\s*(case|default)\b/.test(e);return n.baseIndent+(t?0:i?1:2)*n.unit},Block:Zg({closing:"}"}),ArrowFunction:n=>n.baseIndent+n.unit,"TemplateString BlockComment":()=>null,"Statement Property":Ns({except:/^\s*{/}),JSXElement(n){let e=/^\s*<\//.test(n.textAfter);return n.lineIndent(n.node.from)+(e?0:n.unit)},JSXEscape(n){let e=/\s*\}/.test(n.textAfter);return n.lineIndent(n.node.from)+(e?0:n.unit)},"JSXOpenTag JSXSelfClosingTag"(n){return n.column(n.node.from)+n.unit}}),rf.add({"Block ClassBody SwitchBody EnumBody ObjectExpression ArrayExpression ObjectType":Dg,BlockComment(n){return{from:n.from+2,to:n.to-2}},JSXElement(n){let e=n.firstChild;if(!e||e.name=="JSXSelfClosingTag")return null;let t=n.lastChild;return{from:e.to,to:t.type.isError?n.to:t.from}},"JSXSelfClosingTag JSXOpenTag"(n){var e;let t=(e=n.firstChild)===null||e===void 0?void 0:e.nextSibling,i=n.lastChild;return!t||t.type.isError?null:{from:t.to,to:i.type.isError?n.to:i.from}}})]}),languageData:{closeBrackets:{brackets:["(","[","{","'",'"',"`"]},commentTokens:{line:"//",block:{open:"/*",close:"*/"}},indentOnInput:/^\s*(?:case |default:|\{|\}|<\/)$/,wordChars:"$"}}),hd={test:n=>/^JSX/.test(n.name),facet:_u({commentTokens:{block:{open:"{/*",close:"*/}"}}})},pb=Lt.configure({dialect:"ts"},"typescript"),mb=Lt.configure({dialect:"jsx",props:[Zl.add(n=>n.isTop?[hd]:void 0)]}),Ob=Lt.configure({dialect:"jsx ts",props:[Zl.add(n=>n.isTop?[hd]:void 0)]},"typescript");let ud=n=>({label:n,type:"keyword"});const fd="break case const continue default delete export extends false finally in instanceof let new return static super switch this throw true typeof var yield".split(" ").map(ud),gb=fd.concat(["declare","implements","private","protected","public"].map(ud));function yb(n={}){let e=n.jsx?n.typescript?Ob:mb:n.typescript?pb:Lt,t=n.typescript?hb.concat(gb):od.concat(fd);return new kg(e,[Lt.data.of({autocomplete:Ty(cd,Yf(t))}),Lt.data.of({autocomplete:db}),n.jsx?Sb:[]])}function Ab(n){for(;;){if(n.name=="JSXOpenTag"||n.name=="JSXSelfClosingTag"||n.name=="JSXFragmentTag")return n;if(n.name=="JSXEscape"||!n.parent)return null;n=n.parent}}function _c(n,e,t=n.length){for(let i=e?.firstChild;i;i=i.nextSibling)if(i.name=="JSXIdentifier"||i.name=="JSXBuiltin"||i.name=="JSXNamespacedName"||i.name=="JSXMemberExpression")return n.sliceString(i.from,Math.min(i.to,t));return""}const bb=typeof navigator=="object"&&/Android\b/.test(navigator.userAgent),Sb=R.inputHandler.of((n,e,t,i,r)=>{if((bb?n.composing:n.compositionStarted)||n.state.readOnly||e!=t||i!=">"&&i!="/"||!Lt.isActiveAt(n.state,e,-1))return!1;let s=r(),{state:o}=s,l=o.changeByRange(a=>{var c;let{head:h}=a,u=Ce(o).resolveInner(h-1,-1),f;if(u.name=="JSXStartTag"&&(u=u.parent),!(o.doc.sliceString(h-1,h)!=i||u.name=="JSXAttributeValue"&&u.to>h)){if(i==">"&&u.name=="JSXFragmentTag")return{range:a,changes:{from:h,insert:""}};if(i=="/"&&u.name=="JSXStartCloseTag"){let d=u.parent,p=d.parent;if(p&&d.from==h-2&&((f=_c(o.doc,p.firstChild,h))||((c=p.firstChild)===null||c===void 0?void 0:c.name)=="JSXFragmentTag")){let m=`${f}>`;return{range:A.cursor(h+m.length,-1),changes:{from:h,insert:m}}}}else if(i==">"){let d=Ab(u);if(d&&d.name=="JSXOpenTag"&&!/^\/?>|^<\//.test(o.doc.sliceString(h,h+2))&&(f=_c(o.doc,d,h)))return{range:a,changes:{from:h,insert:``}}}}return{range:a}});return l.changes.empty?!1:(n.dispatch([s,o.update(l,{userEvent:"input.complete",scrollIntoView:!0})]),!0)}),Jc=typeof String.prototype.normalize=="function"?n=>n.normalize("NFKD"):n=>n;class Yi{constructor(e,t,i=0,r=e.length,s,o){this.test=o,this.value={from:0,to:0,precise:!1},this.done=!1,this.matches=[],this.buffer="",this.bufferPos=0,this.iter=e.iterRange(i,r),this.bufferStart=i,this.normalize=s?l=>s(Jc(l)):Jc,this.query=this.normalize(t)}peek(){if(this.bufferPos==this.buffer.length){if(this.bufferStart+=this.buffer.length,this.iter.next(),this.iter.done)return-1;this.bufferPos=0,this.buffer=this.iter.value}return Ne(this.buffer,this.bufferPos)}next(){for(;this.matches.length;)this.matches.pop();return this.nextOverlapping()}nextOverlapping(){for(;;){let e=this.peek();if(e<0)return this.done=!0,this;let t=fl(e),i=this.bufferStart+this.bufferPos;this.bufferPos+=Qt(e);let r=this.normalize(t);if(r.length)for(let s=0,o=i,l=!0;;s++){let a=r.charCodeAt(s),c=this.match(a,o,l,this.bufferPos+this.bufferStart,s==r.length-1);if(c)return this.value=c,this;if(s==r.length-1)break;l&&sthis.to&&(this.curLine=this.curLine.slice(0,this.to-this.curLineStart)),this.iter.next())}nextLine(){this.curLineStart=this.curLineStart+this.curLine.length+1,this.curLineStart>this.to?this.curLine="":this.getLine(0)}next(){for(let e=this.matchPos-this.curLineStart;;){this.re.lastIndex=e;let t=this.matchPos<=this.to&&this.re.exec(this.curLine);if(t){let i=this.curLineStart+t.index,r=i+t[0].length;if(this.matchPos=_r(this.text,r+(i==r?1:0)),i==this.curLineStart+this.curLine.length&&this.nextLine(),(ithis.value.to)&&(!this.test||this.test(i,r,t)))return this.value={from:i,to:r,precise:!0,match:t},this;e=this.matchPos-this.curLineStart}else if(this.curLineStart+this.curLine.length=i||r.to<=t){let l=new Xi(t,e.sliceString(t,i));return oo.set(e,l),l}if(r.from==t&&r.to==i)return r;let{text:s,from:o}=r;return o>t&&(s=e.sliceString(t,o)+s,o=t),r.to=this.to?this.to:this.text.lineAt(e).to}next(){for(;;){let e=this.re.lastIndex=this.matchPos-this.flat.from,t=this.re.exec(this.flat.text);if(t&&!t[0]&&t.index==e&&(this.re.lastIndex=e+1,t=this.re.exec(this.flat.text)),t){let i=this.flat.from+t.index,r=i+t[0].length;if((this.flat.to>=this.to||t.index+t[0].length<=this.flat.text.length-10)&&(!this.test||this.test(i,r,t)))return this.value={from:i,to:r,precise:!0,match:t},this.matchPos=_r(this.text,r+(i==r?1:0)),this}if(this.flat.to==this.to)return this.done=!0,this;this.flat=Xi.get(this.text,this.flat.from,this.chunkEnd(this.flat.from+this.flat.text.length*2))}}}typeof Symbol<"u"&&(pd.prototype[Symbol.iterator]=md.prototype[Symbol.iterator]=function(){return this});function xb(n){try{return new RegExp(n,Nl),!0}catch{return!1}}function _r(n,e){if(e>=n.length)return e;let t=n.lineAt(e),i;for(;e=56320&&i<57344;)e++;return e}const wb=n=>{let{state:e}=n,t=String(e.doc.lineAt(n.state.selection.main.head).number),{close:i,result:r}=VO(n,{label:e.phrase("Go to line"),input:{type:"text",name:"line",value:t},focus:!0,submitLabel:e.phrase("go")});return r.then(s=>{let o=s&&/^([+-])?(\d+)?(:\d+)?(%)?$/.exec(s.elements.line.value);if(!o){n.dispatch({effects:i});return}let l=e.doc.lineAt(e.selection.main.head),[,a,c,h,u]=o,f=h?+h.slice(1):0,d=c?+c:l.number;if(c&&u){let O=d/100;a&&(O=O*(a=="-"?-1:1)+l.number/e.doc.lines),d=Math.round(e.doc.lines*O)}else c&&a&&(d=d*(a=="-"?-1:1)+l.number);let p=e.doc.line(Math.max(1,Math.min(e.doc.lines,d))),m=A.cursor(p.from+Math.max(0,Math.min(f,p.length)));n.dispatch({effects:[i,R.scrollIntoView(m.from,{y:"center"})],selection:m})}),!0},vb={highlightWordAroundCursor:!1,minSelectionLength:1,maxMatches:100,wholeWords:!1},kb=K.define({combine(n){return yt(n,vb,{highlightWordAroundCursor:(e,t)=>e||t,minSelectionLength:Math.min,maxMatches:Math.min})}});function Qb(n){return[Rb,$b]}const Pb=B.mark({class:"cm-selectionMatch"}),Cb=B.mark({class:"cm-selectionMatch cm-selectionMatch-main"});function eh(n,e,t,i){return(t==0||n(e.sliceDoc(t-1,t))!=ye.Word)&&(i==e.doc.length||n(e.sliceDoc(i,i+1))!=ye.Word)}function Tb(n,e,t,i){return n(e.sliceDoc(t,t+1))==ye.Word&&n(e.sliceDoc(i-1,i))==ye.Word}const $b=xe.fromClass(class{constructor(n){this.decorations=this.getDeco(n)}update(n){(n.selectionSet||n.docChanged||n.viewportChanged)&&(this.decorations=this.getDeco(n.view))}getDeco(n){let e=n.state.facet(kb),{state:t}=n,i=t.selection;if(i.ranges.length>1)return B.none;let r=i.main,s,o=null;if(r.empty){if(!e.highlightWordAroundCursor)return B.none;let a=t.wordAt(r.head);if(!a)return B.none;o=t.charCategorizer(r.head),s=t.sliceDoc(a.from,a.to)}else{let a=r.to-r.from;if(a200)return B.none;if(e.wholeWords){if(s=t.sliceDoc(r.from,r.to),o=t.charCategorizer(r.head),!(eh(o,t,r.from,r.to)&&Tb(o,t,r.from,r.to)))return B.none}else if(s=t.sliceDoc(r.from,r.to),!s)return B.none}let l=[];for(let a of n.visibleRanges){let c=new Yi(t.doc,s,a.from,a.to);for(;!c.next().done;){let{from:h,to:u}=c.value;if((!o||eh(o,t,h,u))&&(r.empty&&h<=r.from&&u>=r.to?l.push(Cb.range(h,u)):(h>=r.to||u<=r.from)&&l.push(Pb.range(h,u)),l.length>e.maxMatches))return B.none}}return B.set(l)}},{decorations:n=>n.decorations}),Rb=R.baseTheme({".cm-selectionMatch":{backgroundColor:"#99ff7780"},".cm-searchMatch .cm-selectionMatch":{backgroundColor:"transparent"}}),Kb=({state:n,dispatch:e})=>{let{selection:t}=n,i=A.create(t.ranges.map(r=>n.wordAt(r.head)||A.cursor(r.head)),t.mainIndex);return i.eq(t)?!1:(e(n.update({selection:i})),!0)};function Zb(n,e){let{main:t,ranges:i}=n.selection,r=n.wordAt(t.head),s=r&&r.from==t.from&&r.to==t.to;for(let o=!1,l=new Yi(n.doc,e,i[i.length-1].to);;)if(l.next(),l.done){if(o)return null;l=new Yi(n.doc,e,0,Math.max(0,i[i.length-1].from-1)),o=!0}else{if(o&&i.some(a=>a.from==l.value.from))continue;if(s){let a=n.wordAt(l.value.from);if(!a||a.from!=l.value.from||a.to!=l.value.to)continue}return l.value}}const Mb=({state:n,dispatch:e})=>{let{ranges:t}=n.selection;if(t.some(s=>s.from===s.to))return Kb({state:n,dispatch:e});let i=n.sliceDoc(t[0].from,t[0].to);if(n.selection.ranges.some(s=>n.sliceDoc(s.from,s.to)!=i))return!1;let r=Zb(n,i);return r?(e(n.update({selection:n.selection.addRange(A.range(r.from,r.to),!1),effects:R.scrollIntoView(r.to)})),!0):!1},tn=K.define({combine(n){return yt(n,{top:!1,caseSensitive:!1,literal:!1,regexp:!1,wholeWord:!1,createPanel:e=>new Yb(e),scrollToMatch:e=>R.scrollIntoView(e)})}});class Od{constructor(e){this.search=e.search,this.caseSensitive=!!e.caseSensitive,this.literal=!!e.literal,this.regexp=!!e.regexp,this.replace=e.replace||"",this.valid=!!this.search&&(!this.regexp||xb(this.search)),this.unquoted=this.unquote(this.search),this.wholeWord=!!e.wholeWord,this.test=e.test}unquote(e){return this.literal?e:e.replace(/\\([nrt\\])/g,(t,i)=>i=="n"?` -`:i=="r"?"\r":i=="t"?" ":"\\")}eq(e){return this.search==e.search&&this.replace==e.replace&&this.caseSensitive==e.caseSensitive&&this.regexp==e.regexp&&this.wholeWord==e.wholeWord&&this.test==e.test}create(){return this.regexp?new Lb(this):new Fb(this)}getCursor(e,t=0,i){let r=e.doc?e:_.create({doc:e});return i==null&&(i=r.doc.length),this.regexp?Pi(this,r,t,i):Qi(this,r,t,i)}}class gd{constructor(e){this.spec=e}}function Eb(n,e,t){return(i,r,s,o)=>{if(t&&!t(i,r,s,o))return!1;let l=i>=o&&r<=o+s.length?s.slice(i-o,r-o):e.doc.sliceString(i,r);return n(l,e,i,r)}}function Qi(n,e,t,i){let r;return n.wholeWord&&(r=Ub(e.doc,e.charCategorizer(e.selection.main.head))),n.test&&(r=Eb(n.test,e,r)),new Yi(e.doc,n.unquoted,t,i,n.caseSensitive?void 0:s=>s.toLowerCase(),r)}function Ub(n,e){return(t,i,r,s)=>((s>t||s+r.length=t)return null;r.push(i.value)}return r}highlight(e,t,i,r){let s=Qi(this.spec,e,Math.max(0,t-this.spec.unquoted.length),Math.min(i+this.spec.unquoted.length,e.doc.length));for(;!s.next().done;)r(s.value.from,s.value.to)}}function Db(n,e,t){return(i,r,s)=>(!t||t(i,r,s))&&n(s[0],e,i,r)}function Pi(n,e,t,i){let r;return n.wholeWord&&(r=Xb(e.charCategorizer(e.selection.main.head))),n.test&&(r=Db(n.test,e,r)),new pd(e.doc,n.search,{ignoreCase:!n.caseSensitive,test:r},t,i)}function Jr(n,e){return n.slice(Ze(n,e,!1),e)}function es(n,e){return n.slice(e,Ze(n,e))}function Xb(n){return(e,t,i)=>!i[0].length||(n(Jr(i.input,i.index))!=ye.Word||n(es(i.input,i.index))!=ye.Word)&&(n(es(i.input,i.index+i[0].length))!=ye.Word||n(Jr(i.input,i.index+i[0].length))!=ye.Word)}class Lb extends gd{nextMatch(e,t,i){let r=Pi(this.spec,e,i,e.doc.length).next();return r.done&&(r=Pi(this.spec,e,0,t).next()),r.done?null:r.value}prevMatchInRange(e,t,i){for(let r=1;;r++){let s=Math.max(t,i-r*1e4),o=Pi(this.spec,e,s,i),l=null;for(;!o.next().done;)l=o.value;if(l&&(s==t||l.from>s+10))return l;if(s==t)return null}}prevMatch(e,t,i){return this.prevMatchInRange(e,0,t)||this.prevMatchInRange(e,i,e.doc.length)}getReplacement(e){return this.spec.unquote(this.spec.replace).replace(/\$([$&]|\d+)/g,(t,i)=>{if(i=="&")return e.match[0];if(i=="$")return"$";for(let r=i.length;r>0;r--){let s=+i.slice(0,r);if(s>0&&s=t)return null;r.push(i.value)}return r}highlight(e,t,i,r){let s=Pi(this.spec,e,Math.max(0,t-250),Math.min(i+250,e.doc.length));for(;!s.next().done;)r(s.value.from,s.value.to)}}const Zn=W.define(),Gl=W.define(),Ht=Te.define({create(n){return new lo(ll(n).create(),null)},update(n,e){for(let t of e.effects)t.is(Zn)?n=new lo(t.value.create(),n.panel):t.is(Gl)&&(n=new lo(n.query,t.value?Hl:null));return n},provide:n=>ji.from(n,e=>e.panel)});class lo{constructor(e,t){this.query=e,this.panel=t}}const Bb=B.mark({class:"cm-searchMatch"}),qb=B.mark({class:"cm-searchMatch cm-searchMatch-selected"}),Ib=xe.fromClass(class{constructor(n){this.view=n,this.decorations=this.highlight(n.state.field(Ht))}update(n){let e=n.state.field(Ht);(e!=n.startState.field(Ht)||n.docChanged||n.selectionSet||n.viewportChanged)&&(this.decorations=this.highlight(e))}highlight({query:n,panel:e}){if(!e||!n.spec.valid)return B.none;let{view:t}=this,i=new Bt;for(let r=0,s=t.visibleRanges,o=s.length;rs[r+1].from-500;)a=s[++r].to;n.highlight(t.state,l,a,(c,h)=>{let u=t.state.selection.ranges.some(f=>f.from==c&&f.to==h);i.add(c,h,u?qb:Bb)})}return i.finish()}},{decorations:n=>n.decorations});function zn(n){return e=>{let t=e.state.field(Ht,!1);return t&&t.query.spec.valid?n(e,t):bd(e)}}const ts=zn((n,{query:e})=>{let{to:t}=n.state.selection.main,i=e.nextMatch(n.state,t,t);if(!i)return!1;let r=A.single(i.from,i.to),s=n.state.facet(tn);return n.dispatch({selection:r,effects:[_l(n,i),s.scrollToMatch(r.main,n)],userEvent:"select.search"}),Ad(n),!0}),is=zn((n,{query:e})=>{let{state:t}=n,{from:i}=t.selection.main,r=e.prevMatch(t,i,i);if(!r)return!1;let s=A.single(r.from,r.to),o=n.state.facet(tn);return n.dispatch({selection:s,effects:[_l(n,r),o.scrollToMatch(s.main,n)],userEvent:"select.search"}),Ad(n),!0}),zb=zn((n,{query:e})=>{let t=e.matchAll(n.state,1e3);return!t||!t.length?!1:(n.dispatch({selection:A.create(t.map(i=>A.range(i.from,i.to))),userEvent:"select.search.matches"}),!0)}),Wb=({state:n,dispatch:e})=>{let t=n.selection;if(t.ranges.length>1||t.main.empty)return!1;let{from:i,to:r}=t.main,s=[],o=0;for(let l=new Yi(n.doc,n.sliceDoc(i,r));!l.next().done;){if(s.length>1e3)return!1;l.value.from==i&&(o=s.length),s.push(A.range(l.value.from,l.value.to))}return e(n.update({selection:A.create(s,o),userEvent:"select.search.matches"})),!0},th=zn((n,{query:e})=>{let{state:t}=n,{from:i,to:r}=t.selection.main;if(t.readOnly)return!1;let s=e.nextMatch(t,i,i);if(!s)return!1;let o=s,l=[],a,c,h=[];o.precise?o.from==i&&o.to==r&&(c=t.toText(e.getReplacement(o)),l.push({from:o.from,to:o.to,insert:c}),o=e.nextMatch(t,o.from,o.to),h.push(R.announce.of(t.phrase("replaced match on line $",t.doc.lineAt(i).number)+"."))):o=e.nextMatch(t,o.from,o.to);let u=n.state.changes(l);return o&&(a=A.single(o.from,o.to).map(u),h.push(_l(n,o)),h.push(t.facet(tn).scrollToMatch(a.main,n))),n.dispatch({changes:u,selection:a,effects:h,userEvent:"input.replace"}),!0}),jb=zn((n,{query:e})=>{if(n.state.readOnly)return!1;let t=[];for(let r of e.matchAll(n.state,1e9)){let{from:s,to:o,precise:l}=r;l&&t.push({from:s,to:o,insert:e.getReplacement(r)})}if(!t.length)return!1;let i=n.state.phrase("replaced $ matches",t.length)+".";return n.dispatch({changes:t,effects:R.announce.of(i),userEvent:"input.replace.all"}),!0});function Hl(n){return n.state.facet(tn).createPanel(n)}function ll(n,e){var t,i,r,s,o;let l=n.selection.main,a=l.empty||l.to>l.from+100?"":n.sliceDoc(l.from,l.to);if(e&&!a)return e;let c=n.facet(tn);return new Od({search:((t=e?.literal)!==null&&t!==void 0?t:c.literal)?a:a.replace(/\n/g,"\\n"),caseSensitive:(i=e?.caseSensitive)!==null&&i!==void 0?i:c.caseSensitive,literal:(r=e?.literal)!==null&&r!==void 0?r:c.literal,regexp:(s=e?.regexp)!==null&&s!==void 0?s:c.regexp,wholeWord:(o=e?.wholeWord)!==null&&o!==void 0?o:c.wholeWord})}function yd(n){let e=Fu(n,Hl);return e&&e.dom.querySelector("[main-field]")}function Ad(n){let e=yd(n);e&&e==n.root.activeElement&&e.select()}const bd=n=>{let e=n.state.field(Ht,!1);if(e&&e.panel){let t=yd(n);if(t&&t!=n.root.activeElement){let i=ll(n.state,e.query.spec);i.valid&&n.dispatch({effects:Zn.of(i)}),t.focus(),t.select()}}else n.dispatch({effects:[Gl.of(!0),e?Zn.of(ll(n.state,e.query.spec)):W.appendConfig.of(Gb)]});return!0},Sd=n=>{let e=n.state.field(Ht,!1);if(!e||!e.panel)return!1;let t=Fu(n,Hl);return t&&t.dom.contains(n.root.activeElement)&&n.focus(),n.dispatch({effects:Gl.of(!1)}),!0},Vb=[{key:"Mod-f",run:bd,scope:"editor search-panel"},{key:"F3",run:ts,shift:is,scope:"editor search-panel",preventDefault:!0},{key:"Mod-g",run:ts,shift:is,scope:"editor search-panel",preventDefault:!0},{key:"Escape",run:Sd,scope:"editor search-panel"},{key:"Mod-Shift-l",run:Wb},{key:"Mod-Alt-g",run:wb},{key:"Mod-d",run:Mb,preventDefault:!0}];class Yb{constructor(e){this.view=e;let t=this.query=e.state.field(Ht).query.spec;this.commit=this.commit.bind(this),this.searchField=he("input",{value:t.search,placeholder:rt(e,"Find"),"aria-label":rt(e,"Find"),class:"cm-textfield",name:"search",form:"","main-field":"true",onchange:this.commit,onkeyup:this.commit}),this.replaceField=he("input",{value:t.replace,placeholder:rt(e,"Replace"),"aria-label":rt(e,"Replace"),class:"cm-textfield",name:"replace",form:"",onchange:this.commit,onkeyup:this.commit}),this.caseField=he("input",{type:"checkbox",name:"case",form:"",checked:t.caseSensitive,onchange:this.commit}),this.reField=he("input",{type:"checkbox",name:"re",form:"",checked:t.regexp,onchange:this.commit}),this.wordField=he("input",{type:"checkbox",name:"word",form:"",checked:t.wholeWord,onchange:this.commit});function i(r,s,o){return he("button",{class:"cm-button",name:r,onclick:s,type:"button"},o)}this.dom=he("div",{onkeydown:r=>this.keydown(r),class:"cm-search"},[this.searchField,i("next",()=>ts(e),[rt(e,"next")]),i("prev",()=>is(e),[rt(e,"previous")]),i("select",()=>zb(e),[rt(e,"all")]),he("label",null,[this.caseField,rt(e,"match case")]),he("label",null,[this.reField,rt(e,"regexp")]),he("label",null,[this.wordField,rt(e,"by word")]),...e.state.readOnly?[]:[he("br"),this.replaceField,i("replace",()=>th(e),[rt(e,"replace")]),i("replaceAll",()=>jb(e),[rt(e,"replace all")])],he("button",{name:"close",onclick:()=>Sd(e),"aria-label":rt(e,"close"),type:"button"},["×"])])}commit(){let e=new Od({search:this.searchField.value,caseSensitive:this.caseField.checked,regexp:this.reField.checked,wholeWord:this.wordField.checked,replace:this.replaceField.value});e.eq(this.query)||(this.query=e,this.view.dispatch({effects:Zn.of(e)}))}keydown(e){rO(this.view,e,"search-panel")?e.preventDefault():e.keyCode==13&&e.target==this.searchField?(e.preventDefault(),(e.shiftKey?is:ts)(this.view)):e.keyCode==13&&e.target==this.replaceField&&(e.preventDefault(),th(this.view))}update(e){for(let t of e.transactions)for(let i of t.effects)i.is(Zn)&&!i.value.eq(this.query)&&this.setQuery(i.value)}setQuery(e){this.query=e,this.searchField.value=e.search,this.replaceField.value=e.replace,this.caseField.checked=e.caseSensitive,this.reField.checked=e.regexp,this.wordField.checked=e.wholeWord}mount(){this.searchField.select()}get pos(){return 80}get top(){return this.view.state.facet(tn).top}}function rt(n,e){return n.state.phrase(e)}const fr=30,dr=/[\s\.,:;?!]/;function _l(n,{from:e,to:t}){let i=n.state.doc.lineAt(e),r=n.state.doc.lineAt(t).to,s=Math.max(i.from,e-fr),o=Math.min(r,t+fr),l=n.state.sliceDoc(s,o);if(s!=i.from){for(let a=0;al.length-fr;a--)if(!dr.test(l[a-1])&&dr.test(l[a])){l=l.slice(0,a);break}}return R.announce.of(`${n.state.phrase("current match")}. ${l} ${n.state.phrase("on line")} ${i.number}.`)}const Nb=R.baseTheme({".cm-panel.cm-search":{padding:"2px 6px 4px",position:"relative","& [name=close]":{position:"absolute",top:"0",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",padding:0,margin:0},"& input, & button, & label":{margin:".2em .6em .2em 0"},"& input[type=checkbox]":{marginRight:".2em"},"& label":{fontSize:"80%",whiteSpace:"pre"}},"&light .cm-searchMatch":{backgroundColor:"#ffff0054"},"&dark .cm-searchMatch":{backgroundColor:"#00ffff8a"},"&light .cm-searchMatch-selected":{backgroundColor:"#ff6a0054"},"&dark .cm-searchMatch-selected":{backgroundColor:"#ff00ff8a"}}),Gb=[Ht,si.low(Ib),Nb];class ih{constructor(e,t,i){this.from=e,this.to=t,this.diagnostic=i}}class di{constructor(e,t,i){this.diagnostics=e,this.panel=t,this.selected=i}static init(e,t,i){let r=i.facet(Kt).markerFilter;r&&(e=r(e,i));let s=e.slice().sort((d,p)=>d.from-p.from||d.to-p.to),o=new Bt,l=[],a=0,c=i.doc.iter(),h=0,u=i.doc.length;for(let d=0;;){let p=d==s.length?null:s[d];if(!p&&!l.length)break;let m,O;if(l.length)m=a,O=l.reduce((x,C)=>Math.min(x,C.to),p&&p.from>m?p.from:1e8);else{if(m=p.from,m>u)break;O=p.to,l.push(p),d++}for(;dx.from||x.to==m))l.push(x),d++,O=Math.min(x.to,O);else{O=Math.min(x.from,O);break}}O=Math.min(O,u);let y=!1;if(l.some(x=>x.from==m&&(x.to==O||O==u))&&(y=m==O,!y&&O-m<10)){let x=m-(h+c.value.length);x>0&&(c.next(x),h=m);for(let C=m;;){if(C>=O){y=!0;break}if(!c.lineBreak&&h+c.value.length>C)break;C=h+c.value.length,h+=c.value.length,c.next()}}let b=Td(l);if(y)o.add(m,m,B.widget({widget:new n1(b),diagnostics:l.slice()}));else{let x=l.reduce((C,k)=>k.markClass?C+" "+k.markClass:C,"");o.add(m,O,B.mark({class:"cm-lintRange cm-lintRange-"+b+x,diagnostics:l.slice(),inclusiveEnd:l.some(C=>C.to>O)}))}if(a=O,a==u)break;for(let x=0;x{if(!(e&&o.diagnostics.indexOf(e)<0))if(!i)i=new ih(r,s,e||o.diagnostics[0]);else{if(o.diagnostics.indexOf(i.diagnostic)<0)return!1;i=new ih(i.from,s,i.diagnostic)}}),i}function xd(n,e){let t=e.pos,i=e.end||t,r=n.state.facet(Kt).hideOn(n,t,i);if(r!=null)return r;let s=n.startState.doc.lineAt(e.pos);return!!(n.effects.some(o=>o.is(Qs))||n.changes.touchesRange(s.from,Math.max(s.to,i)))}function Hb(n,e){return n.field(Ot,!1)?e:e.concat(W.appendConfig.of(Kd))}function wd(n,e){return{effects:Hb(n,[Qs.of(e)])}}const Qs=W.define(),vd=W.define(),kd=W.define(),Ot=Te.define({create(){return new di(B.none,null,null)},update(n,e){if(e.docChanged&&n.diagnostics.size){let t=n.diagnostics.map(e.changes),i=null,r=n.panel;if(n.selected){let s=e.changes.mapPos(n.selected.from,1);i=Ni(t,n.selected.diagnostic,s)||Ni(t,null,s)}!t.size&&r&&e.state.facet(Kt).autoPanel&&(r=null),n=new di(t,r,i)}for(let t of e.effects)if(t.is(Qs)){let i=e.state.facet(Kt).autoPanel?t.value.length?ns.open:null:n.panel;n=di.init(t.value,i,e.state)}else t.is(vd)?n=new di(n.diagnostics,t.value?ns.open:null,n.selected):t.is(kd)&&(n=new di(n.diagnostics,n.panel,t.value));return n},provide:n=>[ji.from(n,e=>e.panel),R.decorations.from(n,e=>e.diagnostics)]}),_b=B.mark({class:"cm-lintRange cm-lintRange-active"});function Jb(n,e,t){let{diagnostics:i}=n.state.field(Ot),r,s=-1,o=-1;i.between(e-(t<0?1:0),e+(t>0?1:0),(a,c,{spec:h})=>{if(e>=a&&e<=c&&(a==c||(e>a||t>0)&&(eCd(n,t,!1)))}const nh=n=>{let e=n.state.field(Ot,!1);return!e||!e.panel?!1:(n.dispatch({effects:vd.of(!1)}),!0)},e1=xe.fromClass(class{constructor(n){this.view=n,this.timeout=-1,this.set=!0;let{delay:e}=n.state.facet(Kt);this.lintTime=Date.now()+e,this.run=this.run.bind(this),this.timeout=setTimeout(this.run,e)}run(){clearTimeout(this.timeout);let n=Date.now();if(nPromise.resolve(i(this.view))),i=>{this.view.state.doc==e.doc&&this.view.dispatch(wd(this.view.state,i.reduce((r,s)=>r.concat(s))))},i=>{We(this.view.state,i)})}}update(n){let e=n.state.facet(Kt);(n.docChanged||e!=n.startState.facet(Kt)||e.needsRefresh&&e.needsRefresh(n))&&(this.lintTime=Date.now()+e.delay,this.set||(this.set=!0,this.timeout=setTimeout(this.run,e.delay)))}force(){this.set&&(this.lintTime=Date.now(),this.run())}destroy(){clearTimeout(this.timeout)}});function t1(n,e,t){let i=[],r=-1;for(let s of n)s.then(o=>{i.push(o),clearTimeout(r),i.length==n.length?e(i):r=setTimeout(()=>e(i),200)},t)}const Kt=K.define({combine(n){return{sources:n.map(e=>e.source).filter(e=>e!=null),...yt(n.map(e=>e.config),{delay:750,markerFilter:null,tooltipFilter:null,needsRefresh:null,hideOn:()=>null},{delay:Math.max,markerFilter:rh,tooltipFilter:rh,needsRefresh:(e,t)=>e?t?i=>e(i)||t(i):e:t,hideOn:(e,t)=>e?t?(i,r,s)=>e(i,r,s)||t(i,r,s):e:t,autoPanel:(e,t)=>e||t})}}});function rh(n,e){return n?e?(t,i)=>e(n(t,i),i):n:e}function i1(n,e={}){return[Kt.of({source:n,config:e}),e1,Kd]}function Pd(n){let e=[];if(n)e:for(let{name:t}of n){for(let i=0;is.toLowerCase()==r.toLowerCase())){e.push(r);continue e}}e.push("")}return e}function Cd(n,e,t){var i;let r=t?Pd(e.actions):[];return he("li",{class:"cm-diagnostic cm-diagnostic-"+e.severity},he("span",{class:"cm-diagnosticText"},e.renderMessage?e.renderMessage(n):e.message),(i=e.actions)===null||i===void 0?void 0:i.map((s,o)=>{let l=!1,a=d=>{if(d.preventDefault(),l)return;l=!0;let p=Ni(n.state.field(Ot).diagnostics,e);p&&s.apply(n,p.from,p.to)},{name:c}=s,h=r[o]?c.indexOf(r[o]):-1,u=h<0?c:[c.slice(0,h),he("u",c.slice(h,h+1)),c.slice(h+1)],f=s.markClass?" "+s.markClass:"";return he("button",{type:"button",class:"cm-diagnosticAction"+f,onclick:a,onmousedown:a,"aria-label":` Action: ${c}${h<0?"":` (access key "${r[o]})"`}.`},u)}),e.source&&he("div",{class:"cm-diagnosticSource"},e.source))}class n1 extends Ut{constructor(e){super(),this.sev=e}eq(e){return e.sev==this.sev}toDOM(){return he("span",{class:"cm-lintPoint cm-lintPoint-"+this.sev})}}class sh{constructor(e,t){this.diagnostic=t,this.id="item_"+Math.floor(Math.random()*4294967295).toString(16),this.dom=Cd(e,t,!0),this.dom.id=this.id,this.dom.setAttribute("role","option")}}class ns{constructor(e){this.view=e,this.items=[];let t=r=>{if(!(r.ctrlKey||r.altKey||r.metaKey)){if(r.keyCode==27)nh(this.view),this.view.focus();else if(r.keyCode==38||r.keyCode==33)this.moveSelection((this.selectedIndex-1+this.items.length)%this.items.length);else if(r.keyCode==40||r.keyCode==34)this.moveSelection((this.selectedIndex+1)%this.items.length);else if(r.keyCode==36)this.moveSelection(0);else if(r.keyCode==35)this.moveSelection(this.items.length-1);else if(r.keyCode==13)this.view.focus();else if(r.keyCode>=65&&r.keyCode<=90&&this.selectedIndex>=0){let{diagnostic:s}=this.items[this.selectedIndex],o=Pd(s.actions);for(let l=0;l{for(let s=0;snh(this.view)},"×")),this.update()}get selectedIndex(){let e=this.view.state.field(Ot).selected;if(!e)return-1;for(let t=0;t{for(let h of c.diagnostics){if(o.has(h))continue;o.add(h);let u=-1,f;for(let d=i;di&&(this.items.splice(i,u-i),r=!0)),t&&f.diagnostic==t.diagnostic?f.dom.hasAttribute("aria-selected")||(f.dom.setAttribute("aria-selected","true"),s=f):f.dom.hasAttribute("aria-selected")&&f.dom.removeAttribute("aria-selected"),i++}});i({sel:s.dom.getBoundingClientRect(),panel:this.list.getBoundingClientRect()}),write:({sel:l,panel:a})=>{let c=a.height/this.list.offsetHeight;l.topa.bottom&&(this.list.scrollTop+=(l.bottom-a.bottom)/c)}})):this.selectedIndex<0&&this.list.removeAttribute("aria-activedescendant"),r&&this.sync()}sync(){let e=this.list.firstChild;function t(){let i=e;e=i.nextSibling,i.remove()}for(let i of this.items)if(i.dom.parentNode==this.list){for(;e!=i.dom;)t();e=i.dom.nextSibling}else this.list.insertBefore(i.dom,e);for(;e;)t()}moveSelection(e){if(this.selectedIndex<0)return;let t=this.view.state.field(Ot),i=Ni(t.diagnostics,this.items[e].diagnostic);i&&this.view.dispatch({selection:{anchor:i.from,head:i.to},scrollIntoView:!0,effects:kd.of(i)})}static open(e){return new ns(e)}}function Qr(n,e='viewBox="0 0 40 40"'){return`url('data:image/svg+xml,${encodeURIComponent(n)}')`}function pr(n){return Qr(``,'width="6" height="3"')}const r1=R.baseTheme({".cm-diagnostic":{padding:"3px 6px 3px 8px",marginLeft:"-1px",display:"block",whiteSpace:"pre-wrap"},".cm-diagnostic-error":{borderLeft:"5px solid #d11"},".cm-diagnostic-warning":{borderLeft:"5px solid orange"},".cm-diagnostic-info":{borderLeft:"5px solid #999"},".cm-diagnostic-hint":{borderLeft:"5px solid #66d"},".cm-diagnosticAction":{font:"inherit",border:"none",padding:"2px 4px",backgroundColor:"#444",color:"white",borderRadius:"3px",marginLeft:"8px",cursor:"pointer"},".cm-diagnosticSource":{fontSize:"70%",opacity:.7},".cm-lintRange":{backgroundPosition:"left bottom",backgroundRepeat:"repeat-x",paddingBottom:"0.7px"},".cm-lintRange-error":{backgroundImage:pr("#f11")},".cm-lintRange-warning":{backgroundImage:pr("orange")},".cm-lintRange-info":{backgroundImage:pr("#999")},".cm-lintRange-hint":{backgroundImage:pr("#66d")},".cm-lintRange-active":{backgroundColor:"#ffdd9980"},".cm-tooltip-lint":{padding:0,margin:0},".cm-lintPoint":{position:"relative","&:after":{content:'""',position:"absolute",bottom:0,left:"-2px",borderLeft:"3px solid transparent",borderRight:"3px solid transparent",borderBottom:"4px solid #d11"}},".cm-lintPoint-warning":{"&:after":{borderBottomColor:"orange"}},".cm-lintPoint-info":{"&:after":{borderBottomColor:"#999"}},".cm-lintPoint-hint":{"&:after":{borderBottomColor:"#66d"}},".cm-panel.cm-panel-lint":{position:"relative","& ul":{maxHeight:"100px",overflowY:"auto","& [aria-selected]":{backgroundColor:"#ddd","& u":{textDecoration:"underline"}},"&:focus [aria-selected]":{background_fallback:"#bdf",backgroundColor:"Highlight",color_fallback:"white",color:"HighlightText"},"& u":{textDecoration:"none"},padding:0,margin:0},"& [name=close]":{position:"absolute",top:"0",right:"2px",background:"inherit",border:"none",font:"inherit",padding:0,margin:0}},"&dark .cm-lintRange-active":{backgroundColor:"#86714a80"},"&dark .cm-panel.cm-panel-lint ul":{"& [aria-selected]":{backgroundColor:"#2e343e"}}});function s1(n){return n=="error"?4:n=="warning"?3:n=="info"?2:1}function Td(n){let e="hint",t=1;for(let i of n){let r=s1(i.severity);r>t&&(t=r,e=i.severity)}return e}class $d extends Mt{constructor(e){super(),this.diagnostics=e,this.severity=Td(e)}toDOM(e){let t=document.createElement("div");t.className="cm-lint-marker cm-lint-marker-"+this.severity;let i=this.diagnostics,r=e.state.facet(Ps).tooltipFilter;return r&&(i=r(i,e.state)),i.length&&(t.onmouseover=()=>l1(e,t,i)),t}}function o1(n,e){let t=i=>{let r=e.getBoundingClientRect();if(!(i.clientX>r.left-10&&i.clientXr.top-10&&i.clientYe.getBoundingClientRect()}}})}),e.onmouseout=e.onmousemove=null,o1(n,e)}let{hoverTime:r}=n.state.facet(Ps),s=setTimeout(i,r);e.onmouseout=()=>{clearTimeout(s),e.onmouseout=e.onmousemove=null},e.onmousemove=()=>{clearTimeout(s),s=setTimeout(i,r)}}function a1(n,e){let t=Object.create(null);for(let r of e){let s=n.lineAt(r.from);(t[s.from]||(t[s.from]=[])).push(r)}let i=[];for(let r in t)i.push(new $d(t[r]).range(+r));return N.of(i,!0)}const c1=Bu({class:"cm-gutter-lint",markers:n=>n.state.field(al),widgetMarker:(n,e,t)=>{let i=[];return n.state.field(al).between(t.from,t.to,(r,s,o)=>{r>t.from&&ri.is(Jl)?i.value:t,n)},provide:n=>Os.from(n)}),h1=R.baseTheme({".cm-gutter-lint":{width:"1.4em","& .cm-gutterElement":{padding:".2em"}},".cm-lint-marker":{width:"1em",height:"1em"},".cm-lint-marker-info":{content:Qr('')},".cm-lint-marker-warning":{content:Qr('')},".cm-lint-marker-error":{content:Qr('')}}),u1=Eu(Jb,{hideOn:xd}),Kd=[Ot,R.decorations.compute([Ot],n=>{let{selected:e,panel:t}=n.field(Ot);return!e||!t||e.from==e.to?B.none:B.set([_b.range(e.from,e.to)])}),u1,r1],Ps=K.define({combine(n){return yt(n,{hoverTime:300,markerFilter:null,tooltipFilter:null})}});function f1(n={}){return[Ps.of(n),al,c1,h1,Rd]}const Zd=1,Ue=Object.freeze({ready:"studio.ready",documentOpen:"document.open",documentReplace:"document.replace",documentChanged:"document.changed",documentSave:"document.save",documentSetReadOnly:"document.setReadOnly",documentResources:"document.resources",editorInsert:"editor.insert",editorFormat:"editor.format",editorSetTheme:"editor.setTheme",editorGetState:"editor.getState",diagnosticsPublish:"diagnostics.publish"});function d1(n,e={},t=null,i=null){return{protocolVersion:Zd,type:n,documentID:t,...i===null?{}:{sessionID:i},payload:e}}function p1(n){if(window.WKWebViewJavascriptBridge){n(window.WKWebViewJavascriptBridge);return}if(window.WKWVJBCallbacks){window.WKWVJBCallbacks.push(n);return}window.webkit?.messageHandlers?.iOS_Native_InjectJavascript&&(window.WKWVJBCallbacks=[n],window.webkit.messageHandlers.iOS_Native_InjectJavascript.postMessage(null))}function nn(n,e,t={},i=null,r=()=>{},s=null){if(!n){r({result:"unavailable"});return}n.callHandler(e,d1(e,t,i,s),r)}function m1(n){const e={capabilities:["jsx","format","search","completion","diagnostics"]};nn(n,Ue.ready,e)}const O1=[{id:"$storage.getString",arguments:["key"],returns:"string",documentation:"Read a package-scoped string. Missing keys return an empty string."},{id:"$storage.setString",arguments:["key","value"],returns:"boolean",documentation:"Set a package-scoped string, limited to 256 KiB of UTF-8. Check the success result."},{id:"$storage.getJSON",arguments:["key"],returns:"json",documentation:"Read a package-scoped JSON object. Missing or invalid values return an empty object."},{id:"$storage.setJSON",arguments:["key","value"],returns:"boolean",documentation:"Store a valid JSON object, limited to 256 KiB encoded. Check the success result."},{id:"$storage.compareAndSetString",arguments:["key","expected","value"],returns:"boolean",minimumRuntimeVersion:"1.3",documentation:"Atomically replace a string only when it still matches expected. A missing key matches an empty string. Conflicts return false; reread and retry a bounded number of times. Requires the shared storage container."},{id:"$storage.compareAndSetJSON",arguments:["key","expected","value"],returns:"boolean",minimumRuntimeVersion:"1.3",documentation:"Atomically replace a JSON object if its current value equals expected, independent of property order. A missing key matches an empty object. Wrong types and conflicts return false. Requires the shared storage container."},{id:"$storage.remove",arguments:["key"],returns:"boolean",documentation:"Remove a key from this package only."},{id:"$storage.keys",arguments:[],returns:"string[]",documentation:"List keys in this package's namespace."},{id:"$storage.clear",arguments:[],returns:"boolean",documentation:"Clear this package's storage. Other packages are unaffected."}].map(n=>({minimumRuntimeVersion:"1.0",...n,preview:"initialState"})),cl="2026-01-01T00:00:00Z",Wt="2026-01-01T12:00:00Z",ao=(n,e)=>({value:n,unit:e,hasSamples:!0,start:cl,end:Wt}),oh=(n,e,t)=>Object.entries(e).map(([i,r])=>({id:`$${n}.${i}`,group:n,value:r,legacy:[t,i]})),g1=[{id:"$music.isAvailable",group:"music",value:!0},{id:"$music.authorizationStatus",group:"music",value:"authorized"},{id:"$music.current",group:"music",value:{provider:"appleMusic",state:"playing",hasItem:!0,title:"Morning Light",artist:"ScriptWidget Sessions",album:"Focus",duration:224,position:86,capturedAt:Wt}},{id:"$services.status",group:"services",value:"connected"},{id:"$services.profile",group:"services",value:{service:"github",subject:"42",username:"octocat",displayName:"Mona",publicRepositories:32,privateRepositories:8,followers:128,following:24}},...oh("device",{screen:{width:390,height:844,scale:3},language:"en",model:"iPhone",name:"My iPhone",battery:{level:.8,state:"unplugged"},systemVersion:"26.0",isdarkmode:!1,totalDiskSpace:128e9,freeDiskSpace:64e9},"device"),...oh("system",{appInfo:{name:"ScriptWidget",bundleId:"com.everettjf.scriptwidget",version:"27.1.2",build:"27012"},locale:"en_US",preferredLanguages:["en-US"],timeZone:{identifier:"UTC",abbreviation:"GMT",offsetSeconds:0},is24HourClock:!1,calendarInfo:{identifier:"gregorian",firstWeekday:1,minimumDaysInFirstWeek:1},systemUptime:3600,memory:{physical:8589934592},thermalState:"nominal",lowPowerMode:!1,brightness:.5,reduceMotionEnabled:!1,platform:"ios",hostName:"My iPhone",processName:"ScriptWidgetWidget",osVersionString:"Version 26.0",processorCount:6,activeProcessorCount:6},"system"),{id:"$photos.isAvailable",group:"photos",value:!0},{id:"$photos.authorizationStatus",group:"photos",value:"authorized"},{id:"$photos.requestAuthorization",group:"photos",value:!0},{id:"$photos.items",group:"photos",value:{status:"ready",items:[{id:"sample.photo",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/biiiiu08cKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/biiiiu08cKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKK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8ZPqjNpumMVthwzdC/8A9j/OnGNzOpUUFdkvjHxm2oFtL0psQdHcdX9h/s/z+leb0UVslY86c3N3YUUUUyAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvePgx8bNT+Gl8NN1Hdc6PM2ZIurRE9Xjz39V6H2PNeD0UjSlWlSkpwdmftNo+saZr+mQ6xo0y3FtcLvjkQ5BH9COhB5B4NaVflf8IPjJrHww1LyZN1zpU7Znt88g9N8eejDuOjDg9iP038P+INH8UaRDrmhTrcWs67kdf1BHUEdCDyDUNWPrsFjo4mPZ9Uf//S+tKKKK6z8/CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACmSSRwxtLKwVVGSTwABSTTRW8TTzsERBkseABXhHi3xfLrchsrIlLVT9C5Hc+3oPz9nGNzOpVUFdkvi/xjJrDmw08lbVTyehkI7n29B+ftwNFFbJWPNnNyd2FFFFMkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvWvhP8W9b+F+r+bBm40+dh9otieG7bk/uuB379D2x5LRSLp1JU5KcHZo//0/rSiiius/PwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACobi4gtYWublgiIMsx6AU26ureyt3urpwkaDJY14H4q8V3GvzeTDmO1Q/Kvdvdv8O1OMbmVWqoIk8WeLZtdlNrbZS1Q8Du59T/AEFcXRRWyVjzpScndhRRRTJCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9T60ooorrPz8KKKKACiiigAooooAKKKKACiiigAooooAKKKKACq15eW2n2z3d44SNBkk0y/v7XTbV7y9cJGg5J/kPU18/eJfE114hucnKW6H5I/6n1P8qqMbmVWqoLzJPFHim58QXGxcx2yH5E9fdvf+VcpRRWqVjzpScndhRRRTJCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9X60ooorrPz8KKKKACiiigAooooAKKKKACiiigAooooAKo6jqNppVo17etsRfzJ9B6mm6pqlnpFm17ettReg7k+gHrXz34h8Q3niC786c7Y1+5GOij+p9TVRjcxq1lBeY/xH4ku/EN1vk+SFD8kfYe59TXOUUVqedKTbuwooopiCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9b60oqRYZX+4pP0FWV03UX+5byH6Kf8K6z4CzKVFay6Drj/AHLKc/SNv8KsJ4V8Tv8Ac026P0hf/CgfJLsYNFdOngvxc/TTbn8Y2H8xVhPAHjN+mnTfiMfzoH7KfZnIUV3C/Dbxu/TT3/FkH82qyvws8dt/y44+skf/AMVQV7Cp/K/uPPqK9JT4S+OG62yL9ZE/oTVlfg94zbqkK/WQf0BosP6vU/lZ5bRXra/Bjxc3V7Zfq7f0U1YX4JeKD9+4tR/wJz/7JRYf1Wr/ACnjlZmravZaLZte3rYUcADqx9BXpXjnwZH8PNCfXPEeoQKv3Y4k3GSV/wC6gIH4noB1r4t1vXL3Xrw3V2cAcIg6KPQf1PeqjG5y4mTo+7Jaj9e1+91+8NzcnCDhEHRR/j6msOiitTy223dhRRRTEFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/Z",width:384,height:256},{id:"sample.photo.2",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/ayiiivVAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9D9rKKKK9UAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoooo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AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApGZUUu5wBySe1NkkSJDJIQqqMkngADua+f8Axv46fWGbStJYrag4Zuhk/wDsf51dOm5uyJbsT+OPHbamX0jR2xbDh5B1k9h/s/z+leV0UV6UIKKsjJu4UUUVYgooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvoL4IfHTVfhdfjTNS3XWiztmSHq0RPWSPPf1Xo3sea+faKzq0o1IuE1oVGbi+aJ+5Gi61pXiLS4Na0SdLm1uF3xyIcgj+hHQg8g8HmtSvyU+DHxs1r4Vap5Em660i4bM9tnkHp5keeA47jow4PYj9UPDviPRfFmjQa/4fnW5tbhdyOv6gjqCDwQeQa+ZxeElQl5Hq0ayqLzP//S+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACo5ZYoI2mmYIiDLMTgADuaSeeG2ha4uGCIgyzHgACvnbxp42m1+Q2NgSlmp+hkI7n29B+J9tKVJzdkTKViXxt44k1x203TSUtFPJ6GQjuf9n0H4n284oor0oxUVZGLdwoooqgCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAr2H4QfGLXvhTrPm2+bjTZ2H2m1J4btvT+64Hfv0PbHj1FROEZxcZrQcZOLuj//T+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKgubm3s7d7q6cRxxjLMegFNu7y2sLZ7y8cRxxjLMe1fN/jDxlc+JJ/IgzHaIflTux/vN7+g7VpSpObJlKxL4y8aXHiKY2lpmOzQ8L0Lkd2/oK4OiivSjFRVkYt3CiiiqAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//1PqCiiivszygooooAKKKKACiiigAooooAKKKKACiiigAooooAKqX19aabave3riOKMZJP+etN1HUbPSrN76/cRxoOSf5D1J9K+afFfiy88TXeTmO2Q/u4/6t6n+XataVJzfkTKViXxd4vu/EtzsTMdrGfkj9f9pvf+VcbRRXoxioqyMW7hRRRVAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH//V+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACs/VNUstHsnv799kafmT2AHcmmavq9jolk1/qD7UXoO7HsAO5NfM3iXxNfeJb3z7j5Il/1cY6KP6k9zW1Ki5vyJlKxL4o8VXvia78yX5IEP7uMdB7n1PvXLUUV6EYpKyMW7hRRRVAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//1vqCipFhlf7ik/QVZTTdRk+5byH6IT/SvsrnlWKVFay6Drr/AHLKc/SNv8Ksp4U8UP8Ac026P0hf/ClzLuOzMCiuoXwT4vfpplz+MbD+Yqynw/8AGj9NNm/EY/nS549x8j7HHUV3K/DXxy/TT3/FkH82qyvwq8ev/wAuGPrLEP8A2al7WHdByS7HntFelp8I/HLdbZF+sif0NWU+DnjRuqQr9ZB/QGl7aH8w/Zy7HldFeur8FfGDdXtl+rt/Rasr8D/FR+/c2o/4E5/9kpe3h3H7KXY8ZrJ1rWrDQbFr+/bCjhVHVj6AetemePvB0Xw20B9f8TajAq/diiTcZJX/ALqAgZ9z0A5NfCviDxBf+Ir43l6cAcIg+6g9B/U966sPBVfeWxlUvDRkniPxHf8AiS9N1dnai8Rxjoo/x9TXP0UV6aSSsjAKKKKYBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH//Z",width:384,height:256},{id:"sample.photo.3",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//R/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKrXl5a6fave3sgiijG5mY4AFOtbq3vbZLu0cSRSAMrLyCDTt1AnooopAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/9L9vKKKK7T0wooooAKKKKACiiigAooooAKKKKACs/VNUsNGsX1HUpBFDGMkn+Q9Sewqtruvab4c099S1STYi8AfxMeyqO5NfInjDxnqfi++865PlwIf3UIPCj1Pq3qa3o0XUfkXCHMX/HHjy/8AF115SZhsoz+7iz1/2m9T+g/U6/w38fv4buRpWqMTYSnr18pj3H+ye4/Ed8+U0V6DpR5eS2h0cqtY/QJHSVBJGQysMgjkEHuKdXzT8MPiF/Zjp4c1t/8ARmOIZGP+rJ/hP+ye3p9On0tXmVKbg7M5pR5XYKKKKzJCiiigAooooAKKKKACiiigAooooAKKKKACiiigApkkiRIZJDtUckmn1xXiK/aSb7DGflTlvc//AFqmcuVXPJzrNYZbhpYiau9ku7C/8RTSMY7H5F/vHqf8K52SeaY5ldmPuc1FRXHKTe5+LY/NsVjpOeIm35dF6IljnmhOYnZT7HFdFYeIpo2Ed986/wB4dR/jXMUURk1sGAzbFYGSnh5teXR+qPWY5ElQSRncp5BFPrivDt+0c32GQ/K/K+x/+vXa12QlzK5+05LmsMyw0cRBWezXZhRRRVHrBRRRQAUUUUAf/9P9vKKKK7T0wooooAKKKKACiiigAooooAK5vxP4p0vwpp5vtRbLHiOMfec+g/qe1U/GHjPTfCFj51yfMuHH7qEHlj6n0X1NfIeu69qXiPUH1LVJN8jcAfwqOyqOwH+ea6KNBz1expCnfVlvxP4o1TxXqBvtRbAHEcY+6i+g/qe9c5RRXpJJKyOlK2wUUUUwCvoj4XfEPzRH4Y1yT5x8tvK3f0Rj6/3T36elfO9KCQcjgioqU1NWZMoqSsz9A6K8c+GfxBGtxLoWsv8A6ZGP3bn/AJaqP/Zh39Rz617HXlTg4PlZyyjZ2YUUUVAgooooAKKKKACiiigAooooAKKKKACiiigAryieQzTPKerMT+der15RPGYZniPVWI/KsK/Q/O/EDm5KFtry+/S36kVFFFcx+ZhRRRQBLBIYZklHVWB/KvV68ogjM0yRDqzAfnXq9dNDqfpnh/zcle+14/frf9Aooorc/RAooooAKKKKAP/U/byiiiu09MKKKKACiiigAooooAK4Dxx49sPCNr5SYmvZB+7i9P8Aaf0H6n8zWd4++Itr4XibTtOIlv2HTqsYPdvf0H4njr8qXl5dahcve3shllkO5mY5JNdVDD83vS2NYU76sn1TVL/Wb59R1KQyzSHJY/yHoB2FZ9FFeilbY6AooooAKKKKACiiigCSGaW3lWeBijoQysDggjoQa+s/h349i8U2n2G/IW/hX5h08xR/EP6j+lfJFW7G+u9MvI7+xcxzRHcrDqDWVWkqisROPMj75orhvA3jW08X6duOI7uIATR/+zL/ALJ/TpXc15couLszlatowoooqQCiiigAooooAKKKKACiiigAoor5v+OXx2svh5av4f8ADzLPrUq/VbdSOGf1b+6v4njAOlOnKpLlgioU3N8sS58bfjlp/wANrJtH0crca1MvyJ1WEHo8nv8A3V79Tx14j4Z/GOx+IsK2eq7bfWEX94g4WbA++nv/AHl7dRx0+Ab+/vdUvZdR1GVp552LySOcszHkkk0y0u7mxuY7yykaKWJgyOpwykdCCK9eeWQlS5Hv3FnPDtLMsI8LN2e6fZ/5H6wUV4f8HfixB47iXw/rDLFq6D5QcKLgDunbeO6/iOMge6yQTQnEqMp9xivmq1CdGThNH4HmeS4vLqjpYqm1br0foyKipY4JpjiJGY+wzXRWHh2aRhJffIv90dT/AIVnGLexngMpxWOkoYeDfn0Xqw8O2DSTfbpB8qcL7n/61drTI40iQRxjao4AFPrshHlVj9pyXKoZbho4eDu92+7CiiiqPWCiiigAooooA//V/byiiiu09MKKKKACiiigArxz4h/EuLQw+jaEwe8PDydVi/xb+Xf0rK+InxPFr5mheGpMy8rLOv8AD6qh9fU9u3PT50JLEsxyTXbQw9/embU6fVj5ZZZ5WmnYu7klmY5JJ6kmo6KK7jcKKKKACiiigAooooAKKKKACiiigDU0bWL/AEHUY9U019ksZ/AjuCO4Pevsjwl4qsPFulrf2nyyLxLGTyjf4Hse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",width:384,height:256},{id:"sample.photo.4",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/biiiiuc9UKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/biiiiuc9UKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKK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0ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApGZUUu5wBySaR3SNDJIQqqMkngACvBvGnjZ9WZtM0titqDhm6GT/7H+dJuxrSpOo7Im8a+OG1ItpOkNi36O46v7D/Z/n9K8woorNu560IKC5YhRRRSLCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK98+Cvxw1T4ZXw0zUd1zo07Zki6tET1ePPf1XofrzXgdFMyq0o1YuE1oftro2s6X4h0yHWdFnW5tbhd8ciHII/oR0IPIPBrTr8o/g58Z9Z+Fup+TJuudJnbM9vnkHp5keeA47jow4PYj9QvD3iHRvFWjw69oE63NrcLuR1/UEdQQeCDyDTTPlMbgpYeXddGf/9L60ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACmSyxwxtNMwVFGSTwAB3NNnnhtoWuLhgiIMsx4AAr5+8Y+M5tekNlYkpaKfoXI7n29B+J9k3Y2o0XUdkS+MvGsmtO2nacSlop5PQyEdz7eg/E155RRWbdz1YQUFyxCiiikWFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXrvwk+L2ufCzWPNt83GnTsPtNqTw3ben91wO/foe2PIqKDOpTjUi4TV0f/T+tKKKKyPtQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACobi4gtIHublwkaDLMegFNu7u2sbZ7u7cRxoMsx7V87+LfF9z4in8iHMdoh+VO7H+839B2pN2NqNF1H5EvjDxjP4gmNra5jtEPC93I7t/QVw1FFZtnqwgorliFFFFIsKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9T60ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAqre3trp1q95euI40GST/nrTNQ1C00u0e9vnCRoOSf5D1Jr5y8U+KrvxJdZOY7dD+7j/qfU/wAqTdjejRdR+RL4r8WXXiO52LmO1Q/Inr/tN7/yrkKKKzPVjFRVkFFFFIoKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//V+tKKKKyPtQooooAKKKKACiiigAooooAKKKKACiiigAqhqep2ekWb318+yNPzJ9AO5NM1XVrLRbJr6/fai9B3Y9gB3NfN/iPxJe+I7zz7j5Yl/wBXGOij+p9TUt2OihQdR+RJ4m8T3niO78yT5IEP7uPsPc+prmaKKg9SMVFWQUUUUigooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/W+tKKkWKV/uKT9BVlNO1CT7kEjfRT/hWR9rcpUVqpoWuP9yznP0jb/CrK+FvE7/c066P0hf8AwoFzLuYNFdMvgvxc/TTbn8Y2H8xVlfAPjJ+mnTfiMfzoF7SPc5Ciu4X4b+N36ae/4sg/masL8LfHb/8ALjj6yR//ABVIXtYfzI8/or0lPhL44brbIv1kT+hqynwf8Zt1SFfrIP6A0C9tD+ZHltFetL8GPFzdXt1+rt/RTVlfgl4oP37i1H/AnP8A7JQL6xT/AJjxysrWNZsdDsmvr5sKOAB1Y+gHrXpXjvwfF8OdBfXfEmoQKv3Yok3GSV/7qAgZ9z0A618Q69r194gvTd3hwBwiDoo9B/U96TdjrwtNVveWw/xB4hvvEV6bm6O1F4SMdFH+PqawaKKg9dJJWQUUUUhhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH/9k=",width:384,height:256}],unavailableCount:0,fetchedAt:Wt,source:"system",stale:!1}},{id:"$calendar.isAvailable",group:"calendar",value:!0},{id:"$calendar.authorizationStatus",group:"calendar",value:"authorized"},{id:"$calendar.requestAuthorization",group:"calendar",value:!0},{id:"$calendar.calendars",group:"calendar",value:[{id:"sample.personal",title:"Personal"}]},{id:"$calendar.events",group:"calendar",value:{status:"ready",items:[{id:"sample.event",calendarID:"sample.personal",title:"Read and recharge",start:"2026-01-01T15:00:00Z",end:"2026-01-01T15:30:00Z",allDay:!1}],fetchedAt:Wt,source:"system",stale:!1,truncated:!1}},{id:"$reminders.isAvailable",group:"reminders",value:!0},{id:"$reminders.authorizationStatus",group:"reminders",value:"authorized"},{id:"$reminders.requestAuthorization",group:"reminders",value:!0},{id:"$reminders.lists",group:"reminders",value:[{id:"sample.reminders",title:"Personal"}]},{id:"$reminders.items",group:"reminders",value:{status:"ready",items:[{id:"sample.reminder",listID:"sample.reminders",title:"Read a chapter",due:"2026-01-01T16:00:00Z",hasTime:!0,completed:!1,completedAt:null,priority:0}],fetchedAt:Wt,source:"system",stale:!1,truncated:!1}},{id:"$reminders.create",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$reminders.update",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$reminders.setCompleted",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$health.authorizationStatus",group:"health",value:"notDisclosed"},{id:"$health.sleepSummary",group:"health",value:{start:cl,end:Wt,timeZone:"UTC",hasSamples:!0,totalSleepSeconds:27e3,inBedSeconds:30600,awakeSeconds:1800,stages:{unspecified:0,core:16200,deep:5400,rem:5400},sourceCount:1,truncated:!1,conflictSeconds:0,aggregation:"union-awake-rem-deep-core-unspecified"}},{id:"$health.activitySummary",group:"health",value:{date:"2026-01-01",timeZone:"UTC",hasSamples:!0,moveMode:"activeEnergy",move:{value:480,goal:600,unit:"kcal"},exercise:{value:26,goal:30,unit:"min"},stand:{value:9,goal:12,unit:"count"}}},{id:"$health.workouts",group:"health",value:{start:cl,end:Wt,timeZone:"UTC",hasSamples:!0,truncated:!1,items:[{id:"550e8400-e29b-41d4-a716-446655440000",activity:"running",start:"2026-01-01T09:00:00.000Z",end:"2026-01-01T09:45:00.000Z",duration:2700,energyKcal:285,distanceMeters:5200}]}},{id:"$health.isAvailable",group:"health",value:!0},{id:"$health.requestAuthorization",group:"health",value:!0,legacy:["health"]},{id:"$health.stepCountToday",group:"health",value:ao(6840,"count"),legacy:["health.stepCountToday"]},{id:"$health.activeEnergyToday",group:"health",value:ao(420,"kcal"),legacy:["health.activeEnergyToday"]},{id:"$health.heartRateLatest",group:"health",value:ao(72,"bpm"),legacy:["health.heartRateLatest"]},{id:"$location.isAvailable",group:"location",value:!0},{id:"$location.authorizationStatus",group:"location",value:"authorizedWhenInUse"},{id:"$location.isAuthorizedForWidgetUpdates",group:"location",value:!0},{id:"$location.requestAuthorization",group:"location",value:!0,legacy:["location.authorization"]},{id:"$location.current",group:"location",value:{latitude:37.3349,longitude:-122.009,altitude:0,accuracy:10,verticalAccuracy:10,speed:-1,course:-1,timestamp:Wt,accuracyAuthorization:"full",age:0,isStale:!1},legacy:["location"]}],y1={music:{platforms:["ios"],authorization:"musicRead",notes:"Runtime 1.4. Read-only Music app playback, not every audio player. Requires an installed owner, music permission and Music library approval in native Settings. current() is asynchronous and returns provider, state, hasItem, nullable title/artist/album/duration/position and capturedAt. State may be playing while details are absent (Home Sharing). Native player access is main-thread-only: synchronous hosts prepare one snapshot before JSC waits; background previews capture on the main queue. No playback/queue mutation, scans, artwork, credential or subscription inference. No live queries in future entries/Live Activities. Main system-player API is unavailable on macOS; Web supplies editable samples, never actual device access."},services:{platforms:["ios","macos"],authorization:"serviceAccount",notes:"Runtime 1.4: status(service) and profile(service) are asynchronous. Only provider: github. Unknown providers reject with service_operation. Requires an installed owner and services permission. profile additionally requires network and api.github.com in networkDomains, a native connected GitHub account and explicit owner grant. Native Settings handles credentials; scripts cannot log in or obtain tokens. profile returns service, subject, username, displayName, publicRepositories, privateRepositories, followers and following (missing values are null). No live queries in future entries or Live Activities. Eight shared data queries; native requests also share the eight concurrent/32 total network budget, while Web uses its existing preview network budget. Web uses editable best-state samples only; it never authenticates or sends these requests."},photos:{platforms:["ios","macos"],authorization:"photosRead",notes:"Requires photos permission and explicit native photo/album selection (none by default). Limited Photos authorization is supported. items({limit:1..4,offset:0..63,maxAgeSeconds:0..86400}) returns bounded, metadata-stripped JPEG thumbnails as image data URLs. Four 768px / 128KiB thumbnails at most; private snapshots expire after 24h and are rechecked against current access, selected sources and asset revisions. Cloud-only items report unavailableCount; download them in Photos first. Future timeline/Live Activity presentations read matching snapshots only. Web uses editable sample data and photosSelection IDs; no actual library access."},reminders:{platforms:["ios","macos"],authorization:"remindersRead",notes:"Requires reminders in widget.json and full system Reminders access. Reads require native Settings selected lists. create/update/setCompleted additionally require remindersWrite and a native declared action, stable requestID, durable replay receipts and an unchanged selected list. Web writes update preview-only items/receipts; receipt identities are derived from that state, with configurable API errors. Use lists() for IDs. items() selects pending due dates or completed completion dates in a positive window of at most 31 days, up to 50 items. Undated pending reminders are excluded. remindersSelection mocks the user scope without granting permission."},calendar:{platforms:["ios","macos"],authorization:"calendarRead",notes:"Requires calendar in widget.json and full system Calendar access. The user's native Settings source selection limits metadata, events and cached snapshots; scripts cannot enlarge it. Use calendars() for permitted IDs and show setup when no sources are selected. Queries are read-only, limited to 31 days and 50 items. Preview calendarSelection simulates this scope without granting package permission."},device:{platforms:["ios","macos"],authorization:"none",notes:"Device values depend on hardware. Some legacy Mac values are placeholders; inspect the API documentation before using them as measurements."},system:{platforms:["ios","macos"],authorization:"none",notes:"System metadata, not whole-device CPU, FPS or network traffic monitoring. Hardware-specific values may be unavailable."},health:{platforms:["ios"],authorization:"healthRead",notes:"Read-only HealthKit. Request access in the app. Runtime 1.4 adds sleepSummary (up to 7 days / 256 samples, union with explicit stage-conflict precedence), activitySummary (one local Gregorian day including active-energy or move-time mode and optional goals), and workouts (up to 31 days / 50 fully-contained summaries, optional kcal/meters). These require an explicit health manifest; all share the query budget and cannot run in future/Live Activity presentations. No Health readings are persisted by the API. Authorization completion does not reveal whether individual read types were granted; empty results must not be labelled permission denied. API 1.3 readings add hasSamples: false means no readable samples, never a read-denial verdict. Existing value/unit/start/end fields remain."},location:{platforms:["ios"],authorization:"locationWhenInUse",notes:"Package 2.0 requires location permission. Request system access in the app. Widgets also require isAuthorizedForWidgetUpdates. Cache is rechecked against authorization and precision, discarded after 24 hours or revocation, and reports age/isStale."}},A1=g1.map(n=>{const e=n.id.split(".")[1],t=n.group==="music"&&e==="current"||n.group==="services"||["calendar","reminders","photos"].includes(n.group)&&!["isAvailable","authorizationStatus"].includes(e)||n.group==="health"&&!["isAvailable","authorizationStatus"].includes(e)||n.group==="location"&&["current","requestAuthorization"].includes(e),i=n.group==="health"&&["sleepSummary","activitySummary","workouts"].includes(e)||n.group==="location"&&["current","requestAuthorization"].includes(e)||["calendar","reminders","photos"].includes(n.group)&&["events","items"].includes(e);return{...n,...y1[n.group],minimumRuntimeVersion:["music","services","reminders","photos"].includes(n.group)||n.group==="health"&&["sleepSummary","activitySummary","workouts"].includes(e)?"1.4":["calendar"].includes(n.group)||["$health.authorizationStatus","$location.isAuthorizedForWidgetUpdates"].includes(n.id)?"1.3":"1.0",arguments:n.group==="services"?["service"]:n.group==="reminders"&&["create","update","setCompleted"].includes(e)?["options"]:i?["options?"]:[],...n.group==="reminders"&&["create","update","setCompleted"].includes(e)?{authorization:"remindersWrite"}:{},asynchronous:t,nativeSource:`Shared/ScriptWidgetRuntime/Widget/API/ScriptWidgetRuntime${n.group[0].toUpperCase()}${n.group.slice(1)}.swift`,operation:n.group==="reminders"&&["create","update","setCompleted"].includes(e)?"write":e==="requestAuthorization"?"authorization":["isAvailable","authorizationStatus","isAuthorizedForWidgetUpdates"].includes(e)?"availability":"read",mock:"editable"}}),Re={globalProperties:{background:{type:"color",documentation:"Background color, hex value, or named color."},backgroundMaterial:{type:"enum",values:["none","ultraThin","thin","regular","thick","ultraThick"],documentation:"Runtime API 1.4. Public SwiftUI frosted material inside the widget, not Home Screen wallpaper blur or system Liquid Glass. At most four material backgrounds per tree. background supplies an opaque-composited Reduce Transparency fallback. In accented/vibrant mode local material is omitted for system rendering; none keeps the ordinary background."},color:{type:"color",documentation:"Foreground color, hex value, or named color."},font:{type:"enum",values:["largeTitle","title","title2","title3","headline","subheadline","body","callout","footnote","caption","caption2"],documentation:"Semantic text style."},fontWeight:{type:"enum",values:["ultraLight","thin","light","regular","medium","semibold","bold","heavy","black"],documentation:"Font weight."},fontDesign:{type:"enum",values:["default","rounded","serif","monospaced"],documentation:"Font design."},frame:{type:"number|string",documentation:'Square size (64 or "64"), width,height ("64,48"), or flexible frame ("max", "max,48").'},padding:{type:"number|string",documentation:"Padding amount or edge description."},corner:{type:"number",documentation:"Corner radius."},opacity:{type:"number",documentation:"Opacity from 0 through 1."},alignment:{type:"enum",values:["center","leading","trailing","top","bottom","topLeading","topTrailing","bottomLeading","bottomTrailing","firstTextBaseline","lastTextBaseline"],documentation:"Layout alignment."},clip:{type:"enum",values:["circle","rect","capsule","ellipse"],documentation:"Clip the view to a shape."},rotation:{type:"number",documentation:"Rotation in degrees."},rotation3d:{type:"string",documentation:"Static degree,x,y,z rotation with four finite numbers and a nonzero axis. Invalid values are ignored; axis magnitude is normalized. Reduce Motion removes the tilt. This does not read device motion or provide parallax."},shadow:{type:"string",documentation:"Shadow color, radius, and offset description."},animation:{type:"string",documentation:"iOS public SwiftUI scheduled clock/swing effect. Mac renders static standard-clock poses; other effects remain at rest. Signed clockCustom intervals specify a half-turn (full cycle = 2 * abs(interval)), minimum magnitude 0.25 seconds. App previews can animate; WidgetKit decides cadence and may freeze. Reduced motion/luminance freezes standard clock poses and keeps custom/swing effects at rest. Dynamic scripts are not rerun on animation ticks."},widgetAccentable:{type:"boolean",documentation:"Group this view into the accent layer in tinted and vibrant widget appearances."},linkurl:{type:"url",documentation:"Widget deep link URL."}},components:{VStack:{documentation:"Arrange children vertically.",children:!0,properties:{spacing:{type:"number"}}},HStack:{documentation:"Arrange children horizontally.",children:!0,properties:{spacing:{type:"number"}}},ZStack:{documentation:"Overlay children.",children:!0,properties:{}},VGrid:{documentation:"Vertical lazy grid.",children:!0,properties:{columns:{type:"string",required:!0},spacing:{type:"number"},alignment:{type:"enum",values:["leading","center","trailing"]}}},HGrid:{documentation:"Horizontal lazy grid.",children:!0,properties:{columns:{type:"string",required:!0},spacing:{type:"number"},alignment:{type:"enum",values:["top","center","bottom","firstTextBaseline","lastTextBaseline"]}}},Text:{documentation:"Display text content.",children:!1,properties:{text:{type:"string"}}},Date:{documentation:"Display a formatted date.",children:!1,properties:{date:{type:"date|number",required:!0},style:{type:"enum",values:["time","date","relative","offset","timer"]}}},Image:{documentation:"Display a bundled, remote, or SF Symbol image.",children:!1,properties:{systemName:{type:"string"},name:{type:"string"},id:{type:"string",deprecated:"Use name."},url:{type:"url"},src:{type:"url",deprecated:"Use url."},ratio:{type:"number"},mode:{type:"enum",values:["fit","fill"]},accentedRenderingMode:{type:"enum",values:["accented","desaturated","accentedDesaturated","fullColor"],documentation:"Control how the image is rendered in tinted and clear widget appearances."}}},Gif:{documentation:"Display a bounded package GIF using system decoding: up to 8 MiB input and 1,048,576 canvas pixels. iOS retains at most 60 downsampled frames / 4 MiB decoded pixels; more frames fall back to the first frame. Mac, Reduce Motion and reduced luminance use a static first frame. Active iOS previews use public SwiftUI timing with variable frame delays. Snapshots, inactive scenes, Mac and accessibility fallbacks use the first frame. No private clock bridge or continuous WidgetKit guarantee.",children:!1,properties:{file:{type:"string",required:!0}}},Spacer:{documentation:"Flexible layout space.",children:!1,properties:{minLength:{type:"number"}}},Rect:{documentation:"Rectangle shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},RoundedRect:{documentation:"Filled rounded rectangle.",children:!1,properties:{radius:{type:"number"}}},Capsule:{documentation:"Capsule shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Ellipse:{documentation:"Ellipse shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Circle:{documentation:"Circle shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Gauge:{documentation:"Display a gauge or instrument.",children:!1,properties:{type:{type:"string"},value:{type:"number"},style:{type:"enum",values:["circular","linear"]},text:{type:"string"},current:{type:"string"},min:{type:"string"},max:{type:"string"},angle:{type:"number"},thickness:{type:"number"},needleColor:{type:"color"},label:{type:"string"},title:{type:"string"},sections:{type:"string"}}},Chart:{documentation:"Render chart data.",children:!1,availability:{ios:"16.0",macos:"26.0"},properties:{type:{type:"enum",values:["bar","bar-x","bar-y","bar-gantt","line","point","line-point","area","rect","rule-x"]},data:{type:"json",required:!0},category:{type:"boolean"},hideLegend:{type:"boolean"},hideXAxis:{type:"boolean"},hideYAxis:{type:"boolean"}}},Link:{documentation:"Open a URL when selected.",children:!0,properties:{url:{type:"url",required:!0}}},Divider:{documentation:"Horizontal or vertical separator.",children:!1,properties:{thickness:{type:"number"},axis:{type:"enum",values:["horizontal","vertical"]}}},Line:{documentation:"Fixed-length line.",children:!1,properties:{thickness:{type:"number"},length:{type:"number"},axis:{type:"enum",values:["horizontal","vertical"]}}},Icon:{documentation:"SF Symbol icon.",children:!1,properties:{systemName:{type:"string",required:!0},size:{type:"number"}}},Label:{documentation:"Text paired with an SF Symbol.",children:!1,properties:{title:{type:"string"},systemName:{type:"string",required:!0}}},Progress:{documentation:"Linear or circular progress.",children:!1,properties:{value:{type:"number",required:!0},total:{type:"number"},label:{type:"string"},style:{type:"enum",values:["linear","circular"]},trackColor:{type:"color"},thickness:{type:"number"}}},Ring:{documentation:"Circular progress ring.",children:!1,properties:{value:{type:"number",required:!0},thickness:{type:"number"},trackColor:{type:"color"}}},Badge:{documentation:"Compact status badge.",children:!1,properties:{text:{type:"string"},radius:{type:"number"}}},Chip:{documentation:"Outlined metadata chip.",children:!1,properties:{text:{type:"string"},radius:{type:"number"},borderColor:{type:"color"}}},Stat:{documentation:"Title, value, and subtitle statistic.",children:!1,properties:{title:{type:"string"},value:{type:"string",required:!0},subtitle:{type:"string"},mutedColor:{type:"color"}}},Button:{documentation:"Interactive widget button. Use actionID to share a declared Package 2.0 action with Siri, Shortcuts, and Control Widgets.",children:!0,availability:{interactiveIos:"17.0"},requiredAny:["action","onClick","actionID"],properties:{action:{type:"enum",values:["reload"],conflicts:["actionID"]},onClick:{type:"function",conflicts:["actionID"]},actionID:{type:"action-id",conflicts:["action","onClick"]}}},Toggle:{documentation:"Interactive widget toggle. A declared actionID requires a package-relative storage stateKey.",children:!0,availability:{interactiveIos:"17.0"},requiredAny:["onClick","actionID"],properties:{on:{type:"boolean",required:!0},onClick:{type:"function",conflicts:["actionID"]},actionID:{type:"action-id",requires:["stateKey"],conflicts:["onClick"]},stateKey:{type:"storage-key",requires:["actionID"]}}}},functions:{...Object.fromEntries(O1.map(n=>[n.id,n])),...Object.fromEntries(A1.map(n=>[n.id,{documentation:n.notes,arguments:n.arguments,returns:n.asynchronous?"Promise":typeof n.value=="object"?"json":typeof n.value}])),"$file.exists":{documentation:"Return whether a regular file is locally present inside this package, including binary assets. Missing files, directories, unsafe paths and links return false. Does not download iCloud files or read outside the package. Web preview uses workspace file presence.",arguments:["path"],returns:"boolean",minimumRuntimeVersion:"1.3"},"$file.read":{documentation:"Read package-relative UTF-8 text. Missing or unavailable files return an empty string.",arguments:["path"],returns:"string"},"$file.readJSON":{documentation:"Read a package-relative JSON object.",arguments:["path"],returns:"json"},fetch:{documentation:"Perform a network request and return its response text. Existing HTTP methods keep this string result."},"$http.getResponse":{documentation:"GET response metadata: {status, body, headers}. Headers are limited to content-type, cache-control, expires, last-modified, date and retry-after (lowercase; each at most 2048 UTF-8 bytes). Network permissions, public-host policy, redirects, body limits and timeline restrictions still apply. HTTP error statuses resolve; transport errors reject.",arguments:["url","options?"],returns:"Promise",minimumRuntimeVersion:"1.3"},importJS:{documentation:"Import a package-relative JavaScript file."},"$dataSource.request":{documentation:"Call a declared Data Source Plugin operation. Requires Package 2.0 plugins, network permission, and networkDomains declarations.",arguments:["pluginID","operationID","parameters"],returns:"Promise"},"$xml.parse":{documentation:"Parse up to 256 KiB of XML into {name, attributes, text, children}. DTDs and external entities are rejected; depth and node limits apply.",arguments:["source"],returns:"XMLNode"}}};function b1(n,e){const t=Object.entries(e.properties??{}).filter(([,r])=>r.required).map(([r,s])=>`${r}=${s.type.includes("number")||s.type==="boolean"?"{${}}":'"${}"'}`).join(" "),i=t?` ${t}`:"";return e.children?`<${n}${i}> +`:i=="r"?"\r":i=="t"?" ":"\\")}eq(e){return this.search==e.search&&this.replace==e.replace&&this.caseSensitive==e.caseSensitive&&this.regexp==e.regexp&&this.wholeWord==e.wholeWord&&this.test==e.test}create(){return this.regexp?new Lb(this):new Fb(this)}getCursor(e,t=0,i){let r=e.doc?e:_.create({doc:e});return i==null&&(i=r.doc.length),this.regexp?Pi(this,r,t,i):Qi(this,r,t,i)}}class gd{constructor(e){this.spec=e}}function Eb(n,e,t){return(i,r,s,o)=>{if(t&&!t(i,r,s,o))return!1;let l=i>=o&&r<=o+s.length?s.slice(i-o,r-o):e.doc.sliceString(i,r);return n(l,e,i,r)}}function Qi(n,e,t,i){let r;return n.wholeWord&&(r=Ub(e.doc,e.charCategorizer(e.selection.main.head))),n.test&&(r=Eb(n.test,e,r)),new Yi(e.doc,n.unquoted,t,i,n.caseSensitive?void 0:s=>s.toLowerCase(),r)}function Ub(n,e){return(t,i,r,s)=>((s>t||s+r.length=t)return null;r.push(i.value)}return r}highlight(e,t,i,r){let s=Qi(this.spec,e,Math.max(0,t-this.spec.unquoted.length),Math.min(i+this.spec.unquoted.length,e.doc.length));for(;!s.next().done;)r(s.value.from,s.value.to)}}function Db(n,e,t){return(i,r,s)=>(!t||t(i,r,s))&&n(s[0],e,i,r)}function Pi(n,e,t,i){let r;return n.wholeWord&&(r=Xb(e.charCategorizer(e.selection.main.head))),n.test&&(r=Db(n.test,e,r)),new pd(e.doc,n.search,{ignoreCase:!n.caseSensitive,test:r},t,i)}function Jr(n,e){return n.slice(Ze(n,e,!1),e)}function es(n,e){return n.slice(e,Ze(n,e))}function Xb(n){return(e,t,i)=>!i[0].length||(n(Jr(i.input,i.index))!=ye.Word||n(es(i.input,i.index))!=ye.Word)&&(n(es(i.input,i.index+i[0].length))!=ye.Word||n(Jr(i.input,i.index+i[0].length))!=ye.Word)}class Lb extends gd{nextMatch(e,t,i){let r=Pi(this.spec,e,i,e.doc.length).next();return r.done&&(r=Pi(this.spec,e,0,t).next()),r.done?null:r.value}prevMatchInRange(e,t,i){for(let r=1;;r++){let s=Math.max(t,i-r*1e4),o=Pi(this.spec,e,s,i),l=null;for(;!o.next().done;)l=o.value;if(l&&(s==t||l.from>s+10))return l;if(s==t)return null}}prevMatch(e,t,i){return this.prevMatchInRange(e,0,t)||this.prevMatchInRange(e,i,e.doc.length)}getReplacement(e){return this.spec.unquote(this.spec.replace).replace(/\$([$&]|\d+)/g,(t,i)=>{if(i=="&")return e.match[0];if(i=="$")return"$";for(let r=i.length;r>0;r--){let s=+i.slice(0,r);if(s>0&&s=t)return null;r.push(i.value)}return r}highlight(e,t,i,r){let s=Pi(this.spec,e,Math.max(0,t-250),Math.min(i+250,e.doc.length));for(;!s.next().done;)r(s.value.from,s.value.to)}}const Zn=W.define(),Gl=W.define(),Ht=Te.define({create(n){return new lo(ll(n).create(),null)},update(n,e){for(let t of e.effects)t.is(Zn)?n=new lo(t.value.create(),n.panel):t.is(Gl)&&(n=new lo(n.query,t.value?Hl:null));return n},provide:n=>ji.from(n,e=>e.panel)});class lo{constructor(e,t){this.query=e,this.panel=t}}const Bb=B.mark({class:"cm-searchMatch"}),qb=B.mark({class:"cm-searchMatch cm-searchMatch-selected"}),Ib=xe.fromClass(class{constructor(n){this.view=n,this.decorations=this.highlight(n.state.field(Ht))}update(n){let e=n.state.field(Ht);(e!=n.startState.field(Ht)||n.docChanged||n.selectionSet||n.viewportChanged)&&(this.decorations=this.highlight(e))}highlight({query:n,panel:e}){if(!e||!n.spec.valid)return B.none;let{view:t}=this,i=new Bt;for(let r=0,s=t.visibleRanges,o=s.length;rs[r+1].from-500;)a=s[++r].to;n.highlight(t.state,l,a,(c,h)=>{let u=t.state.selection.ranges.some(f=>f.from==c&&f.to==h);i.add(c,h,u?qb:Bb)})}return i.finish()}},{decorations:n=>n.decorations});function zn(n){return e=>{let t=e.state.field(Ht,!1);return t&&t.query.spec.valid?n(e,t):bd(e)}}const ts=zn((n,{query:e})=>{let{to:t}=n.state.selection.main,i=e.nextMatch(n.state,t,t);if(!i)return!1;let r=A.single(i.from,i.to),s=n.state.facet(tn);return n.dispatch({selection:r,effects:[_l(n,i),s.scrollToMatch(r.main,n)],userEvent:"select.search"}),Ad(n),!0}),is=zn((n,{query:e})=>{let{state:t}=n,{from:i}=t.selection.main,r=e.prevMatch(t,i,i);if(!r)return!1;let s=A.single(r.from,r.to),o=n.state.facet(tn);return n.dispatch({selection:s,effects:[_l(n,r),o.scrollToMatch(s.main,n)],userEvent:"select.search"}),Ad(n),!0}),zb=zn((n,{query:e})=>{let t=e.matchAll(n.state,1e3);return!t||!t.length?!1:(n.dispatch({selection:A.create(t.map(i=>A.range(i.from,i.to))),userEvent:"select.search.matches"}),!0)}),Wb=({state:n,dispatch:e})=>{let t=n.selection;if(t.ranges.length>1||t.main.empty)return!1;let{from:i,to:r}=t.main,s=[],o=0;for(let l=new Yi(n.doc,n.sliceDoc(i,r));!l.next().done;){if(s.length>1e3)return!1;l.value.from==i&&(o=s.length),s.push(A.range(l.value.from,l.value.to))}return e(n.update({selection:A.create(s,o),userEvent:"select.search.matches"})),!0},th=zn((n,{query:e})=>{let{state:t}=n,{from:i,to:r}=t.selection.main;if(t.readOnly)return!1;let s=e.nextMatch(t,i,i);if(!s)return!1;let o=s,l=[],a,c,h=[];o.precise?o.from==i&&o.to==r&&(c=t.toText(e.getReplacement(o)),l.push({from:o.from,to:o.to,insert:c}),o=e.nextMatch(t,o.from,o.to),h.push(R.announce.of(t.phrase("replaced match on line $",t.doc.lineAt(i).number)+"."))):o=e.nextMatch(t,o.from,o.to);let u=n.state.changes(l);return o&&(a=A.single(o.from,o.to).map(u),h.push(_l(n,o)),h.push(t.facet(tn).scrollToMatch(a.main,n))),n.dispatch({changes:u,selection:a,effects:h,userEvent:"input.replace"}),!0}),jb=zn((n,{query:e})=>{if(n.state.readOnly)return!1;let t=[];for(let r of e.matchAll(n.state,1e9)){let{from:s,to:o,precise:l}=r;l&&t.push({from:s,to:o,insert:e.getReplacement(r)})}if(!t.length)return!1;let i=n.state.phrase("replaced $ matches",t.length)+".";return n.dispatch({changes:t,effects:R.announce.of(i),userEvent:"input.replace.all"}),!0});function Hl(n){return n.state.facet(tn).createPanel(n)}function ll(n,e){var t,i,r,s,o;let l=n.selection.main,a=l.empty||l.to>l.from+100?"":n.sliceDoc(l.from,l.to);if(e&&!a)return e;let c=n.facet(tn);return new Od({search:((t=e?.literal)!==null&&t!==void 0?t:c.literal)?a:a.replace(/\n/g,"\\n"),caseSensitive:(i=e?.caseSensitive)!==null&&i!==void 0?i:c.caseSensitive,literal:(r=e?.literal)!==null&&r!==void 0?r:c.literal,regexp:(s=e?.regexp)!==null&&s!==void 0?s:c.regexp,wholeWord:(o=e?.wholeWord)!==null&&o!==void 0?o:c.wholeWord})}function yd(n){let e=Fu(n,Hl);return e&&e.dom.querySelector("[main-field]")}function Ad(n){let e=yd(n);e&&e==n.root.activeElement&&e.select()}const bd=n=>{let e=n.state.field(Ht,!1);if(e&&e.panel){let t=yd(n);if(t&&t!=n.root.activeElement){let i=ll(n.state,e.query.spec);i.valid&&n.dispatch({effects:Zn.of(i)}),t.focus(),t.select()}}else n.dispatch({effects:[Gl.of(!0),e?Zn.of(ll(n.state,e.query.spec)):W.appendConfig.of(Gb)]});return!0},Sd=n=>{let e=n.state.field(Ht,!1);if(!e||!e.panel)return!1;let t=Fu(n,Hl);return t&&t.dom.contains(n.root.activeElement)&&n.focus(),n.dispatch({effects:Gl.of(!1)}),!0},Vb=[{key:"Mod-f",run:bd,scope:"editor search-panel"},{key:"F3",run:ts,shift:is,scope:"editor search-panel",preventDefault:!0},{key:"Mod-g",run:ts,shift:is,scope:"editor search-panel",preventDefault:!0},{key:"Escape",run:Sd,scope:"editor search-panel"},{key:"Mod-Shift-l",run:Wb},{key:"Mod-Alt-g",run:wb},{key:"Mod-d",run:Mb,preventDefault:!0}];class Yb{constructor(e){this.view=e;let t=this.query=e.state.field(Ht).query.spec;this.commit=this.commit.bind(this),this.searchField=he("input",{value:t.search,placeholder:rt(e,"Find"),"aria-label":rt(e,"Find"),class:"cm-textfield",name:"search",form:"","main-field":"true",onchange:this.commit,onkeyup:this.commit}),this.replaceField=he("input",{value:t.replace,placeholder:rt(e,"Replace"),"aria-label":rt(e,"Replace"),class:"cm-textfield",name:"replace",form:"",onchange:this.commit,onkeyup:this.commit}),this.caseField=he("input",{type:"checkbox",name:"case",form:"",checked:t.caseSensitive,onchange:this.commit}),this.reField=he("input",{type:"checkbox",name:"re",form:"",checked:t.regexp,onchange:this.commit}),this.wordField=he("input",{type:"checkbox",name:"word",form:"",checked:t.wholeWord,onchange:this.commit});function i(r,s,o){return he("button",{class:"cm-button",name:r,onclick:s,type:"button"},o)}this.dom=he("div",{onkeydown:r=>this.keydown(r),class:"cm-search"},[this.searchField,i("next",()=>ts(e),[rt(e,"next")]),i("prev",()=>is(e),[rt(e,"previous")]),i("select",()=>zb(e),[rt(e,"all")]),he("label",null,[this.caseField,rt(e,"match case")]),he("label",null,[this.reField,rt(e,"regexp")]),he("label",null,[this.wordField,rt(e,"by word")]),...e.state.readOnly?[]:[he("br"),this.replaceField,i("replace",()=>th(e),[rt(e,"replace")]),i("replaceAll",()=>jb(e),[rt(e,"replace all")])],he("button",{name:"close",onclick:()=>Sd(e),"aria-label":rt(e,"close"),type:"button"},["×"])])}commit(){let e=new Od({search:this.searchField.value,caseSensitive:this.caseField.checked,regexp:this.reField.checked,wholeWord:this.wordField.checked,replace:this.replaceField.value});e.eq(this.query)||(this.query=e,this.view.dispatch({effects:Zn.of(e)}))}keydown(e){rO(this.view,e,"search-panel")?e.preventDefault():e.keyCode==13&&e.target==this.searchField?(e.preventDefault(),(e.shiftKey?is:ts)(this.view)):e.keyCode==13&&e.target==this.replaceField&&(e.preventDefault(),th(this.view))}update(e){for(let t of e.transactions)for(let i of t.effects)i.is(Zn)&&!i.value.eq(this.query)&&this.setQuery(i.value)}setQuery(e){this.query=e,this.searchField.value=e.search,this.replaceField.value=e.replace,this.caseField.checked=e.caseSensitive,this.reField.checked=e.regexp,this.wordField.checked=e.wholeWord}mount(){this.searchField.select()}get pos(){return 80}get top(){return this.view.state.facet(tn).top}}function rt(n,e){return n.state.phrase(e)}const fr=30,dr=/[\s\.,:;?!]/;function _l(n,{from:e,to:t}){let i=n.state.doc.lineAt(e),r=n.state.doc.lineAt(t).to,s=Math.max(i.from,e-fr),o=Math.min(r,t+fr),l=n.state.sliceDoc(s,o);if(s!=i.from){for(let a=0;al.length-fr;a--)if(!dr.test(l[a-1])&&dr.test(l[a])){l=l.slice(0,a);break}}return R.announce.of(`${n.state.phrase("current match")}. ${l} ${n.state.phrase("on line")} ${i.number}.`)}const Nb=R.baseTheme({".cm-panel.cm-search":{padding:"2px 6px 4px",position:"relative","& [name=close]":{position:"absolute",top:"0",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",padding:0,margin:0},"& input, & button, & label":{margin:".2em .6em .2em 0"},"& input[type=checkbox]":{marginRight:".2em"},"& label":{fontSize:"80%",whiteSpace:"pre"}},"&light .cm-searchMatch":{backgroundColor:"#ffff0054"},"&dark .cm-searchMatch":{backgroundColor:"#00ffff8a"},"&light .cm-searchMatch-selected":{backgroundColor:"#ff6a0054"},"&dark .cm-searchMatch-selected":{backgroundColor:"#ff00ff8a"}}),Gb=[Ht,si.low(Ib),Nb];class ih{constructor(e,t,i){this.from=e,this.to=t,this.diagnostic=i}}class di{constructor(e,t,i){this.diagnostics=e,this.panel=t,this.selected=i}static init(e,t,i){let r=i.facet(Kt).markerFilter;r&&(e=r(e,i));let s=e.slice().sort((d,p)=>d.from-p.from||d.to-p.to),o=new Bt,l=[],a=0,c=i.doc.iter(),h=0,u=i.doc.length;for(let d=0;;){let p=d==s.length?null:s[d];if(!p&&!l.length)break;let m,O;if(l.length)m=a,O=l.reduce((x,C)=>Math.min(x,C.to),p&&p.from>m?p.from:1e8);else{if(m=p.from,m>u)break;O=p.to,l.push(p),d++}for(;dx.from||x.to==m))l.push(x),d++,O=Math.min(x.to,O);else{O=Math.min(x.from,O);break}}O=Math.min(O,u);let y=!1;if(l.some(x=>x.from==m&&(x.to==O||O==u))&&(y=m==O,!y&&O-m<10)){let x=m-(h+c.value.length);x>0&&(c.next(x),h=m);for(let C=m;;){if(C>=O){y=!0;break}if(!c.lineBreak&&h+c.value.length>C)break;C=h+c.value.length,h+=c.value.length,c.next()}}let b=Td(l);if(y)o.add(m,m,B.widget({widget:new n1(b),diagnostics:l.slice()}));else{let x=l.reduce((C,k)=>k.markClass?C+" "+k.markClass:C,"");o.add(m,O,B.mark({class:"cm-lintRange cm-lintRange-"+b+x,diagnostics:l.slice(),inclusiveEnd:l.some(C=>C.to>O)}))}if(a=O,a==u)break;for(let x=0;x{if(!(e&&o.diagnostics.indexOf(e)<0))if(!i)i=new ih(r,s,e||o.diagnostics[0]);else{if(o.diagnostics.indexOf(i.diagnostic)<0)return!1;i=new ih(i.from,s,i.diagnostic)}}),i}function xd(n,e){let t=e.pos,i=e.end||t,r=n.state.facet(Kt).hideOn(n,t,i);if(r!=null)return r;let s=n.startState.doc.lineAt(e.pos);return!!(n.effects.some(o=>o.is(Qs))||n.changes.touchesRange(s.from,Math.max(s.to,i)))}function Hb(n,e){return n.field(Ot,!1)?e:e.concat(W.appendConfig.of(Kd))}function wd(n,e){return{effects:Hb(n,[Qs.of(e)])}}const Qs=W.define(),vd=W.define(),kd=W.define(),Ot=Te.define({create(){return new di(B.none,null,null)},update(n,e){if(e.docChanged&&n.diagnostics.size){let t=n.diagnostics.map(e.changes),i=null,r=n.panel;if(n.selected){let s=e.changes.mapPos(n.selected.from,1);i=Ni(t,n.selected.diagnostic,s)||Ni(t,null,s)}!t.size&&r&&e.state.facet(Kt).autoPanel&&(r=null),n=new di(t,r,i)}for(let t of e.effects)if(t.is(Qs)){let i=e.state.facet(Kt).autoPanel?t.value.length?ns.open:null:n.panel;n=di.init(t.value,i,e.state)}else t.is(vd)?n=new di(n.diagnostics,t.value?ns.open:null,n.selected):t.is(kd)&&(n=new di(n.diagnostics,n.panel,t.value));return n},provide:n=>[ji.from(n,e=>e.panel),R.decorations.from(n,e=>e.diagnostics)]}),_b=B.mark({class:"cm-lintRange cm-lintRange-active"});function Jb(n,e,t){let{diagnostics:i}=n.state.field(Ot),r,s=-1,o=-1;i.between(e-(t<0?1:0),e+(t>0?1:0),(a,c,{spec:h})=>{if(e>=a&&e<=c&&(a==c||(e>a||t>0)&&(eCd(n,t,!1)))}const nh=n=>{let e=n.state.field(Ot,!1);return!e||!e.panel?!1:(n.dispatch({effects:vd.of(!1)}),!0)},e1=xe.fromClass(class{constructor(n){this.view=n,this.timeout=-1,this.set=!0;let{delay:e}=n.state.facet(Kt);this.lintTime=Date.now()+e,this.run=this.run.bind(this),this.timeout=setTimeout(this.run,e)}run(){clearTimeout(this.timeout);let n=Date.now();if(nPromise.resolve(i(this.view))),i=>{this.view.state.doc==e.doc&&this.view.dispatch(wd(this.view.state,i.reduce((r,s)=>r.concat(s))))},i=>{We(this.view.state,i)})}}update(n){let e=n.state.facet(Kt);(n.docChanged||e!=n.startState.facet(Kt)||e.needsRefresh&&e.needsRefresh(n))&&(this.lintTime=Date.now()+e.delay,this.set||(this.set=!0,this.timeout=setTimeout(this.run,e.delay)))}force(){this.set&&(this.lintTime=Date.now(),this.run())}destroy(){clearTimeout(this.timeout)}});function t1(n,e,t){let i=[],r=-1;for(let s of n)s.then(o=>{i.push(o),clearTimeout(r),i.length==n.length?e(i):r=setTimeout(()=>e(i),200)},t)}const Kt=K.define({combine(n){return{sources:n.map(e=>e.source).filter(e=>e!=null),...yt(n.map(e=>e.config),{delay:750,markerFilter:null,tooltipFilter:null,needsRefresh:null,hideOn:()=>null},{delay:Math.max,markerFilter:rh,tooltipFilter:rh,needsRefresh:(e,t)=>e?t?i=>e(i)||t(i):e:t,hideOn:(e,t)=>e?t?(i,r,s)=>e(i,r,s)||t(i,r,s):e:t,autoPanel:(e,t)=>e||t})}}});function rh(n,e){return n?e?(t,i)=>e(n(t,i),i):n:e}function i1(n,e={}){return[Kt.of({source:n,config:e}),e1,Kd]}function Pd(n){let e=[];if(n)e:for(let{name:t}of n){for(let i=0;is.toLowerCase()==r.toLowerCase())){e.push(r);continue e}}e.push("")}return e}function Cd(n,e,t){var i;let r=t?Pd(e.actions):[];return he("li",{class:"cm-diagnostic cm-diagnostic-"+e.severity},he("span",{class:"cm-diagnosticText"},e.renderMessage?e.renderMessage(n):e.message),(i=e.actions)===null||i===void 0?void 0:i.map((s,o)=>{let l=!1,a=d=>{if(d.preventDefault(),l)return;l=!0;let p=Ni(n.state.field(Ot).diagnostics,e);p&&s.apply(n,p.from,p.to)},{name:c}=s,h=r[o]?c.indexOf(r[o]):-1,u=h<0?c:[c.slice(0,h),he("u",c.slice(h,h+1)),c.slice(h+1)],f=s.markClass?" "+s.markClass:"";return he("button",{type:"button",class:"cm-diagnosticAction"+f,onclick:a,onmousedown:a,"aria-label":` Action: ${c}${h<0?"":` (access key "${r[o]})"`}.`},u)}),e.source&&he("div",{class:"cm-diagnosticSource"},e.source))}class n1 extends Ut{constructor(e){super(),this.sev=e}eq(e){return e.sev==this.sev}toDOM(){return he("span",{class:"cm-lintPoint cm-lintPoint-"+this.sev})}}class sh{constructor(e,t){this.diagnostic=t,this.id="item_"+Math.floor(Math.random()*4294967295).toString(16),this.dom=Cd(e,t,!0),this.dom.id=this.id,this.dom.setAttribute("role","option")}}class ns{constructor(e){this.view=e,this.items=[];let t=r=>{if(!(r.ctrlKey||r.altKey||r.metaKey)){if(r.keyCode==27)nh(this.view),this.view.focus();else if(r.keyCode==38||r.keyCode==33)this.moveSelection((this.selectedIndex-1+this.items.length)%this.items.length);else if(r.keyCode==40||r.keyCode==34)this.moveSelection((this.selectedIndex+1)%this.items.length);else if(r.keyCode==36)this.moveSelection(0);else if(r.keyCode==35)this.moveSelection(this.items.length-1);else if(r.keyCode==13)this.view.focus();else if(r.keyCode>=65&&r.keyCode<=90&&this.selectedIndex>=0){let{diagnostic:s}=this.items[this.selectedIndex],o=Pd(s.actions);for(let l=0;l{for(let s=0;snh(this.view)},"×")),this.update()}get selectedIndex(){let e=this.view.state.field(Ot).selected;if(!e)return-1;for(let t=0;t{for(let h of c.diagnostics){if(o.has(h))continue;o.add(h);let u=-1,f;for(let d=i;di&&(this.items.splice(i,u-i),r=!0)),t&&f.diagnostic==t.diagnostic?f.dom.hasAttribute("aria-selected")||(f.dom.setAttribute("aria-selected","true"),s=f):f.dom.hasAttribute("aria-selected")&&f.dom.removeAttribute("aria-selected"),i++}});i({sel:s.dom.getBoundingClientRect(),panel:this.list.getBoundingClientRect()}),write:({sel:l,panel:a})=>{let c=a.height/this.list.offsetHeight;l.topa.bottom&&(this.list.scrollTop+=(l.bottom-a.bottom)/c)}})):this.selectedIndex<0&&this.list.removeAttribute("aria-activedescendant"),r&&this.sync()}sync(){let e=this.list.firstChild;function t(){let i=e;e=i.nextSibling,i.remove()}for(let i of this.items)if(i.dom.parentNode==this.list){for(;e!=i.dom;)t();e=i.dom.nextSibling}else this.list.insertBefore(i.dom,e);for(;e;)t()}moveSelection(e){if(this.selectedIndex<0)return;let t=this.view.state.field(Ot),i=Ni(t.diagnostics,this.items[e].diagnostic);i&&this.view.dispatch({selection:{anchor:i.from,head:i.to},scrollIntoView:!0,effects:kd.of(i)})}static open(e){return new ns(e)}}function Qr(n,e='viewBox="0 0 40 40"'){return`url('data:image/svg+xml,${encodeURIComponent(n)}')`}function pr(n){return Qr(``,'width="6" height="3"')}const r1=R.baseTheme({".cm-diagnostic":{padding:"3px 6px 3px 8px",marginLeft:"-1px",display:"block",whiteSpace:"pre-wrap"},".cm-diagnostic-error":{borderLeft:"5px solid #d11"},".cm-diagnostic-warning":{borderLeft:"5px solid orange"},".cm-diagnostic-info":{borderLeft:"5px solid #999"},".cm-diagnostic-hint":{borderLeft:"5px solid #66d"},".cm-diagnosticAction":{font:"inherit",border:"none",padding:"2px 4px",backgroundColor:"#444",color:"white",borderRadius:"3px",marginLeft:"8px",cursor:"pointer"},".cm-diagnosticSource":{fontSize:"70%",opacity:.7},".cm-lintRange":{backgroundPosition:"left bottom",backgroundRepeat:"repeat-x",paddingBottom:"0.7px"},".cm-lintRange-error":{backgroundImage:pr("#f11")},".cm-lintRange-warning":{backgroundImage:pr("orange")},".cm-lintRange-info":{backgroundImage:pr("#999")},".cm-lintRange-hint":{backgroundImage:pr("#66d")},".cm-lintRange-active":{backgroundColor:"#ffdd9980"},".cm-tooltip-lint":{padding:0,margin:0},".cm-lintPoint":{position:"relative","&:after":{content:'""',position:"absolute",bottom:0,left:"-2px",borderLeft:"3px solid transparent",borderRight:"3px solid transparent",borderBottom:"4px solid #d11"}},".cm-lintPoint-warning":{"&:after":{borderBottomColor:"orange"}},".cm-lintPoint-info":{"&:after":{borderBottomColor:"#999"}},".cm-lintPoint-hint":{"&:after":{borderBottomColor:"#66d"}},".cm-panel.cm-panel-lint":{position:"relative","& ul":{maxHeight:"100px",overflowY:"auto","& [aria-selected]":{backgroundColor:"#ddd","& u":{textDecoration:"underline"}},"&:focus [aria-selected]":{background_fallback:"#bdf",backgroundColor:"Highlight",color_fallback:"white",color:"HighlightText"},"& u":{textDecoration:"none"},padding:0,margin:0},"& [name=close]":{position:"absolute",top:"0",right:"2px",background:"inherit",border:"none",font:"inherit",padding:0,margin:0}},"&dark .cm-lintRange-active":{backgroundColor:"#86714a80"},"&dark .cm-panel.cm-panel-lint ul":{"& [aria-selected]":{backgroundColor:"#2e343e"}}});function s1(n){return n=="error"?4:n=="warning"?3:n=="info"?2:1}function Td(n){let e="hint",t=1;for(let i of n){let r=s1(i.severity);r>t&&(t=r,e=i.severity)}return e}class $d extends Mt{constructor(e){super(),this.diagnostics=e,this.severity=Td(e)}toDOM(e){let t=document.createElement("div");t.className="cm-lint-marker cm-lint-marker-"+this.severity;let i=this.diagnostics,r=e.state.facet(Ps).tooltipFilter;return r&&(i=r(i,e.state)),i.length&&(t.onmouseover=()=>l1(e,t,i)),t}}function o1(n,e){let t=i=>{let r=e.getBoundingClientRect();if(!(i.clientX>r.left-10&&i.clientXr.top-10&&i.clientYe.getBoundingClientRect()}}})}),e.onmouseout=e.onmousemove=null,o1(n,e)}let{hoverTime:r}=n.state.facet(Ps),s=setTimeout(i,r);e.onmouseout=()=>{clearTimeout(s),e.onmouseout=e.onmousemove=null},e.onmousemove=()=>{clearTimeout(s),s=setTimeout(i,r)}}function a1(n,e){let t=Object.create(null);for(let r of e){let s=n.lineAt(r.from);(t[s.from]||(t[s.from]=[])).push(r)}let i=[];for(let r in t)i.push(new $d(t[r]).range(+r));return N.of(i,!0)}const c1=Bu({class:"cm-gutter-lint",markers:n=>n.state.field(al),widgetMarker:(n,e,t)=>{let i=[];return n.state.field(al).between(t.from,t.to,(r,s,o)=>{r>t.from&&ri.is(Jl)?i.value:t,n)},provide:n=>Os.from(n)}),h1=R.baseTheme({".cm-gutter-lint":{width:"1.4em","& .cm-gutterElement":{padding:".2em"}},".cm-lint-marker":{width:"1em",height:"1em"},".cm-lint-marker-info":{content:Qr('')},".cm-lint-marker-warning":{content:Qr('')},".cm-lint-marker-error":{content:Qr('')}}),u1=Eu(Jb,{hideOn:xd}),Kd=[Ot,R.decorations.compute([Ot],n=>{let{selected:e,panel:t}=n.field(Ot);return!e||!t||e.from==e.to?B.none:B.set([_b.range(e.from,e.to)])}),u1,r1],Ps=K.define({combine(n){return yt(n,{hoverTime:300,markerFilter:null,tooltipFilter:null})}});function f1(n={}){return[Ps.of(n),al,c1,h1,Rd]}const Zd=1,Ue=Object.freeze({ready:"studio.ready",documentOpen:"document.open",documentReplace:"document.replace",documentChanged:"document.changed",documentSave:"document.save",documentSetReadOnly:"document.setReadOnly",documentResources:"document.resources",editorInsert:"editor.insert",editorFormat:"editor.format",editorSetTheme:"editor.setTheme",editorGetState:"editor.getState",diagnosticsPublish:"diagnostics.publish"});function d1(n,e={},t=null,i=null){return{protocolVersion:Zd,type:n,documentID:t,...i===null?{}:{sessionID:i},payload:e}}function p1(n){if(window.WKWebViewJavascriptBridge){n(window.WKWebViewJavascriptBridge);return}if(window.WKWVJBCallbacks){window.WKWVJBCallbacks.push(n);return}window.webkit?.messageHandlers?.iOS_Native_InjectJavascript&&(window.WKWVJBCallbacks=[n],window.webkit.messageHandlers.iOS_Native_InjectJavascript.postMessage(null))}function nn(n,e,t={},i=null,r=()=>{},s=null){if(!n){r({result:"unavailable"});return}n.callHandler(e,d1(e,t,i,s),r)}function m1(n){const e={capabilities:["jsx","format","search","completion","diagnostics"]};nn(n,Ue.ready,e)}const O1=[{id:"$storage.getString",arguments:["key"],returns:"string",documentation:"Read a package-scoped string. Missing keys return an empty string."},{id:"$storage.setString",arguments:["key","value"],returns:"boolean",documentation:"Set a package-scoped string, limited to 256 KiB of UTF-8. Check the success result."},{id:"$storage.getJSON",arguments:["key"],returns:"json",documentation:"Read a package-scoped JSON object. Missing or invalid values return an empty object."},{id:"$storage.setJSON",arguments:["key","value"],returns:"boolean",documentation:"Store a valid JSON object, limited to 256 KiB encoded. Check the success result."},{id:"$storage.compareAndSetString",arguments:["key","expected","value"],returns:"boolean",minimumRuntimeVersion:"1.3",documentation:"Atomically replace a string only when it still matches expected. A missing key matches an empty string. Conflicts return false; reread and retry a bounded number of times. Requires the shared storage container."},{id:"$storage.compareAndSetJSON",arguments:["key","expected","value"],returns:"boolean",minimumRuntimeVersion:"1.3",documentation:"Atomically replace a JSON object if its current value equals expected, independent of property order. A missing key matches an empty object. Wrong types and conflicts return false. Requires the shared storage container."},{id:"$storage.remove",arguments:["key"],returns:"boolean",documentation:"Remove a key from this package only."},{id:"$storage.keys",arguments:[],returns:"string[]",documentation:"List keys in this package's namespace."},{id:"$storage.clear",arguments:[],returns:"boolean",documentation:"Clear this package's storage. Other packages are unaffected."}].map(n=>({minimumRuntimeVersion:"1.0",...n,preview:"initialState"})),cl="2026-01-01T00:00:00Z",Wt="2026-01-01T12:00:00Z",ao=(n,e)=>({value:n,unit:e,hasSamples:!0,start:cl,end:Wt}),oh=(n,e,t)=>Object.entries(e).map(([i,r])=>({id:`$${n}.${i}`,group:n,value:r,legacy:[t,i]})),g1=[{id:"$music.isAvailable",group:"music",value:!0},{id:"$music.authorizationStatus",group:"music",value:"authorized"},{id:"$music.current",group:"music",value:{provider:"appleMusic",state:"playing",hasItem:!0,title:"Morning Light",artist:"ScriptWidget Sessions",album:"Focus",duration:224,position:86,capturedAt:Wt}},{id:"$services.status",group:"services",value:"connected"},{id:"$services.profile",group:"services",value:{service:"github",subject:"42",username:"octocat",displayName:"Mona",publicRepositories:32,privateRepositories:8,followers:128,following:24}},...oh("device",{screen:{width:390,height:844,scale:3},language:"en",model:"iPhone",name:"My iPhone",battery:{level:.8,state:"unplugged"},systemVersion:"26.0",isdarkmode:!1,totalDiskSpace:128e9,freeDiskSpace:64e9},"device"),...oh("system",{appInfo:{name:"ScriptWidget",bundleId:"com.everettjf.scriptwidget",version:"27.1.2",build:"27012"},locale:"en_US",preferredLanguages:["en-US"],timeZone:{identifier:"UTC",abbreviation:"GMT",offsetSeconds:0},is24HourClock:!1,calendarInfo:{identifier:"gregorian",firstWeekday:1,minimumDaysInFirstWeek:1},systemUptime:3600,memory:{physical:8589934592},thermalState:"nominal",lowPowerMode:!1,brightness:.5,reduceMotionEnabled:!1,platform:"ios",hostName:"My iPhone",processName:"ScriptWidgetWidget",osVersionString:"Version 26.0",processorCount:6,activeProcessorCount:6},"system"),{id:"$photos.isAvailable",group:"photos",value:!0},{id:"$photos.authorizationStatus",group:"photos",value:"authorized"},{id:"$photos.requestAuthorization",group:"photos",value:!0},{id:"$photos.items",group:"photos",value:{status:"ready",items:[{id:"sample.photo",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/biiiiu08cKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/biiiiu08cKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//R/biiiiu08cKKKKACiiigAooooAKKKKACiiigAooooAKKrXl5a6fbPe3sgiijG5mY4AFOtbq3vbZLu0cSRSAMrLyCDQK/QnooooGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/9L9uKKKK7TxwooooAKKKKACiiigAooooAKKKKACs/VNUsNGsX1HUpBFDGMkn+Q9Sewqtruvab4c099S1STYi8AfxMeyqO5NfInjDxnqfi++865Pl26H91CDwo9T6t6mhs5cTiVSXmX/ABx49v8Axdc+UmYbKM/u4vX/AGm9T+g/U6/w38fv4buRpOqMTYynr18pj3H+ye4/H1z5TRU3PHVean7S+p+gSOkiCSMhlYZBHIIPcU6vmn4YfEL+zXTw5rb/AOjscQyN/wAsyf4Sf7p7Ht9On0tVHuUKyqx5kFFFFBsFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABTXdIkMkhwo5JNOrjPEN8ZJfsUZ+VeW9z/8AWry84zOGXYd4iSu9ku7O7L8FLF1lSjt19Av/ABDLIxjsvkX+8ep/wrn5J5pjmVyx9zmoqK/HsdmuJxsnPETb8ui9EfoWFwNHDR5aUbefX7yWOeaE5icqfY4roLDxDLGwjvfnX+8Oo/xrmqKMDmuJwUlPDza8uj9UGKwNHEx5asb+fX7z1ZHSVBJGcqeQRTq4zw9fGOX7FIflblfY/wD167Ov2HJ8zhmOHWIirPZrsz89zDBSwlZ0pbdPQKKKK9Q4QooooAKKKKAP/9P9uKKKK7TxwooooAKKKKACiiigAooooAK5zxP4o0vwpp5vtRbJPEcY+87eg/qe1UvGHjPTfCFj51yfMncfuoQeWPqfRfU18ia7r2peI9QfUtUk3u3AH8KjsqjsBQ2ceKxape7Hct+J/FGqeK9QN9qLYUcRxj7qL6D+p71zdFFSeJKTk7yCiiigQV9EfC74h+cI/DGuSfOPlt5W7+iMfX+6e/T0r53pQSDkcEUGtGs6UuaJ+gdFeOfDP4hDXIl0LWX/ANMjH7tz/wAtVH/sw7+o59a9jqj36VRVI80QooooNAooooAKKKKACiiigAooooAKKKKACiiigAryyeQzTPKerEn869TryyeMwzPEeqkj8q+B475uShba8vv0t+p9Xwvbmq99P1IqKKK/OD7AKKKKAJYJDDMko6qQfyr1OvLIIzNMkQ6sQPzr1Ov0fgTm5K99rx+/W/6Hx/FFual31/QKKKK++PlAooooAKKKKAP/1P24ooortPHCiiigAooooAKKKKACuA8cePbDwja+UmJr2Qfu4vT/AGm9B+p/UZ3j74i2vheJtO04iW/YdOqxg929/QfieOvypeXl1qF097eyGWWQ7mZjkk0mzgxWM5PchuT6pql/rN8+o6lIZZpDkk/yHoB2FZ9FFI8du+rCiiigQUUUUAFFFFAEkM0tvKs8DFHQhlYHBBHQg19afDvx7F4qtPsN+Qt/CvzDp5gH8Q/qP6V8j1bsb670y8jv7FzHNEdysOoNCZ0Yeu6Ur9D75orhvA3jW08X6dvOI7uIATR/+zL/ALJ/Tp9e5qj3oTU1zRCiiigoKKKKACiiigAooooAKKKKACiivnP43/HOz+Hts2geH2WfWZV9ituCOGcd2/ur+J4wC0rmVatGjFzqPQufGv436f8ADizbR9IK3GszL8qdVhB6PJ7/AN1e/U8deK+GvxhsfiHCtnqu231dF/eIOFmwPvp7/wB5e3UcdPge+vrzU7yXUdRlaeeZi8kjnLMx6kk0y1urmxuY7yzkaKWJgyOpwykdCCK87N8qhmOHeHm7PdPszycv4lrYTFLExXu7OPdf5n6sUV4l8H/ivD46jXQNXZYtXQfKDhRcAd07bx3X8RxkD3KSCaE4lQqfcYr8bx+U4nAzcMRBrz6P0Z+y5fmuHx9NVcNO9+nVeqIqKljgmmOIkLH2Ga6Cw8PSyMJL35F/ujqf8KWByrE42Shh4N+fRerNsVjqOGjzVZW8uv3B4esTJL9tkHyrwvuf/rV2dNREiQRxjCjgAU6v2HJ8shl2HWHi7vdvuz89zDGyxdZ1ZbdPQKKKK9Q4QooooAKKKKAP/9X9uKKKK7TxwooooAKKKKACvHfiF8S4tDD6NoTB7zo79Vi/xb27d/Ssr4ifE8WvmaF4akzLyss69F9VQ+vqe3bnp85kliWY5J6mk2ebi8Zb3Kf3j5ZZZ5WnnYu7klmY5JJ6kmo6KKR5IUUUUAFFFFABRRRQAUUUUAFFFFAGpo2s3+g6jHqmmvsljP4EdwR3B719keEvFVh4t0tb+0O2RcCWM9Ub+oPY9/rmviOug8M+JNQ8LaompWB6cOh+669wf6HsaEzrwuJdJ2ex9yUVi+H9f0/xJpkeqac2Ufgqeqt3U+4/+vW1VHuJpq6CiiigYUUUUAFFFFABRRXy38dfj1D4Ojk8J+EZFk1ZhtllGCtsD+hk9B/D1PpTSuY168KMHOoy58cvjvbeBYZPDPhh1m1iRcO3DLbA9z2LkfdXt1PYH857q6ub65kvb2RpZpWLu7klmYnJJJ5JNMnnmupnubl2kkkYszMSWZjySSeSSepqKtErHyGLxk8TLmlt0QUUUUzkJYJ57WdLm2do5I2DI6khlYHIII5BB71+hvwL+PkHjBIvCfi+RYtVUbYpjwtwB29BJ7dG7c8V+dtPR3icSxEqynII4II6EGk1c6sJi54eXNHbqj9taK+RvgT+0AniAQ+DfHEoW/4S3uWOBP6K57Seh/i/3uv1zWbVj6/D4iFeHPTYUUUUjcKKKKACiiigAooooA//1v24ooortPHCiio5ZYoImnnYIiAlmY4AA6kmgB5IUFmOAK+c/iJ8UDdeZoXhqTEXKyzr1b1VD6ep79uOuX8Q/iXLrhfRtCYpZjh36NL/AIL7d+/pXjlJs8rFYy/uU/vCiiikeYFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAHYeDfF974Q1MXUOXgkwJouzD1H+0Oxr7F0vU7LWbCPUtOcSQyjKkfyPoR3FfBVeheAfHNx4Sv/KnJeymP7xP7p/vr7jv6j8KaZ3YTFezfJLY+wqKgtbq3vbZLu0cSRSAMrLyCDU9M9oKKKKACiivkD48fH9dEE3gvwNNm95S5ukPEPYoh/v8Aqf4eg+b7rSuYYjEQoQ55su/Hf4+x+GFl8HeC5Q+pHKz3C8i39VX1k9f7v16fn5JJJNI00zF3YksxOSSepJ9aRmZ2LuSSTkk9SabWiVj5DFYueIlzT+SCiiimcoUUUUAFFFFACgkHI4Ir7p+BH7QYvvJ8FePZsT8JbXjn7/YJKT/F2Dd+h55PwrRSaudOGxU8PPngftxRXw58CP2gzB5Pgrx9P8nCW145+72CSk9vRj06HjkfcYOeRWbVj6/DYmGIhzwCiiikdAUUUUAFFFFAH//X/biiis/VNUsNGsX1HUpBFDGMkn+Q9SewrtPGbtqye8vLXT7Z729kEUUY3MzHAAr5U8ffEW68UStp2nExWCnp0aQju3t6D8Tz0zvHHj2/8XXXlJmGyjP7uL1/2m9T+g/WuApNnj4rGOfuQ2CiiikcAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB6v8N/H7+G7gaTqjE2Mp69fKY9x/snuPxHfP1WjpIgkjIZWGQRyCD3Ffn7Xt3ww+IX9munhzXH/0djiGRv8AlmT/AAk/3T2Pb6dGmelg8Vy/u57H0tRRXxH8ef2gf9d4J8BT+qXV5GfwMcRH5Mw+g9apK53YnEww8OeZe+PP7QIsfO8E+BJv3/KXV2h+52McZH8XZmHToOeR8LEknJpKK0SsfIYrFTxE+eYUUUUzmCiiigAooooAKKKKACiiigAr7A+BH7QLaL5Pgzx1MTZcJbXTnmHsEkP9z0b+Hv8AL934/opNXN8PiJ0J88GftsrK6h0IKkZBHQinV+enwK+PsvhRovCHjKQyaYSFhnPLW/oD3Mf6r244H6DwzRXES3FuweNwGVlOQQeQQR1BrNqx9fhMXDER5ofNElFFFI6gooooA//Q/Z/Xde03w5p76lqkmxF4A/iY9lUdya+RPGHjPUvF9951yfLt0P7qEHhR6n1b1NUvE/ijVPFeoG+1FsAcRxj7qL6D+p71zldjZ8XisW6vux2CiiikcYUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFISAMngCvGvGPjQ3m7StIbEXSSQfxew9vfv9OrSuRUqKCuz0Pxn8e/Ec/ho+BdGnxEMpLdKf3jR9PLU+nYt1I46dfm6iitkrHn1a06rvNhRRRTMgooooAKKKKACiiigAooooAKKKKACiiigAr6a+B3x5uvA00fhjxS7TaO5wj8s1sT3Hcp6r26juD8y0UjahXnRkp03qfthaXdrfW0d7ZSLNDKodHQgqynkEEcEGrFfmf8EvjnffDy6XQtdLXGiytyOrW5J5ZPVe7L+I5zn9JNO1Gx1axi1PTJVnt51DxyIcqynoQazasfXYPGQxMbx36ouUUUUjsP/R+tKKKK6z8/CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApGZVUsxwBySaR3SNDJIQqqMkngAV4h4w8ZPqjNpumMVthwzdC/8A9j/OnGNzOpUUFdkvjHxm2oFtL0psQdHcdX9h/s/z+leb0UVslY86c3N3YUUUUyAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvePgx8bNT+Gl8NN1Hdc6PM2ZIurRE9Xjz39V6H2PNeD0UjSlWlSkpwdmftNo+saZr+mQ6xo0y3FtcLvjkQ5BH9COhB5B4NaVflf8IPjJrHww1LyZN1zpU7Znt88g9N8eejDuOjDg9iP038P+INH8UaRDrmhTrcWs67kdf1BHUEdCDyDUNWPrsFjo4mPZ9Uf//S+tKKKK6z8/CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACmSSRwxtLKwVVGSTwABSTTRW8TTzsERBkseABXhHi3xfLrchsrIlLVT9C5Hc+3oPz9nGNzOpVUFdkvi/xjJrDmw08lbVTyehkI7n29B+ftwNFFbJWPNnNyd2FFFFMkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvWvhP8W9b+F+r+bBm40+dh9otieG7bk/uuB379D2x5LRSLp1JU5KcHZo//0/rSiiius/PwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACobi4gtYWublgiIMsx6AU26ureyt3urpwkaDJY14H4q8V3GvzeTDmO1Q/Kvdvdv8O1OMbmVWqoIk8WeLZtdlNrbZS1Q8Du59T/AEFcXRRWyVjzpScndhRRRTJCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9T60ooorrPz8KKKKACiiigAooooAKKKKACiiigAooooAKKKKACq15eW2n2z3d44SNBkk0y/v7XTbV7y9cJGg5J/kPU18/eJfE114hucnKW6H5I/6n1P8qqMbmVWqoLzJPFHim58QXGxcx2yH5E9fdvf+VcpRRWqVjzpScndhRRRTJCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9X60ooorrPz8KKKKACiiigAooooAKKKKACiiigAooooAKo6jqNppVo17etsRfzJ9B6mm6pqlnpFm17ettReg7k+gHrXz34h8Q3niC786c7Y1+5GOij+p9TVRjcxq1lBeY/xH4ku/EN1vk+SFD8kfYe59TXOUUVqedKTbuwooopiCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9b60oqRYZX+4pP0FWV03UX+5byH6Kf8K6z4CzKVFay6Drj/AHLKc/SNv8KsJ4V8Tv8Ac026P0hf/CgfJLsYNFdOngvxc/TTbn8Y2H8xVhPAHjN+mnTfiMfzoH7KfZnIUV3C/Dbxu/TT3/FkH82qyvws8dt/y44+skf/AMVQV7Cp/K/uPPqK9JT4S+OG62yL9ZE/oTVlfg94zbqkK/WQf0BosP6vU/lZ5bRXra/Bjxc3V7Zfq7f0U1YX4JeKD9+4tR/wJz/7JRYf1Wr/ACnjlZmravZaLZte3rYUcADqx9BXpXjnwZH8PNCfXPEeoQKv3Y4k3GSV/wC6gIH4noB1r4t1vXL3Xrw3V2cAcIg6KPQf1PeqjG5y4mTo+7Jaj9e1+91+8NzcnCDhEHRR/j6msOiitTy223dhRRRTEFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/Z",width:384,height:256},{id:"sample.photo.2",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/ayiiivVAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9D9rKKKK9UAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoooo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AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApGZUUu5wBySe1NkkSJDJIQqqMkngADua+f8Axv46fWGbStJYrag4Zuhk/wDsf51dOm5uyJbsT+OPHbamX0jR2xbDh5B1k9h/s/z+leV0UV6UIKKsjJu4UUUVYgooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvoL4IfHTVfhdfjTNS3XWiztmSHq0RPWSPPf1Xo3sea+faKzq0o1IuE1oVGbi+aJ+5Gi61pXiLS4Na0SdLm1uF3xyIcgj+hHQg8g8HmtSvyU+DHxs1r4Vap5Em660i4bM9tnkHp5keeA47jow4PYj9UPDviPRfFmjQa/4fnW5tbhdyOv6gjqCDwQeQa+ZxeElQl5Hq0ayqLzP//S+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACo5ZYoI2mmYIiDLMTgADuaSeeG2ha4uGCIgyzHgACvnbxp42m1+Q2NgSlmp+hkI7n29B+J9tKVJzdkTKViXxt44k1x203TSUtFPJ6GQjuf9n0H4n284oor0oxUVZGLdwoooqgCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAr2H4QfGLXvhTrPm2+bjTZ2H2m1J4btvT+64Hfv0PbHj1FROEZxcZrQcZOLuj//T+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKgubm3s7d7q6cRxxjLMegFNu7y2sLZ7y8cRxxjLMe1fN/jDxlc+JJ/IgzHaIflTux/vN7+g7VpSpObJlKxL4y8aXHiKY2lpmOzQ8L0Lkd2/oK4OiivSjFRVkYt3CiiiqAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//1PqCiiivszygooooAKKKKACiiigAooooAKKKKACiiigAooooAKqX19aabave3riOKMZJP+etN1HUbPSrN76/cRxoOSf5D1J9K+afFfiy88TXeTmO2Q/u4/6t6n+XataVJzfkTKViXxd4vu/EtzsTMdrGfkj9f9pvf+VcbRRXoxioqyMW7hRRRVAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH//V+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACs/VNUstHsnv799kafmT2AHcmmavq9jolk1/qD7UXoO7HsAO5NfM3iXxNfeJb3z7j5Il/1cY6KP6k9zW1Ki5vyJlKxL4o8VXvia78yX5IEP7uMdB7n1PvXLUUV6EYpKyMW7hRRRVAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//1vqCipFhlf7ik/QVZTTdRk+5byH6IT/SvsrnlWKVFay6Drr/AHLKc/SNv8Ksp4U8UP8Ac026P0hf/ClzLuOzMCiuoXwT4vfpplz+MbD+Yqynw/8AGj9NNm/EY/nS549x8j7HHUV3K/DXxy/TT3/FkH82qyvwq8ev/wAuGPrLEP8A2al7WHdByS7HntFelp8I/HLdbZF+sif0NWU+DnjRuqQr9ZB/QGl7aH8w/Zy7HldFeur8FfGDdXtl+rt/Rasr8D/FR+/c2o/4E5/9kpe3h3H7KXY8ZrJ1rWrDQbFr+/bCjhVHVj6AetemePvB0Xw20B9f8TajAq/diiTcZJX/ALqAgZ9z0A5NfCviDxBf+Ir43l6cAcIg+6g9B/U966sPBVfeWxlUvDRkniPxHf8AiS9N1dnai8Rxjoo/x9TXP0UV6aSSsjAKKKKYBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH//Z",width:384,height:256},{id:"sample.photo.3",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//R/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKrXl5a6fave3sgiijG5mY4AFOtbq3vbZLu0cSRSAMrLyCDTt1AnooopAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/9L9vKKKK7T0wooooAKKKKACiiigAooooAKKKKACs/VNUsNGsX1HUpBFDGMkn+Q9Sewqtruvab4c099S1STYi8AfxMeyqO5NfInjDxnqfi++865PlwIf3UIPCj1Pq3qa3o0XUfkXCHMX/HHjy/8AF115SZhsoz+7iz1/2m9T+g/U6/w38fv4buRpWqMTYSnr18pj3H+ye4/Ed8+U0V6DpR5eS2h0cqtY/QJHSVBJGQysMgjkEHuKdXzT8MPiF/Zjp4c1t/8ARmOIZGP+rJ/hP+ye3p9On0tXmVKbg7M5pR5XYKKKKzJCiiigAooooAKKKKACiiigAooooAKKKKACiiigApkkiRIZJDtUckmn1xXiK/aSb7DGflTlvc//AFqmcuVXPJzrNYZbhpYiau9ku7C/8RTSMY7H5F/vHqf8K52SeaY5ldmPuc1FRXHKTe5+LY/NsVjpOeIm35dF6IljnmhOYnZT7HFdFYeIpo2Ed986/wB4dR/jXMUURk1sGAzbFYGSnh5teXR+qPWY5ElQSRncp5BFPrivDt+0c32GQ/K/K+x/+vXa12QlzK5+05LmsMyw0cRBWezXZhRRRVHrBRRRQAUUUUAf/9P9vKKKK7T0wooooAKKKKACiiigAooooAK5vxP4p0vwpp5vtRbLHiOMfec+g/qe1U/GHjPTfCFj51yfMuHH7qEHlj6n0X1NfIeu69qXiPUH1LVJN8jcAfwqOyqOwH+ea6KNBz1expCnfVlvxP4o1TxXqBvtRbAHEcY+6i+g/qe9c5RRXpJJKyOlK2wUUUUwCvoj4XfEPzRH4Y1yT5x8tvK3f0Rj6/3T36elfO9KCQcjgioqU1NWZMoqSsz9A6K8c+GfxBGtxLoWsv8A6ZGP3bn/AJaqP/Zh39Rz617HXlTg4PlZyyjZ2YUUUVAgooooAKKKKACiiigAooooAKKKKACiiigAryieQzTPKerMT+der15RPGYZniPVWI/KsK/Q/O/EDm5KFtry+/S36kVFFFcx+ZhRRRQBLBIYZklHVWB/KvV68ogjM0yRDqzAfnXq9dNDqfpnh/zcle+14/frf9Aooorc/RAooooAKKKKAP/U/byiiiu09MKKKKACiiigAooooAK4Dxx49sPCNr5SYmvZB+7i9P8Aaf0H6n8zWd4++Itr4XibTtOIlv2HTqsYPdvf0H4njr8qXl5dahcve3shllkO5mY5JNdVDD83vS2NYU76sn1TVL/Wb59R1KQyzSHJY/yHoB2FZ9FFeilbY6AooooAKKKKACiiigCSGaW3lWeBijoQysDggjoQa+s/h349i8U2n2G/IW/hX5h08xR/EP6j+lfJFW7G+u9MvI7+xcxzRHcrDqDWVWkqisROPMj75orhvA3jW08X6duOI7uIATR/+zL/ALJ/TpXc15couLszlatowoooqQCiiigAooooAKKKKACiiigAoor5v+OXx2svh5av4f8ADzLPrUq/VbdSOGf1b+6v4njAOlOnKpLlgioU3N8sS58bfjlp/wANrJtH0crca1MvyJ1WEHo8nv8A3V79Tx14j4Z/GOx+IsK2eq7bfWEX94g4WbA++nv/AHl7dRx0+Ab+/vdUvZdR1GVp552LySOcszHkkk0y0u7mxuY7yykaKWJgyOpwykdCCK9eeWQlS5Hv3FnPDtLMsI8LN2e6fZ/5H6wUV4f8HfixB47iXw/rDLFq6D5QcKLgDunbeO6/iOMge6yQTQnEqMp9xivmq1CdGThNH4HmeS4vLqjpYqm1br0foyKipY4JpjiJGY+wzXRWHh2aRhJffIv90dT/AIVnGLexngMpxWOkoYeDfn0Xqw8O2DSTfbpB8qcL7n/61drTI40iQRxjao4AFPrshHlVj9pyXKoZbho4eDu92+7CiiiqPWCiiigAooooA//V/byiiiu09MKKKKACiiigArxz4h/EuLQw+jaEwe8PDydVi/xb+Xf0rK+InxPFr5mheGpMy8rLOv8AD6qh9fU9u3PT50JLEsxyTXbQw9/embU6fVj5ZZZ5WmnYu7klmY5JJ6kmo6KK7jcKKKKACiiigAooooAKKKKACiiigDU0bWL/AEHUY9U019ksZ/AjuCO4Pevsjwl4qsPFulrf2nyyLxLGTyjf4Hse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",width:384,height:256},{id:"sample.photo.4",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/biiiiuc9UKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/biiiiuc9UKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKK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0ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApGZUUu5wBySaR3SNDJIQqqMkngACvBvGnjZ9WZtM0titqDhm6GT/7H+dJuxrSpOo7Im8a+OG1ItpOkNi36O46v7D/Z/n9K8woorNu560IKC5YhRRRSLCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK98+Cvxw1T4ZXw0zUd1zo07Zki6tET1ePPf1XofrzXgdFMyq0o1YuE1oftro2s6X4h0yHWdFnW5tbhd8ciHII/oR0IPIPBrTr8o/g58Z9Z+Fup+TJuudJnbM9vnkHp5keeA47jow4PYj9QvD3iHRvFWjw69oE63NrcLuR1/UEdQQeCDyDTTPlMbgpYeXddGf/9L60ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACmSyxwxtNMwVFGSTwAB3NNnnhtoWuLhgiIMsx4AAr5+8Y+M5tekNlYkpaKfoXI7n29B+J9k3Y2o0XUdkS+MvGsmtO2nacSlop5PQyEdz7eg/E155RRWbdz1YQUFyxCiiikWFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXrvwk+L2ufCzWPNt83GnTsPtNqTw3ben91wO/foe2PIqKDOpTjUi4TV0f/T+tKKKKyPtQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACobi4gtIHublwkaDLMegFNu7u2sbZ7u7cRxoMsx7V87+LfF9z4in8iHMdoh+VO7H+839B2pN2NqNF1H5EvjDxjP4gmNra5jtEPC93I7t/QVw1FFZtnqwgorliFFFFIsKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9T60ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAqre3trp1q95euI40GST/nrTNQ1C00u0e9vnCRoOSf5D1Jr5y8U+KrvxJdZOY7dD+7j/qfU/wAqTdjejRdR+RL4r8WXXiO52LmO1Q/Inr/tN7/yrkKKKzPVjFRVkFFFFIoKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//V+tKKKKyPtQooooAKKKKACiiigAooooAKKKKACiiigAqhqep2ekWb318+yNPzJ9AO5NM1XVrLRbJr6/fai9B3Y9gB3NfN/iPxJe+I7zz7j5Yl/wBXGOij+p9TUt2OihQdR+RJ4m8T3niO78yT5IEP7uPsPc+prmaKKg9SMVFWQUUUUigooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/W+tKKkWKV/uKT9BVlNO1CT7kEjfRT/hWR9rcpUVqpoWuP9yznP0jb/CrK+FvE7/c066P0hf8AwoFzLuYNFdMvgvxc/TTbn8Y2H8xVlfAPjJ+mnTfiMfzoF7SPc5Ciu4X4b+N36ae/4sg/masL8LfHb/8ALjj6yR//ABVIXtYfzI8/or0lPhL44brbIv1kT+hqynwf8Zt1SFfrIP6A0C9tD+ZHltFetL8GPFzdXt1+rt/RTVlfgl4oP37i1H/AnP8A7JQL6xT/AJjxysrWNZsdDsmvr5sKOAB1Y+gHrXpXjvwfF8OdBfXfEmoQKv3Yok3GSV/7qAgZ9z0A618Q69r194gvTd3hwBwiDoo9B/U96TdjrwtNVveWw/xB4hvvEV6bm6O1F4SMdFH+PqawaKKg9dJJWQUUUUhhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH/9k=",width:384,height:256}],unavailableCount:0,fetchedAt:Wt,source:"system",stale:!1}},{id:"$calendar.isAvailable",group:"calendar",value:!0},{id:"$calendar.authorizationStatus",group:"calendar",value:"authorized"},{id:"$calendar.requestAuthorization",group:"calendar",value:!0},{id:"$calendar.calendars",group:"calendar",value:[{id:"sample.personal",title:"Personal"}]},{id:"$calendar.events",group:"calendar",value:{status:"ready",items:[{id:"sample.event",calendarID:"sample.personal",title:"Read and recharge",start:"2026-01-01T15:00:00Z",end:"2026-01-01T15:30:00Z",allDay:!1}],fetchedAt:Wt,source:"system",stale:!1,truncated:!1}},{id:"$reminders.isAvailable",group:"reminders",value:!0},{id:"$reminders.authorizationStatus",group:"reminders",value:"authorized"},{id:"$reminders.requestAuthorization",group:"reminders",value:!0},{id:"$reminders.lists",group:"reminders",value:[{id:"sample.reminders",title:"Personal"}]},{id:"$reminders.items",group:"reminders",value:{status:"ready",items:[{id:"sample.reminder",listID:"sample.reminders",title:"Read a chapter",due:"2026-01-01T16:00:00Z",hasTime:!0,completed:!1,completedAt:null,priority:0}],fetchedAt:Wt,source:"system",stale:!1,truncated:!1}},{id:"$reminders.create",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$reminders.update",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$reminders.setCompleted",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$health.authorizationStatus",group:"health",value:"notDisclosed"},{id:"$health.sleepSummary",group:"health",value:{start:cl,end:Wt,timeZone:"UTC",hasSamples:!0,totalSleepSeconds:27e3,inBedSeconds:30600,awakeSeconds:1800,stages:{unspecified:0,core:16200,deep:5400,rem:5400},sourceCount:1,truncated:!1,conflictSeconds:0,aggregation:"union-awake-rem-deep-core-unspecified"}},{id:"$health.activitySummary",group:"health",value:{date:"2026-01-01",timeZone:"UTC",hasSamples:!0,moveMode:"activeEnergy",move:{value:480,goal:600,unit:"kcal"},exercise:{value:26,goal:30,unit:"min"},stand:{value:9,goal:12,unit:"count"}}},{id:"$health.workouts",group:"health",value:{start:cl,end:Wt,timeZone:"UTC",hasSamples:!0,truncated:!1,items:[{id:"550e8400-e29b-41d4-a716-446655440000",activity:"running",start:"2026-01-01T09:00:00.000Z",end:"2026-01-01T09:45:00.000Z",duration:2700,energyKcal:285,distanceMeters:5200}]}},{id:"$health.isAvailable",group:"health",value:!0},{id:"$health.requestAuthorization",group:"health",value:!0,legacy:["health"]},{id:"$health.stepCountToday",group:"health",value:ao(6840,"count"),legacy:["health.stepCountToday"]},{id:"$health.activeEnergyToday",group:"health",value:ao(420,"kcal"),legacy:["health.activeEnergyToday"]},{id:"$health.heartRateLatest",group:"health",value:ao(72,"bpm"),legacy:["health.heartRateLatest"]},{id:"$location.isAvailable",group:"location",value:!0},{id:"$location.authorizationStatus",group:"location",value:"authorizedWhenInUse"},{id:"$location.isAuthorizedForWidgetUpdates",group:"location",value:!0},{id:"$location.requestAuthorization",group:"location",value:!0,legacy:["location.authorization"]},{id:"$location.current",group:"location",value:{latitude:37.3349,longitude:-122.009,altitude:0,accuracy:10,verticalAccuracy:10,speed:-1,course:-1,timestamp:Wt,accuracyAuthorization:"full",age:0,isStale:!1},legacy:["location"]}],y1={music:{platforms:["ios"],authorization:"musicRead",notes:"Runtime 1.4. Read-only Music app playback, not every audio player. Requires an installed owner, music permission and Music library approval in native Settings. current() is asynchronous and returns provider, state, hasItem, nullable title/artist/album/duration/position and capturedAt. State may be playing while details are absent (Home Sharing). Native player access is main-thread-only: synchronous hosts prepare one snapshot before JSC waits; background previews capture on the main queue. No playback/queue mutation, scans, artwork, credential or subscription inference. No live queries in future entries/Live Activities. Main system-player API is unavailable on macOS; Web supplies editable samples, never actual device access."},services:{platforms:["ios","macos"],authorization:"serviceAccount",notes:"Runtime 1.4: status(service) and profile(service) are asynchronous. Only provider: github. Unknown providers reject with service_operation. Requires an installed owner and services permission. profile additionally requires network and api.github.com in networkDomains, a native connected GitHub account and explicit owner grant. Native Settings handles credentials; scripts cannot log in or obtain tokens. profile returns service, subject, username, displayName, publicRepositories, privateRepositories, followers and following (missing values are null). No live queries in future entries or Live Activities. Eight shared data queries; native requests also share the eight concurrent/32 total network budget, while Web uses its existing preview network budget. Web uses editable best-state samples only; it never authenticates or sends these requests."},photos:{platforms:["ios","macos"],authorization:"photosRead",notes:"Requires photos permission and explicit native photo/album selection (none by default). Limited Photos authorization is supported. items({limit:1..4,offset:0..63,maxAgeSeconds:0..86400}) returns bounded, metadata-stripped JPEG thumbnails as image data URLs. Four 768px / 128KiB thumbnails at most; private snapshots expire after 24h and are rechecked against current access, selected sources and asset revisions. Cloud-only items report unavailableCount; download them in Photos first. Future timeline/Live Activity presentations read matching snapshots only. Web uses editable sample data and photosSelection IDs; no actual library access."},reminders:{platforms:["ios","macos"],authorization:"remindersRead",notes:"Requires reminders in widget.json and full system Reminders access. Reads require native Settings selected lists. create/update/setCompleted additionally require remindersWrite and a native declared action, stable requestID, durable replay receipts and an unchanged selected list. Web writes update preview-only items/receipts; receipt identities are derived from that state, with configurable API errors. Use lists() for IDs. items() selects pending due dates or completed completion dates in a positive window of at most 31 days, up to 50 items. Undated pending reminders are excluded. remindersSelection mocks the user scope without granting permission."},calendar:{platforms:["ios","macos"],authorization:"calendarRead",notes:"Requires calendar in widget.json and full system Calendar access. The user's native Settings source selection limits metadata, events and cached snapshots; scripts cannot enlarge it. Use calendars() for permitted IDs and show setup when no sources are selected. Queries are read-only, limited to 31 days and 50 items. Preview calendarSelection simulates this scope without granting package permission."},device:{platforms:["ios","macos"],authorization:"none",notes:"Device values depend on hardware. Some legacy Mac values are placeholders; inspect the API documentation before using them as measurements."},system:{platforms:["ios","macos"],authorization:"none",notes:"System metadata, not whole-device CPU, FPS or network traffic monitoring. Hardware-specific values may be unavailable."},health:{platforms:["ios"],authorization:"healthRead",notes:"Read-only HealthKit. Request access in the app. Runtime 1.4 adds sleepSummary (up to 7 days / 256 samples, union with explicit stage-conflict precedence), activitySummary (one local Gregorian day including active-energy or move-time mode and optional goals), and workouts (up to 31 days / 50 fully-contained summaries, optional kcal/meters). These require an explicit health manifest; all share the query budget and cannot run in future/Live Activity presentations. No Health readings are persisted by the API. Authorization completion does not reveal whether individual read types were granted; empty results must not be labelled permission denied. API 1.3 readings add hasSamples: false means no readable samples, never a read-denial verdict. Existing value/unit/start/end fields remain."},location:{platforms:["ios"],authorization:"locationWhenInUse",notes:"Package 2.0 requires location permission. Request system access in the app. Widgets also require isAuthorizedForWidgetUpdates. Cache is rechecked against authorization and precision, discarded after 24 hours or revocation, and reports age/isStale."}},A1=g1.map(n=>{const e=n.id.split(".")[1],t=n.group==="music"&&e==="current"||n.group==="services"||["calendar","reminders","photos"].includes(n.group)&&!["isAvailable","authorizationStatus"].includes(e)||n.group==="health"&&!["isAvailable","authorizationStatus"].includes(e)||n.group==="location"&&["current","requestAuthorization"].includes(e),i=n.group==="health"&&["sleepSummary","activitySummary","workouts"].includes(e)||n.group==="location"&&["current","requestAuthorization"].includes(e)||["calendar","reminders","photos"].includes(n.group)&&["events","items"].includes(e);return{...n,...y1[n.group],minimumRuntimeVersion:["music","services","reminders","photos"].includes(n.group)||n.group==="health"&&["sleepSummary","activitySummary","workouts"].includes(e)?"1.4":["calendar"].includes(n.group)||["$health.authorizationStatus","$location.isAuthorizedForWidgetUpdates"].includes(n.id)?"1.3":"1.0",arguments:n.group==="services"?["service"]:n.group==="reminders"&&["create","update","setCompleted"].includes(e)?["options"]:i?["options?"]:[],...n.group==="reminders"&&["create","update","setCompleted"].includes(e)?{authorization:"remindersWrite"}:{},asynchronous:t,nativeSource:`Shared/ScriptWidgetRuntime/Widget/API/ScriptWidgetRuntime${n.group[0].toUpperCase()}${n.group.slice(1)}.swift`,operation:n.group==="reminders"&&["create","update","setCompleted"].includes(e)?"write":e==="requestAuthorization"?"authorization":["isAvailable","authorizationStatus","isAuthorizedForWidgetUpdates"].includes(e)?"availability":"read",mock:"editable"}}),Re={globalProperties:{background:{type:"color",documentation:"Background color, hex value, or named color."},backgroundMaterial:{type:"enum",values:["none","ultraThin","thin","regular","thick","ultraThick"],documentation:"Runtime API 1.4. Public SwiftUI frosted material inside the widget, not Home Screen wallpaper blur or system Liquid Glass. At most four material backgrounds per tree. background supplies an opaque-composited Reduce Transparency fallback. In accented/vibrant mode local material is omitted for system rendering; none keeps the ordinary background."},color:{type:"color",documentation:"Foreground color, hex value, or named color."},font:{type:"enum",values:["largeTitle","title","title2","title3","headline","subheadline","body","callout","footnote","caption","caption2"],documentation:"Semantic text style."},fontWeight:{type:"enum",values:["ultraLight","thin","light","regular","medium","semibold","bold","heavy","black"],documentation:"Font weight."},fontDesign:{type:"enum",values:["default","rounded","serif","monospaced"],documentation:"Font design."},frame:{type:"number|string",documentation:'Square size (64 or "64"), width,height ("64,48"), or flexible frame ("max", "max,48").'},padding:{type:"number|string",documentation:"Padding amount or edge description."},corner:{type:"number",documentation:"Corner radius."},opacity:{type:"number",documentation:"Opacity from 0 through 1."},alignment:{type:"enum",values:["center","leading","trailing","top","bottom","topLeading","topTrailing","bottomLeading","bottomTrailing","firstTextBaseline","lastTextBaseline"],documentation:"Layout alignment."},clip:{type:"enum",values:["circle","rect","capsule","ellipse"],documentation:"Clip the view to a shape."},rotation:{type:"number",documentation:"Rotation in degrees."},rotation3d:{type:"string",documentation:"Static degree,x,y,z rotation with four finite numbers and a nonzero axis. Invalid values are ignored; axis magnitude is normalized. Reduce Motion removes the tilt. This does not read device motion or provide parallax."},shadow:{type:"string",documentation:"Shadow color, radius, and offset description."},animation:{type:"string",documentation:"iOS public SwiftUI scheduled clock/swing effect. Mac renders static standard-clock poses; other effects remain at rest. Signed clockCustom intervals specify a half-turn (full cycle = 2 * abs(interval)), minimum magnitude 0.25 seconds. App previews can animate; WidgetKit decides cadence and may freeze. Reduced motion/luminance freezes standard clock poses and keeps custom/swing effects at rest. Dynamic scripts are not rerun on animation ticks."},widgetAccentable:{type:"boolean",documentation:"Group this view into the accent layer in tinted and vibrant widget appearances."},linkurl:{type:"url",documentation:"Widget deep link URL."}},components:{VStack:{documentation:"Arrange children vertically.",children:!0,properties:{spacing:{type:"number"}}},HStack:{documentation:"Arrange children horizontally.",children:!0,properties:{spacing:{type:"number"}}},ZStack:{documentation:"Overlay children.",children:!0,properties:{}},VGrid:{documentation:"Vertical lazy grid.",children:!0,properties:{columns:{type:"string",required:!0},spacing:{type:"number"},alignment:{type:"enum",values:["leading","center","trailing"]}}},HGrid:{documentation:"Horizontal lazy grid.",children:!0,properties:{columns:{type:"string",required:!0},spacing:{type:"number"},alignment:{type:"enum",values:["top","center","bottom","firstTextBaseline","lastTextBaseline"]}}},Text:{documentation:"Display text content.",children:!1,properties:{text:{type:"string"}}},Date:{documentation:"Display a formatted date.",children:!1,properties:{date:{type:"date|number",required:!0},style:{type:"enum",values:["time","date","relative","offset","timer"]}}},Image:{documentation:"Display a bundled, remote, or SF Symbol image.",children:!1,properties:{systemName:{type:"string"},name:{type:"string"},id:{type:"string",deprecated:"Use name."},url:{type:"url"},src:{type:"url",deprecated:"Use url."},ratio:{type:"number"},mode:{type:"enum",values:["fit","fill"]},accentedRenderingMode:{type:"enum",values:["accented","desaturated","accentedDesaturated","fullColor"],documentation:"Control how the image is rendered in tinted and clear widget appearances."}}},Gif:{documentation:"Display a bounded package GIF using system decoding: up to 8 MiB input and 1,048,576 canvas pixels. iOS retains at most 60 downsampled frames / 4 MiB decoded pixels; more frames fall back to the first frame. Mac, Reduce Motion and reduced luminance use a static first frame. Active iOS previews use public SwiftUI timing with variable frame delays. Snapshots, inactive scenes, Mac and accessibility fallbacks use the first frame. No private clock bridge or continuous WidgetKit guarantee.",children:!1,properties:{file:{type:"string",required:!0}}},Spacer:{documentation:"Flexible layout space.",children:!1,properties:{minLength:{type:"number"}}},Rect:{documentation:"Rectangle shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},RoundedRect:{documentation:"Filled rounded rectangle.",children:!1,properties:{radius:{type:"number"}}},Capsule:{documentation:"Capsule shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Ellipse:{documentation:"Ellipse shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Circle:{documentation:"Circle shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Gauge:{documentation:"Display a gauge or instrument.",children:!1,properties:{type:{type:"string"},value:{type:"number"},style:{type:"enum",values:["circular","linear"]},text:{type:"string"},current:{type:"string"},min:{type:"string"},max:{type:"string"},angle:{type:"number"},thickness:{type:"number"},needleColor:{type:"color"},label:{type:"string"},title:{type:"string"},sections:{type:"string"}}},Chart:{documentation:"Render chart data.",children:!1,availability:{ios:"16.0",macos:"26.0"},properties:{type:{type:"enum",values:["bar","bar-x","bar-y","bar-gantt","line","point","line-point","area","rect","rule-x"]},data:{type:"json",required:!0},category:{type:"boolean"},hideLegend:{type:"boolean"},hideXAxis:{type:"boolean"},hideYAxis:{type:"boolean"}}},Link:{documentation:"Open a URL when selected.",children:!0,properties:{url:{type:"url",required:!0}}},Divider:{documentation:"Horizontal or vertical separator.",children:!1,properties:{thickness:{type:"number"},axis:{type:"enum",values:["horizontal","vertical"]}}},Line:{documentation:"Fixed-length line.",children:!1,properties:{thickness:{type:"number"},length:{type:"number"},axis:{type:"enum",values:["horizontal","vertical"]}}},Icon:{documentation:"SF Symbol icon.",children:!1,properties:{systemName:{type:"string",required:!0},size:{type:"number"}}},Label:{documentation:"Text paired with an SF Symbol.",children:!1,properties:{title:{type:"string"},systemName:{type:"string",required:!0}}},Progress:{documentation:"Linear or circular progress.",children:!1,properties:{value:{type:"number",required:!0},total:{type:"number"},label:{type:"string"},style:{type:"enum",values:["linear","circular"]},trackColor:{type:"color"},thickness:{type:"number"}}},Ring:{documentation:"Circular progress ring.",children:!1,properties:{value:{type:"number",required:!0},thickness:{type:"number"},trackColor:{type:"color"}}},Badge:{documentation:"Compact status badge.",children:!1,properties:{text:{type:"string"},radius:{type:"number"}}},Chip:{documentation:"Outlined metadata chip.",children:!1,properties:{text:{type:"string"},radius:{type:"number"},borderColor:{type:"color"}}},Stat:{documentation:"Title, value, and subtitle statistic.",children:!1,properties:{title:{type:"string"},value:{type:"string",required:!0},subtitle:{type:"string"},mutedColor:{type:"color"}}},Button:{documentation:"Interactive widget button. Use actionID to share a declared Package 2.0 action with Siri, Shortcuts, and Control Widgets.",children:!0,availability:{interactiveIos:"17.0"},requiredAny:["action","onClick","actionID"],properties:{action:{type:"enum",values:["reload"],conflicts:["actionID"]},onClick:{type:"function",conflicts:["actionID"]},actionID:{type:"action-id",conflicts:["action","onClick"]}}},Toggle:{documentation:"Interactive widget toggle. A declared actionID requires a package-relative storage stateKey.",children:!0,availability:{interactiveIos:"17.0"},requiredAny:["onClick","actionID"],properties:{on:{type:"boolean",required:!0},onClick:{type:"function",conflicts:["actionID"]},actionID:{type:"action-id",requires:["stateKey"],conflicts:["onClick"]},stateKey:{type:"storage-key",requires:["actionID"]}}}},functions:{...Object.fromEntries(O1.map(n=>[n.id,n])),...Object.fromEntries(A1.map(n=>[n.id,{documentation:n.notes,arguments:n.arguments,returns:n.asynchronous?"Promise":typeof n.value=="object"?"json":typeof n.value}])),"$icloud.readJSON":{documentation:"Read cached JSON and revision; conflicts preserve all heads. Requires icloud permission and a native connection. Never certifies upload.",arguments:["name"],returns:"Promise",minimumRuntimeVersion:"1.5"},"$icloud.writeJSON":{documentation:"Save a new immutable JSON revision locally from a user action. Requires the revision from readJSON. Background exchange is eventual.",arguments:["name","value","expectedRevision"],returns:"Promise",minimumRuntimeVersion:"1.5"},"$icloud.resolveJSON":{documentation:"Explicitly resolve all observed conflicting heads into a new revision. Stale tokens fail; originals remain.",arguments:["name","value","expectedRevision"],returns:"Promise",minimumRuntimeVersion:"1.5"},"$icloud.sync":{documentation:"Exchange materialized revisions with iCloud; requires a user action. Upload completion is unknown.",arguments:[],returns:"Promise",minimumRuntimeVersion:"1.5"},"$icloud.status":{documentation:"Read native connection state. Cloud store IDs are granted in the app, never by the script. Web uses preview mocks.",arguments:[],returns:"Promise",minimumRuntimeVersion:"1.5"},"$file.exists":{documentation:"Return whether a regular file is locally present inside this package, including binary assets. Missing files, directories, unsafe paths and links return false. Does not download iCloud files or read outside the package. Web preview uses workspace file presence.",arguments:["path"],returns:"boolean",minimumRuntimeVersion:"1.3"},"$file.read":{documentation:"Read package-relative UTF-8 text. Missing or unavailable files return an empty string.",arguments:["path"],returns:"string"},"$file.readJSON":{documentation:"Read a package-relative JSON object.",arguments:["path"],returns:"json"},fetch:{documentation:"Perform a network request and return its response text. Existing HTTP methods keep this string result."},"$http.getResponse":{documentation:"GET response metadata: {status, body, headers}. Headers are limited to content-type, cache-control, expires, last-modified, date and retry-after (lowercase; each at most 2048 UTF-8 bytes). Network permissions, public-host policy, redirects, body limits and timeline restrictions still apply. HTTP error statuses resolve; transport errors reject.",arguments:["url","options?"],returns:"Promise",minimumRuntimeVersion:"1.3"},importJS:{documentation:"Import a package-relative JavaScript file."},"$dataSource.request":{documentation:"Call a declared Data Source Plugin operation. Requires Package 2.0 plugins, network permission, and networkDomains declarations.",arguments:["pluginID","operationID","parameters"],returns:"Promise"},"$xml.parse":{documentation:"Parse up to 256 KiB of XML into {name, attributes, text, children}. DTDs and external entities are rejected; depth and node limits apply.",arguments:["source"],returns:"XMLNode"}}};function b1(n,e){const t=Object.entries(e.properties??{}).filter(([,r])=>r.required).map(([r,s])=>`${r}=${s.type.includes("number")||s.type==="boolean"?"{${}}":'"${}"'}`).join(" "),i=t?` ${t}`:"";return e.children?`<${n}${i}> \${} `:`<${n}${i} />`}const lh=Object.entries(Re.components).map(([n,e])=>({label:n,type:"class",detail:`ScriptWidget · ${Object.keys(e.properties??{}).length} properties`,info:e.documentation,apply:b1(n,e)}));function S1(n){const e=n.match(/<([A-Za-z][\w]*)\b([^<>]*)$/);return!e||e[2].includes(">")?null:{name:e[1],attributes:e[2]}}function x1(n,e){const t=Re.components[n];return t?Object.entries({...Re.globalProperties,...t.properties??{}}).filter(([i])=>!e.has(i)).map(([i,r])=>({label:i,type:"property",detail:r.required?`${r.type} · required`:r.type,info:r.deprecated?`Deprecated: ${r.deprecated}`:r.documentation,apply:`${i}=${r.type.includes("number")||r.type==="boolean"?"{${}}":'"${}"'}`})):[]}function w1(n){const e=n.match(/<([A-Za-z][\w]*)\b[^<>]*\b([A-Za-z][\w]*)\s*=\s*["']([^"']*)$/);if(!e)return null;const t=Re.components[e[1]]?.properties?.[e[2]]??Re.globalProperties[e[2]];return t?.values?{typed:e[3],values:t.values}:null}function v1(n){const e=n.state.doc.sliceString(Math.max(0,n.pos-1200),n.pos),t=w1(e);if(t)return{from:n.pos-t.typed.length,options:t.values.map(o=>({label:o,type:"enum"})),validFor:/^[\w-]*$/};const i=n.matchBefore(/<[A-Za-z]*$/);if(i)return{from:i.from+1,options:lh,validFor:/^[A-Za-z]*$/};const r=S1(e);if(r&&/(?:\s|^)\w*$/.test(r.attributes)){const o=n.matchBefore(/[A-Za-z][\w]*$/),l=new Set([...r.attributes.matchAll(/\b([A-Za-z][\w]*)\s*=/g)].map(a=>a[1]));return{from:o?.from??n.pos,options:x1(r.name,l),validFor:/^[A-Za-z][\w]*$/}}const s=n.matchBefore(/\$[A-Za-z]+\.[A-Za-z]*$/);if(s){const o=s.text.slice(0,s.text.indexOf(".")+1),l=Object.entries(Re.functions).filter(([a])=>a.startsWith(o));return l.length?{from:s.from+o.length,options:l.map(([a,c])=>({label:a.slice(o.length),type:"function",detail:c.returns,info:c.documentation})),validFor:/^[A-Za-z]*$/}:null}return n.explicit?{from:n.pos,options:lh,validFor:/^[A-Za-z]*$/}:null}function ah(n,e){const t=[];for(let i=n.node.firstChild;i;i=i.nextSibling)i.name===e&&t.push(i);return t}function Md(n){const e=[];return Ce(n).iterate({enter(t){if(t.name!=="JSXOpenTag"&&t.name!=="JSXSelfClosingTag")return;const r=ah(t,"JSXIdentifier")[0];r&&e.push({name:n.doc.sliceString(r.from,r.to),from:r.from,to:r.to,attributes:ah(t,"JSXAttribute").map(s=>{const o=s.firstChild;return o?{name:n.doc.sliceString(o.from,o.to),from:o.from,to:o.to}:null}).filter(Boolean)})}}),e}function Ed(n){const e=[];for(const t of Md(n.state)){const i=Re.components[t.name];if(!i)continue;const r=new Set([...Object.keys(Re.globalProperties),...Object.keys(i.properties??{})]),s=new Set(t.attributes.map(o=>o.name));for(const o of t.attributes){r.has(o.name)||e.push({from:o.from,to:o.to,severity:"warning",message:`“${o.name}” is not supported by <${t.name}>.`});const l=i.properties?.[o.name]??Re.globalProperties[o.name];l?.deprecated&&e.push({from:o.from,to:o.to,severity:"warning",message:`${o.name} is deprecated. ${l.deprecated}`})}for(const[o,l]of Object.entries(i.properties??{}))if(l.required&&!s.has(o)&&e.push({from:t.from,to:t.to,severity:"error",message:`<${t.name}> requires the “${o}” property.`}),s.has(o)){for(const a of l.requires??[])s.has(a)||e.push({from:t.from,to:t.to,severity:"error",message:`<${t.name}> “${o}” requires the “${a}” property.`});for(const a of l.conflicts??[])s.has(a)&&o cannot combine “${o}” with “${a}”.`})}i.requiredAny?.length&&!i.requiredAny.some(o=>s.has(o))&&e.push({from:t.from,to:t.to,severity:"error",message:`<${t.name}> requires one of: ${i.requiredAny.map(o=>`“${o}”`).join(", ")}.`})}return e}function k1(n,e){const t=Md(n.state).find(r=>e>=r.from&&e<=r.to);if(!t)return null;const i=Re.components[t.name];return i?{pos:t.from,end:t.to,above:!0,create(){const r=document.createElement("div");r.className="studio-hover";const s=document.createElement("strong");s.textContent=`<${t.name}>`;const o=document.createElement("p");return o.textContent=i.documentation,r.append(s,o),{dom:r}}}:null}const ch={light:{background:"#FAFAFC",foreground:"#202124",gutter:"#F3F3F6",gutterText:"#8A8A93",activeLine:"#EDEEF3",selection:"#B9D7FF",caret:"#0067C0"},dark:{background:"#1E1F22",foreground:"#E8E8ED",gutter:"#191A1D",gutterText:"#777982",activeLine:"#292A2F",selection:"#315A85",caret:"#65B7FF"}};function Q1(n,e){return R.theme({"&":{height:"100%",color:n.foreground,backgroundColor:n.background,fontSize:"13px"},".cm-scroller":{fontFamily:'ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, monospace',lineHeight:"1.58",overscrollBehavior:"contain"},".cm-content":{caretColor:n.caret,padding:"12px 0 32px"},".cm-line":{padding:"0 8px"},".cm-cursor, .cm-dropCursor":{borderLeftColor:n.caret},"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection":{backgroundColor:n.selection},".cm-activeLine":{backgroundColor:n.activeLine},".cm-gutters":{backgroundColor:n.gutter,color:n.gutterText,border:"none",paddingLeft:"env(safe-area-inset-left)"},".cm-activeLineGutter":{backgroundColor:n.activeLine,color:n.foreground},".cm-foldPlaceholder":{backgroundColor:"transparent",border:`1px solid ${n.gutterText}`,color:n.gutterText},".cm-tooltip":{border:e?"1px solid #41434A":"1px solid #D2D3D8",backgroundColor:e?"#292A2F":"#FFFFFF",borderRadius:"8px",overflow:"hidden",boxShadow:"0 8px 28px rgba(0, 0, 0, 0.2)"},".cm-tooltip-autocomplete ul li[aria-selected]":{backgroundColor:e?"#315A85":"#DCEBFA",color:n.foreground},".cm-panels":{backgroundColor:n.gutter,color:n.foreground},".cm-searchMatch":{backgroundColor:e?"#725C1D":"#FFE69A"},".cm-searchMatch.cm-searchMatch-selected":{backgroundColor:e?"#8D6D20":"#FFD45C"}},{dark:e})}const P1=Ln.define([{tag:[g.keyword,g.controlKeyword,g.moduleKeyword,g.operatorKeyword,g.bool,g.null],color:"#FF7AB2"},{tag:[g.string,g.special(g.string)],color:"#FF8170"},{tag:[g.number,g.regexp],color:"#D9C97C"},{tag:[g.comment,g.lineComment,g.blockComment],color:"#7F8C98",fontStyle:"italic"},{tag:[g.tagName,g.typeName,g.className],color:"#5DD8FF"},{tag:g.attributeName,color:"#B281EB"},{tag:[g.propertyName,g.function(g.variableName),g.function(g.propertyName)],color:"#67B7A4"},{tag:g.definition(g.variableName),color:"#4EB0CC"},{tag:[g.variableName,g.operator,g.punctuation,g.bracket],color:"#E8E8ED"},{tag:g.invalid,color:"#FF453A"}]);function Ud(n){const e=n==="dark";return[Q1(e?ch.dark:ch.light,e),Jg(e?P1:i0)]}const C1=Lt.parser.configure({dialect:"jsx"}),T1=new Map(Object.keys(Re.components).map(n=>[n.toLowerCase(),n])),$1=new Set(["VStack","HStack","ZStack","VGrid","HGrid"]),R1=512*1024,K1=512,Z1=32,ci=n=>{const e=[];for(let t=n.firstChild;t;t=t.nextSibling)e.push(t);return e},hi=(n,e)=>n.slice(e.from,e.to),de=n=>{throw new Error(n)},Fd=n=>{let e=!0;const t=n.replace(/&([^;\s]+);/g,(i,r)=>{const s={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:" "};if(Object.hasOwn(s,r))return s[r];if(/^#(?:[0-9]+|x[0-9a-f]+)$/i.test(r)){const o=r[1].toLowerCase()==="x"?parseInt(r.slice(2),16):Number(r.slice(1));if(o>0&&o<=1114111&&!(o>=55296&&o<=57343))return String.fromCodePoint(o)}return e=!1,i});return e?t:void 0};function co(n,e=!1){if(e)return Fd(n.slice(1,-1));const t=n.trim();if(/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(t)){const i=Number(t);return Number.isFinite(i)?i:void 0}if(t==="true"||t==="false")return t==="true";if(t.startsWith('"'))try{const i=JSON.parse(t);return typeof i=="string"?i:void 0}catch{}}function wi(n){const e={formatVersion:1,source:n,nodes:[],roots:[],issue:""};if(typeof n!="string"||new TextEncoder().encode(n).length>R1)return{...e,issue:"Layout editing supports source files up to 512 KiB."};const t=C1.parse(n);let i=!1;if(t.iterate({enter:s=>{s.type.isError&&(i=!0)}}),i)return{...e,issue:"Fix the JavaScript/JSX syntax before using Layout. Your source is preserved."};function r(s,o,l){(l>Z1||e.nodes.length>=K1)&&de("Layout editing supports up to 512 layers and 32 nesting levels.");const a=ci(s),c=a[0],h=a.at(-1),u=ci(c).find(S=>["JSXBuiltin","JSXIdentifier","JSXNamespacedName","JSXMemberExpression"].includes(S.name)),f=u?hi(n,u):"Fragment",d=T1.get(f.toLowerCase()),p=Re.components[d],m={...Re.globalProperties,...p?.properties??{}},O=c.name==="JSXOpenTag"&&h?.name==="JSXCloseTag",y=O?ci(h).find(S=>["JSXBuiltin","JSXIdentifier","JSXNamespacedName","JSXMemberExpression"].includes(S.name)):null;u&&O&&(!y||hi(n,y)!==f)&&de("Opening and closing JSX tags must match before using Layout.");const b={id:String(s.from),parentID:o,tag:f,component:d??null,from:s.from,to:s.to,openingEnd:c.to,insertAt:c.to-(O?1:2),contentFrom:c.to,contentTo:O?h.from:c.to,children:[],properties:[],locked:!p,structure:!1,text:null,textEditable:!1,depth:l};let x=!1;for(const S of ci(c)){if(S.name==="JSXSpreadAttribute"&&(x=!0),S.name!=="JSXAttribute")continue;const v=ci(S),U=hi(n,v[0]),F=v[2],j=F?co(hi(n,F),F.name==="JSXAttributeValue"):!0,E=F?.name==="JSXEscape"?co(n.slice(F.from+1,F.to-1)):j,M=Object.hasOwn(m,U)?m[U]:null;b.properties.push({name:U,value:E??null,from:S.from,to:S.to,editable:!!(p&&M&&M.type!=="function"&&E!==void 0),definition:M??null})}const C=new Set(b.properties.map(S=>S.name)).size!==b.properties.length;b.locked||=x||C,b.reason=x?"Spread properties are preserved; edit this element in Code.":C?"Duplicate properties are preserved; resolve them in Code.":p?"":"Custom components and fragments are preserved; edit them in Code.",b.locked&&b.properties.forEach(S=>{S.editable=!1}),e.nodes.push(b);const k=O?a.slice(1,-1):[];for(const S of k)S.name==="JSXElement"&&b.children.push(r(S,b.id,l+1).id);if(b.structure=!b.locked&&O&&$1.has(d)&&k.every(S=>S.name==="JSXElement"||S.name==="JSXText"&&!hi(n,S).trim())&&b.children.every(S=>!e.nodes.find(v=>v.id===S).locked),!b.locked&&d==="Text"&&O&&k.length===1){const S=k[0],v=hi(n,S),U=S.name==="JSXText"&&!/[\r\n]/.test(v)?Fd(v):S.name==="JSXEscape"?co(v.slice(1,-1)):void 0;typeof U=="string"&&(b.text=U,b.textEditable=!0)}return b}try{t.iterate({enter(s){if(s.name!=="CallExpression")return;const o=ci(s.node);if(o[0]?.name!=="VariableName"||hi(n,o[0])!=="$render"||o[1]?.name!=="ArgList")return;const l=ci(o[1]).filter(a=>!["(",")"].includes(a.name));if(l.length===1&&l[0].name==="JSXElement")return e.roots.push(r(l[0],null,0).id),!1}})}catch(s){return{...e,nodes:[],roots:[],issue:s.message}}return e.roots.length||(e.issue="Layout needs a direct $render(…) or another native JSX element. Variables, loops and functions stay editable in Code."),e}function hl(n){return!n?.component||n.locked?{}:{...Re.globalProperties,...Re.components[n.component]?.properties??{}}}function M1(n,e,t){(n.source!==e||n.issue)&&de("Layout changed. Select the layer again before editing.");const i=n.nodes.find(a=>a.id===t.id);(!i||i.locked)&&de("This layer is locked. Edit its source in Code.");let r=[],s=i.from;if(t.type==="property"){const a=hl(i),c=Object.hasOwn(a,t.name)?a[t.name]:null,h=i.properties.find(d=>d.name===t.name);(!c||c.type==="function"||h&&!h.editable)&&de("Dynamic or unsupported properties must be edited in Code.");const u=new Set(i.properties.map(d=>d.name)),f=Re.components[i.component];if(t.remove?(f.requiredAny?.includes(t.name)&&!f.requiredAny.some(d=>d!==t.name&&u.has(d))&&de("Keep at least one required component property."),Object.entries(f.properties??{}).some(([d,p])=>u.has(d)&&p.requires?.includes(t.name))&&de("Another property requires this value. Edit the dependent properties together in Code.")):(c.requires?.some(d=>!u.has(d))&&de("Add the required related properties together in Code."),(c.conflicts?.some(d=>u.has(d))||Object.entries(f.properties??{}).some(([d,p])=>d!==t.name&&u.has(d)&&p.conflicts?.includes(t.name)))&&de("This property conflicts with another component property.")),t.remove)(!h||c.required)&&de("This property cannot be removed."),r=[{from:h.from,to:h.to,insert:""}];else{const d=t.value;(!["string","number","boolean"].includes(typeof d)||typeof d=="number"&&!Number.isFinite(d)||typeof d=="string"&&d.length>8192)&&de("Use a bounded string, finite number or Boolean."),c.values&&!c.values.includes(d)&&de("Choose one of the supported property values."),c.type==="boolean"&&typeof d!="boolean"&&de("This property needs true or false."),c.type==="number"&&!(typeof d=="number"||typeof d=="string"&&d.trim()&&Number.isFinite(Number(d)))&&de("This property needs a number."),typeof d=="boolean"&&c.type!=="boolean"&&de("This property does not accept a Boolean."),typeof d=="number"&&!c.type.includes("number")&&de("This property needs a string value."),t.name==="opacity"&&!(Number(d)>=0&&Number(d)<=1)&&de("Opacity must be between zero and one.");const p=`${t.name}={${JSON.stringify(d)}}`;r=h?[{from:h.from,to:h.to,insert:p}]:[{from:i.insertAt,to:i.insertAt,insert:` ${p}`}]}}else if(t.type==="text")(!i.textEditable||typeof t.value!="string"||t.value.length>8192)&&de("Dynamic text is preserved. Edit it in Code."),r=[{from:i.contentFrom,to:i.contentTo,insert:`{${JSON.stringify(t.value)}}`}];else if(t.type==="move"||t.type==="remove")if(n.nodes.find(c=>c.id===i.parentID)?.structure||de("Only literal children of a supported stack/grid can be moved or removed."),t.type==="remove")r=[{from:i.from,to:i.to,insert:""}];else{const c=n.nodes.find(p=>p.id===t.parentID);(!c?.structure||c.from>=i.from&&c.to<=i.to)&&de("Choose a supported container outside this layer.");const h=p=>{for(;p.parentID!==null;)p=n.nodes.find(m=>m.id===p.parentID);return p.id};h(i)!==h(c)&&de("Layers cannot move between render branches or JavaScript scopes.");const u=c.children.filter(p=>p!==i.id),f=t.index;(!Number.isInteger(f)||f<0||f>u.length)&&de("Invalid layer position.");const d=f===u.length?c.contentTo:n.nodes.find(p=>p.id===u[f]).from;if(d===i.from||d===i.to)return{changes:[],selectedFrom:s};r=[{from:i.from,to:i.to,insert:""},{from:d,to:d,insert:e.slice(i.from,i.to)}],s=d-(d>i.to?i.to-i.from:0)}else if(t.type==="add"){i.structure||de("Choose a supported container with literal JSX children.");const a={text:'New text',spacer:"",stack:'',icon:''};Object.hasOwn(a,t.kind)||de("Unsupported new layer."),r=[{from:i.contentTo,to:i.contentTo,insert:a[t.kind]}],s=i.contentTo}else de("Unsupported layout operation.");r.sort((a,c)=>a.from-c.from);let o=e;for(const a of[...r].reverse())o=o.slice(0,a.from)+a.insert+o.slice(a.to);return wi(o).issue&&de("This edit would leave the supported layout. Your source was not changed."),{changes:r,selectedFrom:s}}const Dd=Lt.parser.configure({dialect:"jsx"}),Ci=n=>{const e=[];for(let t=n.firstChild;t;t=t.nextSibling)e.push(t);return e},Qe=n=>{throw new Error(n)},Gi=Object.freeze({background:{type:"color"},surface:{type:"color"},foreground:{type:"color"},muted:{type:"color"},accent:{type:"color"},titleFont:{values:Re.globalProperties.font.values},bodyFont:{values:Re.globalProperties.font.values},fontDesign:{values:Re.globalProperties.fontDesign.values},corner:{type:"number",maximum:48},padding:{type:"number",maximum:48},spacing:{type:"number",maximum:32}}),mr={titleFont:"headline",bodyFont:"caption",fontDesign:"rounded",corner:20,padding:16,spacing:10},rs=Object.freeze({Meadow:Object.freeze({...mr,background:"#F2FAF5",surface:"#FFFFFF",foreground:"#193F31",muted:"#607C70",accent:"#328963"}),Paper:Object.freeze({...mr,background:"#F4F7FD",surface:"#FFFFFF",foreground:"#243454",muted:"#687795",accent:"#4777D6"}),Peach:Object.freeze({...mr,background:"#FFF5EE",surface:"#FFFFFF",foreground:"#56392C",muted:"#967461",accent:"#C86B43"}),Midnight:Object.freeze({...mr,background:"#192332",surface:"#283548",foreground:"#F1F5FC",muted:"#AABAD0",accent:"#8EB7FF"})});function Xd(n,e){Object.hasOwn(Gi,n)||Qe("Unknown theme token.");const t=Gi[n];t.type==="color"&&!(typeof e=="string"&&/^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/i.test(e))&&Qe(`${n} needs a six or eight digit hex color.`),t.values&&!t.values.includes(e)&&Qe(`${n} needs a supported font value.`),t.type==="number"&&!(typeof e=="number"&&Number.isFinite(e)&&e>=0&&e<=t.maximum)&&Qe(`${n} needs a number from zero to ${t.maximum}.`)}function ea(n){(!n||typeof n!="object"||Array.isArray(n)||Object.keys(n).length!==Object.keys(Gi).length)&&Qe("A theme needs exactly the documented tokens.");for(const e of Object.keys(n))Xd(e,n[e]);return n}function Ld(n){(typeof n!="string"||new TextEncoder().encode(n).length>4096)&&Qe("Theme JSON is limited to 4 KiB.");const e=JSON.parse(n);return(!e||Array.isArray(e)||Object.keys(e).sort().join()!=="formatVersion,name,tokens"||e.formatVersion!==1||typeof e.name!="string"||!e.name.trim()||/[\u0000-\u001f]/.test(e.name)||new TextEncoder().encode(e.name).length>64)&&Qe("Use a version 1 theme with a name of at most 64 bytes."),ea(e.tokens),e}function E1(n,e="My theme"){const t=JSON.stringify({formatVersion:1,name:e,tokens:n},null,2);return Ld(t),t}function ss(n){const e={source:n,tokens:null,ranges:{},issue:"",canInitialize:!1};try{(typeof n!="string"||new TextEncoder().encode(n).length>512*1024)&&Qe("Theme editing supports files up to 512 KiB.");const t=Dd.parse(n),i=[],r=[];if(t.iterate({enter(a){a.type.isError&&Qe("Fix JavaScript syntax before editing the theme."),["VariableDefinition","VariableName"].includes(a.name)&&n.slice(a.from,a.to).includes("\\")&&Qe("Escaped identifiers stay in Code."),n.slice(a.from,a.to)==="widgetTheme"&&["VariableDefinition","VariableName"].includes(a.name)&&(a.name==="VariableDefinition"?i:r).push(a.node)}}),!i.length&&!r.length){const a=wi(n);e.canInitialize=!a.issue&&a.roots.length===1&&!a.nodes[0].locked;let c=!1;for(const h of Ci(t.topNode)){const u=h.firstChild;a.nodes[0]&&h.name==="ExpressionStatement"&&u?.name==="CallExpression"&&n.slice(u.firstChild.from,u.firstChild.to)==="$render"&&u.froma.nodes[0].to&&(c=!0)}return e.canInitialize&&=c,e}i.length!==1&&Qe("Keep one top-level const widgetTheme object. Other bindings stay in Code.");const s=i[0].parent,o=Ci(s);(s.name!=="VariableDeclaration"||s.parent?.name!=="Script"||o[0]?.name!=="const"||o[1]?.from!==i[0].from||o[2]?.name!=="Equals"||o[3]?.name!=="ObjectExpression"||o.slice(4).some(a=>a.name!==";"))&&Qe("Theme editing needs a single top-level const widgetTheme = { … } declaration.");const l={};for(const a of Ci(o[3])){if(["{","}",","].includes(a.name))continue;const c=Ci(a),h=c[0]?n.slice(c[0].from,c[0].to):"",u=c[0]?.name==="String"&&h.startsWith('"')?JSON.parse(h):h;(a.name!=="Property"||c.length!==3||!["PropertyDefinition","String"].includes(c[0].name)||c[1].name!==":"||!["String","Number"].includes(c[2].name)||Object.hasOwn(l,u))&&Qe("Theme tokens must be unique literal properties. Computed or dynamic values stay in Code.");let f;try{f=JSON.parse(n.slice(c[2].from,c[2].to))}catch{Qe("Theme values use JSON strings or numbers. Edit this declaration in Code.")}Xd(u,f),l[u]=f,e.ranges[u]={from:c[2].from,to:c[2].to}}ea(l);for(const a of r){const c=a.parent,h=Ci(c),u=h[2]?n.slice(h[2].from,h[2].to):"";(c.name!=="MemberExpression"||h[0].from!==a.from||h[1]?.name!=="."||h[2]?.name!=="PropertyName"||!Object.hasOwn(Gi,u))&&Qe("Theme references must read widgetTheme.token. Other uses stay in Code."),c.parent?.name==="MemberExpression"&&Qe("Mutable theme references stay in Code.");for(let f=c.parent;f;f=f.parent)(["UpdateExpression","PostfixExpression"].includes(f.name)||f.name==="UnaryExpression"&&/^(?:delete|\+\+|--)/.test(n.slice(f.from,c.from).trim())||f.name==="AssignmentExpression"&&f.firstChild.from<=c.from&&f.firstChild.to>=c.to||f.name==="ForOfSpec"||f.name==="ForInSpec")&&Qe("Mutable theme references stay in Code.")}e.tokens=l}catch(t){e.issue=t.message}return e}function ho(n,e,t){return(n.source!==e||n.issue||!n.tokens)&&Qe("Theme changed or is unsupported. Reopen Theme before editing."),ea(t),{changes:Object.keys(Gi).filter(r=>t[r]!==n.tokens[r]).map(r=>({...n.ranges[r],insert:JSON.stringify(t[r])})).sort((r,s)=>r.from-s.from)}}function U1(n,e="Meadow"){(!ss(n).canInitialize||!Object.hasOwn(rs,e))&&Qe("Start with a literal top-level layout without an existing widgetTheme binding.");const i=wi(n),r=i.nodes[0],s=[];function o(c,h,u,f=!1){const d=c.properties.find(m=>m.name===h);if(c.locked||d&&!d.editable||!d&&!f)return;const p=`${h}={widgetTheme.${u}}`;s.push(d?{from:d.from,to:d.to,insert:p}:{from:c.insertAt,to:c.insertAt,insert:` ${p}`})}for(const c of["background","corner","padding"])o(r,c,c,!0);r.structure&&o(r,"spacing","spacing",!0);for(const c of i.nodes)if(c.component==="Text"){const h=c.properties.find(f=>f.name==="font")?.value,u=["headline","largeTitle","title","title2","title3"].includes(h);o(c,"font",u?"titleFont":"bodyFont",!0),o(c,"color",u?"foreground":"muted",!0),o(c,"fontDesign","fontDesign",!0)}else c.component==="Icon"&&o(c,"color","accent",!0);let l=0;for(const c of Ci(Dd.parse(n).topNode))if(!(c.name==="LineComment"||c.name==="BlockComment"))if(c.name==="ExpressionStatement"&&c.firstChild?.name==="String")l=c.to;else break;s.push({from:l,to:l,insert:` const widgetTheme = ${JSON.stringify(rs[e],null,2)}; diff --git a/iOS/ScriptWidget/View/CodeEditor/MirrorEditor/StudioEditor.bundle/index.html b/iOS/ScriptWidget/View/CodeEditor/MirrorEditor/StudioEditor.bundle/index.html index af995b5a..f08a8fe2 100644 --- a/iOS/ScriptWidget/View/CodeEditor/MirrorEditor/StudioEditor.bundle/index.html +++ b/iOS/ScriptWidget/View/CodeEditor/MirrorEditor/StudioEditor.bundle/index.html @@ -5,7 +5,7 @@ ScriptWidget Web Studio - + diff --git a/macOS/ScriptWidgetMac.xcodeproj/project.pbxproj b/macOS/ScriptWidgetMac.xcodeproj/project.pbxproj index c9dda1fc..05bc0971 100644 --- a/macOS/ScriptWidgetMac.xcodeproj/project.pbxproj +++ b/macOS/ScriptWidgetMac.xcodeproj/project.pbxproj @@ -174,7 +174,9 @@ D1995CE429536BB800D1BD94 /* ScriptWidgetRuntimeFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1995C8A29536BB800D1BD94 /* ScriptWidgetRuntimeFetch.swift */; }; D1995CE529536BB800D1BD94 /* ScriptWidgetRuntimeFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1995C8A29536BB800D1BD94 /* ScriptWidgetRuntimeFetch.swift */; }; D1995CE629536BB800D1BD94 /* ScriptWidgetRuntimeFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1995C8B29536BB800D1BD94 /* ScriptWidgetRuntimeFile.swift */; }; + 2848A73946D449769C0CD55F /* ScriptWidgetRuntimeCloudJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F6759273EF4F709A101EA6 /* ScriptWidgetRuntimeCloudJSON.swift */; }; D1995CE729536BB800D1BD94 /* ScriptWidgetRuntimeFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1995C8B29536BB800D1BD94 /* ScriptWidgetRuntimeFile.swift */; }; + 005E0FFB61C747FB9D148955 /* ScriptWidgetRuntimeCloudJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F6759273EF4F709A101EA6 /* ScriptWidgetRuntimeCloudJSON.swift */; }; D1995CE829536BB800D1BD94 /* ScriptWidgetRuntimePromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1995C8C29536BB800D1BD94 /* ScriptWidgetRuntimePromise.swift */; }; D1995CE929536BB800D1BD94 /* ScriptWidgetRuntimePromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1995C8C29536BB800D1BD94 /* ScriptWidgetRuntimePromise.swift */; }; D1995CEA29536BB800D1BD94 /* ScriptWidgetRuntimeConsole.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1995C8D29536BB800D1BD94 /* ScriptWidgetRuntimeConsole.swift */; }; @@ -262,9 +264,12 @@ F2D2B24EA420469CBA72564F /* ReloadWidgetAppIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8445DB90E4A34EB19697BAEB /* ReloadWidgetAppIntent.swift */; }; F2F90FC427970E3E003095A9 /* CreateGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F90FC327970E3E003095A9 /* CreateGuideView.swift */; }; CB6E39FE3BE94BCCAFF9923E /* ScriptWidgetRecordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D98FE968018D484D8FBCDC17 /* ScriptWidgetRecordStore.swift */; }; + F7F9A50928F64529981556FE /* ScriptWidgetCloudJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9469B308A04CF7BC84E549 /* ScriptWidgetCloudJSON.swift */; }; 1A12140C0C8248BDB21EF14F /* ScriptWidgetRecordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10538215ECFD4FADA216F872 /* ScriptWidgetRecordsView.swift */; }; + 466E709555B74AAD834094B4 /* ScriptWidgetCloudJSONView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF5C35C0CEF344629F7D4B6B /* ScriptWidgetCloudJSONView.swift */; }; 2F12113B8920446580D6B39E /* ScriptWidgetStorageUpgradeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF563C68CB1F4834876CF479 /* ScriptWidgetStorageUpgradeView.swift */; }; 36FDE008D662491489635EA7 /* ScriptWidgetRecordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D98FE968018D484D8FBCDC17 /* ScriptWidgetRecordStore.swift */; }; + 3D87692664A44C799D7182CA /* ScriptWidgetCloudJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9469B308A04CF7BC84E549 /* ScriptWidgetCloudJSON.swift */; }; B75FFB025CF34E8FB05FCA34 /* ScriptWidgetStorageNamespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9D4AE7B65A246F08085C382 /* ScriptWidgetStorageNamespaces.swift */; }; 07B9317E06EA447491ED65B1 /* ScriptWidgetStorageFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64423A310BE1421199E81550 /* ScriptWidgetStorageFiles.swift */; }; D2A014501E254E0B81DE3563 /* ScriptWidgetStorageNamespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9D4AE7B65A246F08085C382 /* ScriptWidgetStorageNamespaces.swift */; }; @@ -408,6 +413,7 @@ D1995C8829536BB800D1BD94 /* ScriptWidgetElementTagGrid.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScriptWidgetElementTagGrid.swift; sourceTree = ""; }; D1995C8A29536BB800D1BD94 /* ScriptWidgetRuntimeFetch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeFetch.swift; sourceTree = ""; }; D1995C8B29536BB800D1BD94 /* ScriptWidgetRuntimeFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeFile.swift; sourceTree = ""; }; + 06F6759273EF4F709A101EA6 /* ScriptWidgetRuntimeCloudJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeCloudJSON.swift; sourceTree = ""; }; D1995C8C29536BB800D1BD94 /* ScriptWidgetRuntimePromise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimePromise.swift; sourceTree = ""; }; D1995C8D29536BB800D1BD94 /* ScriptWidgetRuntimeConsole.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeConsole.swift; sourceTree = ""; }; D1995C8E29536BB800D1BD94 /* ScriptWidgetRuntimeDevice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRuntimeDevice.swift; sourceTree = ""; }; @@ -475,7 +481,9 @@ F2F90FC327970E3E003095A9 /* CreateGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGuideView.swift; sourceTree = ""; }; F9B271354202D5108EB31CA9 /* ScriptWidgetRuntimeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScriptWidgetRuntimeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D98FE968018D484D8FBCDC17 /* ScriptWidgetRecordStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRecordStore.swift; sourceTree = ""; }; + 8A9469B308A04CF7BC84E549 /* ScriptWidgetCloudJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetCloudJSON.swift; sourceTree = ""; }; 10538215ECFD4FADA216F872 /* ScriptWidgetRecordsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetRecordsView.swift; sourceTree = ""; }; + FF5C35C0CEF344629F7D4B6B /* ScriptWidgetCloudJSONView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetCloudJSONView.swift; sourceTree = ""; }; FF563C68CB1F4834876CF479 /* ScriptWidgetStorageUpgradeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetStorageUpgradeView.swift; sourceTree = ""; }; B9D4AE7B65A246F08085C382 /* ScriptWidgetStorageNamespaces.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetStorageNamespaces.swift; sourceTree = ""; }; 64423A310BE1421199E81550 /* ScriptWidgetStorageFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptWidgetStorageFiles.swift; sourceTree = ""; }; @@ -795,6 +803,7 @@ children = ( D1995C8A29536BB800D1BD94 /* ScriptWidgetRuntimeFetch.swift */, D1995C8B29536BB800D1BD94 /* ScriptWidgetRuntimeFile.swift */, + 06F6759273EF4F709A101EA6 /* ScriptWidgetRuntimeCloudJSON.swift */, 8E3B593386F820DCF99C631E /* ScriptWidgetRuntimeSystem.swift */, CD9F1F298DF5C4FAED8AAD43 /* ScriptWidgetRuntimeHealth.swift */, 1F219B313B36BE844638FBC2 /* ScriptWidgetRuntimeCalendar.swift */, @@ -847,7 +856,9 @@ children = ( D1995CF529536BC700D1BD94 /* ScriptWidgetPackage.swift */, D98FE968018D484D8FBCDC17 /* ScriptWidgetRecordStore.swift */, + 8A9469B308A04CF7BC84E549 /* ScriptWidgetCloudJSON.swift */, 10538215ECFD4FADA216F872 /* ScriptWidgetRecordsView.swift */, + FF5C35C0CEF344629F7D4B6B /* ScriptWidgetCloudJSONView.swift */, FF563C68CB1F4834876CF479 /* ScriptWidgetStorageUpgradeView.swift */, D1995CF629536BC700D1BD94 /* ScriptModel.swift */, D1A0000000E0000000001000 /* ScriptMetadata.swift */, @@ -1379,6 +1390,7 @@ D1995CDC29536BB800D1BD94 /* ScriptWidgetElementTagShape.swift in Sources */, D1995CB829536BB800D1BD94 /* ScriptWidgetAttributeText.swift in Sources */, D1995CE629536BB800D1BD94 /* ScriptWidgetRuntimeFile.swift in Sources */, + 2848A73946D449769C0CD55F /* ScriptWidgetRuntimeCloudJSON.swift in Sources */, 2748BECB1506585B128418DC /* ScriptWidgetRuntimeSystem.swift in Sources */, 75F45276F84EA01B91EC0CEC /* ScriptWidgetRuntimeHealth.swift in Sources */, 404165AC81E77E3021CBD919 /* ScriptWidgetRuntimeCalendar.swift in Sources */, @@ -1399,7 +1411,9 @@ B75FFB025CF34E8FB05FCA34 /* ScriptWidgetStorageNamespaces.swift in Sources */, 07B9317E06EA447491ED65B1 /* ScriptWidgetStorageFiles.swift in Sources */, CB6E39FE3BE94BCCAFF9923E /* ScriptWidgetRecordStore.swift in Sources */, + F7F9A50928F64529981556FE /* ScriptWidgetCloudJSON.swift in Sources */, 1A12140C0C8248BDB21EF14F /* ScriptWidgetRecordsView.swift in Sources */, + 466E709555B74AAD834094B4 /* ScriptWidgetCloudJSONView.swift in Sources */, 2F12113B8920446580D6B39E /* ScriptWidgetStorageUpgradeView.swift in Sources */, 65BB9B8C3E605B9CDA324E9C /* ScriptWidgetElementTagExtras.swift in Sources */, F29118C02791DC5300B860B0 /* WidgetRowView.swift in Sources */, @@ -1449,6 +1463,7 @@ D1995CCD29536BB800D1BD94 /* ScriptWidgetAttributeDebugModeModifier.swift in Sources */, D1995CB929536BB800D1BD94 /* ScriptWidgetAttributeText.swift in Sources */, D1995CE729536BB800D1BD94 /* ScriptWidgetRuntimeFile.swift in Sources */, + 005E0FFB61C747FB9D148955 /* ScriptWidgetRuntimeCloudJSON.swift in Sources */, 855878C6168D59CE4C6CB13C /* ScriptWidgetRuntimeSystem.swift in Sources */, 1C24B164B4EBE88815490627 /* ScriptWidgetRuntimeHealth.swift in Sources */, 2E70438FD53BEA7361410166 /* ScriptWidgetRuntimeCalendar.swift in Sources */, @@ -1469,6 +1484,7 @@ D2A014501E254E0B81DE3563 /* ScriptWidgetStorageNamespaces.swift in Sources */, 9257A378DFB04A718B883187 /* ScriptWidgetStorageFiles.swift in Sources */, 36FDE008D662491489635EA7 /* ScriptWidgetRecordStore.swift in Sources */, + 3D87692664A44C799D7182CA /* ScriptWidgetCloudJSON.swift in Sources */, A990D65181507F3AA8EB7951 /* ScriptWidgetElementTagExtras.swift in Sources */, F05227C82AD1D7100014BE09 /* ButtonActionAppIntent.swift in Sources */, D1995CA529536BB800D1BD94 /* DeepLinkDefine.swift in Sources */, diff --git a/macOS/ScriptWidgetMac/Sidebar/SidebarView.swift b/macOS/ScriptWidgetMac/Sidebar/SidebarView.swift index 3c3eb919..be78dd68 100644 --- a/macOS/ScriptWidgetMac/Sidebar/SidebarView.swift +++ b/macOS/ScriptWidgetMac/Sidebar/SidebarView.swift @@ -23,6 +23,7 @@ struct SidebarView: View { @State private var widgetGuideShowingSheet = false @State private var searchText = "" @State private var selectedRecordItem: ScriptModel? + @State private var selectedCloudItem: ScriptModel? // rename @State private var renameCurrentName = "" @@ -37,6 +38,7 @@ struct SidebarView: View { var body: some View { content .frame(minWidth: 230, idealWidth: 260, maxWidth: 320) + .sheet(item: $selectedCloudItem) { item in ScriptWidgetCloudJSONView(package: item.package) } .sheet(item: $selectedRecordItem) { item in ScriptWidgetRecordsView(package: item.package) } .sheet(isPresented: $renameShowingSheet) { RenameConfirmView(currentName: $renameCurrentName, inputName: $renameInputName) @@ -141,6 +143,9 @@ struct SidebarView: View { .padding(.vertical, 2) } .contextMenu(menuItems: { + if item.package.readManifest()?.permissions.contains(.icloud) == true { + Button("iCloud Data", systemImage: "icloud") { selectedCloudItem = item } + } if (try? ScriptWidgetRecordKind.load(package: item.package)) != nil { Button("Records & Configuration", systemImage: "list.bullet.rectangle") { selectedRecordItem = item } } diff --git a/macOS/ScriptWidgetMac/StudioEditor.bundle/assets/index-BmJkN3Xp.js b/macOS/ScriptWidgetMac/StudioEditor.bundle/assets/index-BaJfekQk.js similarity index 99% rename from macOS/ScriptWidgetMac/StudioEditor.bundle/assets/index-BmJkN3Xp.js rename to macOS/ScriptWidgetMac/StudioEditor.bundle/assets/index-BaJfekQk.js index 3e5e71a6..0f976695 100644 --- a/macOS/ScriptWidgetMac/StudioEditor.bundle/assets/index-BmJkN3Xp.js +++ b/macOS/ScriptWidgetMac/StudioEditor.bundle/assets/index-BaJfekQk.js @@ -21,7 +21,7 @@ \${} } }`,{label:"class",detail:"definition",type:"keyword"}),Ye('import {${names}} from "${module}"\n${}',{label:"import",detail:"named",type:"keyword"}),Ye('import ${name} from "${module}"\n${}',{label:"import",detail:"default",type:"keyword"})],hb=od.concat([Ye("interface ${name} {\n ${}\n}",{label:"interface",detail:"definition",type:"keyword"}),Ye("type ${name} = ${type}",{label:"type",detail:"definition",type:"keyword"}),Ye("enum ${name} {\n ${}\n}",{label:"enum",detail:"definition",type:"keyword"})]),Gc=new dg,ld=new Set(["Script","Block","FunctionExpression","FunctionDeclaration","ArrowFunction","MethodDeclaration","ForStatement"]);function cn(n){return(e,t)=>{let i=e.node.getChild("VariableDefinition");return i&&t(i,n),!0}}const ub=["FunctionDeclaration"],fb={FunctionDeclaration:cn("function"),ClassDeclaration:cn("class"),ClassExpression:()=>!0,EnumDeclaration:cn("constant"),TypeAliasDeclaration:cn("type"),NamespaceDeclaration:cn("namespace"),VariableDefinition(n,e){n.matchContext(ub)||e(n,"variable")},TypeDefinition(n,e){e(n,"type")},__proto__:null};function ad(n,e){let t=Gc.get(e);if(t)return t;let i=[],r=!0;function s(o,l){let a=n.sliceString(o.from,o.to);i.push({label:a,type:l})}return e.cursor(be.IncludeAnonymous).iterate(o=>{if(r)r=!1;else if(o.name){let l=fb[o.name];if(l&&l(o,s)||ld.has(o.name))return!1}else if(o.to-o.from>8192){for(let l of ad(n,o.node))i.push(l);return!1}}),Gc.set(e,i),i}const Hc=/^[\w$\xa1-\uffff][\w$\d\xa1-\uffff]*$/,cd=["TemplateString","String","RegExp","LineComment","BlockComment","VariableDefinition","TypeDefinition","Label","PropertyDefinition","PropertyName","PrivatePropertyDefinition","PrivatePropertyName","JSXText","JSXAttributeValue","JSXOpenTag","JSXCloseTag","JSXSelfClosingTag",".","?."];function db(n){let e=Ce(n.state).resolveInner(n.pos,-1);if(cd.indexOf(e.name)>-1)return null;let t=e.name=="VariableName"||e.to-e.from<20&&Hc.test(n.state.sliceDoc(e.from,e.to));if(!t&&!n.explicit)return null;let i=[];for(let r=e;r;r=r.parent)ld.has(r.name)&&(i=i.concat(ad(n.state.doc,r)));return{options:i,from:t?e.from:n.pos,validFor:Hc}}const Lt=Lr.define({name:"javascript",parser:cb.configure({props:[ef.add({IfStatement:Ns({except:/^\s*({|else\b)/}),TryStatement:Ns({except:/^\s*({|catch\b|finally\b)/}),LabeledStatement:Mg,SwitchBody:n=>{let e=n.textAfter,t=/^\s*\}/.test(e),i=/^\s*(case|default)\b/.test(e);return n.baseIndent+(t?0:i?1:2)*n.unit},Block:Zg({closing:"}"}),ArrowFunction:n=>n.baseIndent+n.unit,"TemplateString BlockComment":()=>null,"Statement Property":Ns({except:/^\s*{/}),JSXElement(n){let e=/^\s*<\//.test(n.textAfter);return n.lineIndent(n.node.from)+(e?0:n.unit)},JSXEscape(n){let e=/\s*\}/.test(n.textAfter);return n.lineIndent(n.node.from)+(e?0:n.unit)},"JSXOpenTag JSXSelfClosingTag"(n){return n.column(n.node.from)+n.unit}}),rf.add({"Block ClassBody SwitchBody EnumBody ObjectExpression ArrayExpression ObjectType":Dg,BlockComment(n){return{from:n.from+2,to:n.to-2}},JSXElement(n){let e=n.firstChild;if(!e||e.name=="JSXSelfClosingTag")return null;let t=n.lastChild;return{from:e.to,to:t.type.isError?n.to:t.from}},"JSXSelfClosingTag JSXOpenTag"(n){var e;let t=(e=n.firstChild)===null||e===void 0?void 0:e.nextSibling,i=n.lastChild;return!t||t.type.isError?null:{from:t.to,to:i.type.isError?n.to:i.from}}})]}),languageData:{closeBrackets:{brackets:["(","[","{","'",'"',"`"]},commentTokens:{line:"//",block:{open:"/*",close:"*/"}},indentOnInput:/^\s*(?:case |default:|\{|\}|<\/)$/,wordChars:"$"}}),hd={test:n=>/^JSX/.test(n.name),facet:_u({commentTokens:{block:{open:"{/*",close:"*/}"}}})},pb=Lt.configure({dialect:"ts"},"typescript"),mb=Lt.configure({dialect:"jsx",props:[Zl.add(n=>n.isTop?[hd]:void 0)]}),Ob=Lt.configure({dialect:"jsx ts",props:[Zl.add(n=>n.isTop?[hd]:void 0)]},"typescript");let ud=n=>({label:n,type:"keyword"});const fd="break case const continue default delete export extends false finally in instanceof let new return static super switch this throw true typeof var yield".split(" ").map(ud),gb=fd.concat(["declare","implements","private","protected","public"].map(ud));function yb(n={}){let e=n.jsx?n.typescript?Ob:mb:n.typescript?pb:Lt,t=n.typescript?hb.concat(gb):od.concat(fd);return new kg(e,[Lt.data.of({autocomplete:Ty(cd,Yf(t))}),Lt.data.of({autocomplete:db}),n.jsx?Sb:[]])}function Ab(n){for(;;){if(n.name=="JSXOpenTag"||n.name=="JSXSelfClosingTag"||n.name=="JSXFragmentTag")return n;if(n.name=="JSXEscape"||!n.parent)return null;n=n.parent}}function _c(n,e,t=n.length){for(let i=e?.firstChild;i;i=i.nextSibling)if(i.name=="JSXIdentifier"||i.name=="JSXBuiltin"||i.name=="JSXNamespacedName"||i.name=="JSXMemberExpression")return n.sliceString(i.from,Math.min(i.to,t));return""}const bb=typeof navigator=="object"&&/Android\b/.test(navigator.userAgent),Sb=R.inputHandler.of((n,e,t,i,r)=>{if((bb?n.composing:n.compositionStarted)||n.state.readOnly||e!=t||i!=">"&&i!="/"||!Lt.isActiveAt(n.state,e,-1))return!1;let s=r(),{state:o}=s,l=o.changeByRange(a=>{var c;let{head:h}=a,u=Ce(o).resolveInner(h-1,-1),f;if(u.name=="JSXStartTag"&&(u=u.parent),!(o.doc.sliceString(h-1,h)!=i||u.name=="JSXAttributeValue"&&u.to>h)){if(i==">"&&u.name=="JSXFragmentTag")return{range:a,changes:{from:h,insert:""}};if(i=="/"&&u.name=="JSXStartCloseTag"){let d=u.parent,p=d.parent;if(p&&d.from==h-2&&((f=_c(o.doc,p.firstChild,h))||((c=p.firstChild)===null||c===void 0?void 0:c.name)=="JSXFragmentTag")){let m=`${f}>`;return{range:A.cursor(h+m.length,-1),changes:{from:h,insert:m}}}}else if(i==">"){let d=Ab(u);if(d&&d.name=="JSXOpenTag"&&!/^\/?>|^<\//.test(o.doc.sliceString(h,h+2))&&(f=_c(o.doc,d,h)))return{range:a,changes:{from:h,insert:``}}}}return{range:a}});return l.changes.empty?!1:(n.dispatch([s,o.update(l,{userEvent:"input.complete",scrollIntoView:!0})]),!0)}),Jc=typeof String.prototype.normalize=="function"?n=>n.normalize("NFKD"):n=>n;class Yi{constructor(e,t,i=0,r=e.length,s,o){this.test=o,this.value={from:0,to:0,precise:!1},this.done=!1,this.matches=[],this.buffer="",this.bufferPos=0,this.iter=e.iterRange(i,r),this.bufferStart=i,this.normalize=s?l=>s(Jc(l)):Jc,this.query=this.normalize(t)}peek(){if(this.bufferPos==this.buffer.length){if(this.bufferStart+=this.buffer.length,this.iter.next(),this.iter.done)return-1;this.bufferPos=0,this.buffer=this.iter.value}return Ne(this.buffer,this.bufferPos)}next(){for(;this.matches.length;)this.matches.pop();return this.nextOverlapping()}nextOverlapping(){for(;;){let e=this.peek();if(e<0)return this.done=!0,this;let t=fl(e),i=this.bufferStart+this.bufferPos;this.bufferPos+=Qt(e);let r=this.normalize(t);if(r.length)for(let s=0,o=i,l=!0;;s++){let a=r.charCodeAt(s),c=this.match(a,o,l,this.bufferPos+this.bufferStart,s==r.length-1);if(c)return this.value=c,this;if(s==r.length-1)break;l&&sthis.to&&(this.curLine=this.curLine.slice(0,this.to-this.curLineStart)),this.iter.next())}nextLine(){this.curLineStart=this.curLineStart+this.curLine.length+1,this.curLineStart>this.to?this.curLine="":this.getLine(0)}next(){for(let e=this.matchPos-this.curLineStart;;){this.re.lastIndex=e;let t=this.matchPos<=this.to&&this.re.exec(this.curLine);if(t){let i=this.curLineStart+t.index,r=i+t[0].length;if(this.matchPos=_r(this.text,r+(i==r?1:0)),i==this.curLineStart+this.curLine.length&&this.nextLine(),(ithis.value.to)&&(!this.test||this.test(i,r,t)))return this.value={from:i,to:r,precise:!0,match:t},this;e=this.matchPos-this.curLineStart}else if(this.curLineStart+this.curLine.length=i||r.to<=t){let l=new Xi(t,e.sliceString(t,i));return oo.set(e,l),l}if(r.from==t&&r.to==i)return r;let{text:s,from:o}=r;return o>t&&(s=e.sliceString(t,o)+s,o=t),r.to=this.to?this.to:this.text.lineAt(e).to}next(){for(;;){let e=this.re.lastIndex=this.matchPos-this.flat.from,t=this.re.exec(this.flat.text);if(t&&!t[0]&&t.index==e&&(this.re.lastIndex=e+1,t=this.re.exec(this.flat.text)),t){let i=this.flat.from+t.index,r=i+t[0].length;if((this.flat.to>=this.to||t.index+t[0].length<=this.flat.text.length-10)&&(!this.test||this.test(i,r,t)))return this.value={from:i,to:r,precise:!0,match:t},this.matchPos=_r(this.text,r+(i==r?1:0)),this}if(this.flat.to==this.to)return this.done=!0,this;this.flat=Xi.get(this.text,this.flat.from,this.chunkEnd(this.flat.from+this.flat.text.length*2))}}}typeof Symbol<"u"&&(pd.prototype[Symbol.iterator]=md.prototype[Symbol.iterator]=function(){return this});function xb(n){try{return new RegExp(n,Nl),!0}catch{return!1}}function _r(n,e){if(e>=n.length)return e;let t=n.lineAt(e),i;for(;e=56320&&i<57344;)e++;return e}const wb=n=>{let{state:e}=n,t=String(e.doc.lineAt(n.state.selection.main.head).number),{close:i,result:r}=VO(n,{label:e.phrase("Go to line"),input:{type:"text",name:"line",value:t},focus:!0,submitLabel:e.phrase("go")});return r.then(s=>{let o=s&&/^([+-])?(\d+)?(:\d+)?(%)?$/.exec(s.elements.line.value);if(!o){n.dispatch({effects:i});return}let l=e.doc.lineAt(e.selection.main.head),[,a,c,h,u]=o,f=h?+h.slice(1):0,d=c?+c:l.number;if(c&&u){let O=d/100;a&&(O=O*(a=="-"?-1:1)+l.number/e.doc.lines),d=Math.round(e.doc.lines*O)}else c&&a&&(d=d*(a=="-"?-1:1)+l.number);let p=e.doc.line(Math.max(1,Math.min(e.doc.lines,d))),m=A.cursor(p.from+Math.max(0,Math.min(f,p.length)));n.dispatch({effects:[i,R.scrollIntoView(m.from,{y:"center"})],selection:m})}),!0},vb={highlightWordAroundCursor:!1,minSelectionLength:1,maxMatches:100,wholeWords:!1},kb=K.define({combine(n){return yt(n,vb,{highlightWordAroundCursor:(e,t)=>e||t,minSelectionLength:Math.min,maxMatches:Math.min})}});function Qb(n){return[Rb,$b]}const Pb=B.mark({class:"cm-selectionMatch"}),Cb=B.mark({class:"cm-selectionMatch cm-selectionMatch-main"});function eh(n,e,t,i){return(t==0||n(e.sliceDoc(t-1,t))!=ye.Word)&&(i==e.doc.length||n(e.sliceDoc(i,i+1))!=ye.Word)}function Tb(n,e,t,i){return n(e.sliceDoc(t,t+1))==ye.Word&&n(e.sliceDoc(i-1,i))==ye.Word}const $b=xe.fromClass(class{constructor(n){this.decorations=this.getDeco(n)}update(n){(n.selectionSet||n.docChanged||n.viewportChanged)&&(this.decorations=this.getDeco(n.view))}getDeco(n){let e=n.state.facet(kb),{state:t}=n,i=t.selection;if(i.ranges.length>1)return B.none;let r=i.main,s,o=null;if(r.empty){if(!e.highlightWordAroundCursor)return B.none;let a=t.wordAt(r.head);if(!a)return B.none;o=t.charCategorizer(r.head),s=t.sliceDoc(a.from,a.to)}else{let a=r.to-r.from;if(a200)return B.none;if(e.wholeWords){if(s=t.sliceDoc(r.from,r.to),o=t.charCategorizer(r.head),!(eh(o,t,r.from,r.to)&&Tb(o,t,r.from,r.to)))return B.none}else if(s=t.sliceDoc(r.from,r.to),!s)return B.none}let l=[];for(let a of n.visibleRanges){let c=new Yi(t.doc,s,a.from,a.to);for(;!c.next().done;){let{from:h,to:u}=c.value;if((!o||eh(o,t,h,u))&&(r.empty&&h<=r.from&&u>=r.to?l.push(Cb.range(h,u)):(h>=r.to||u<=r.from)&&l.push(Pb.range(h,u)),l.length>e.maxMatches))return B.none}}return B.set(l)}},{decorations:n=>n.decorations}),Rb=R.baseTheme({".cm-selectionMatch":{backgroundColor:"#99ff7780"},".cm-searchMatch .cm-selectionMatch":{backgroundColor:"transparent"}}),Kb=({state:n,dispatch:e})=>{let{selection:t}=n,i=A.create(t.ranges.map(r=>n.wordAt(r.head)||A.cursor(r.head)),t.mainIndex);return i.eq(t)?!1:(e(n.update({selection:i})),!0)};function Zb(n,e){let{main:t,ranges:i}=n.selection,r=n.wordAt(t.head),s=r&&r.from==t.from&&r.to==t.to;for(let o=!1,l=new Yi(n.doc,e,i[i.length-1].to);;)if(l.next(),l.done){if(o)return null;l=new Yi(n.doc,e,0,Math.max(0,i[i.length-1].from-1)),o=!0}else{if(o&&i.some(a=>a.from==l.value.from))continue;if(s){let a=n.wordAt(l.value.from);if(!a||a.from!=l.value.from||a.to!=l.value.to)continue}return l.value}}const Mb=({state:n,dispatch:e})=>{let{ranges:t}=n.selection;if(t.some(s=>s.from===s.to))return Kb({state:n,dispatch:e});let i=n.sliceDoc(t[0].from,t[0].to);if(n.selection.ranges.some(s=>n.sliceDoc(s.from,s.to)!=i))return!1;let r=Zb(n,i);return r?(e(n.update({selection:n.selection.addRange(A.range(r.from,r.to),!1),effects:R.scrollIntoView(r.to)})),!0):!1},tn=K.define({combine(n){return yt(n,{top:!1,caseSensitive:!1,literal:!1,regexp:!1,wholeWord:!1,createPanel:e=>new Yb(e),scrollToMatch:e=>R.scrollIntoView(e)})}});class Od{constructor(e){this.search=e.search,this.caseSensitive=!!e.caseSensitive,this.literal=!!e.literal,this.regexp=!!e.regexp,this.replace=e.replace||"",this.valid=!!this.search&&(!this.regexp||xb(this.search)),this.unquoted=this.unquote(this.search),this.wholeWord=!!e.wholeWord,this.test=e.test}unquote(e){return this.literal?e:e.replace(/\\([nrt\\])/g,(t,i)=>i=="n"?` -`:i=="r"?"\r":i=="t"?" ":"\\")}eq(e){return this.search==e.search&&this.replace==e.replace&&this.caseSensitive==e.caseSensitive&&this.regexp==e.regexp&&this.wholeWord==e.wholeWord&&this.test==e.test}create(){return this.regexp?new Lb(this):new Fb(this)}getCursor(e,t=0,i){let r=e.doc?e:_.create({doc:e});return i==null&&(i=r.doc.length),this.regexp?Pi(this,r,t,i):Qi(this,r,t,i)}}class gd{constructor(e){this.spec=e}}function Eb(n,e,t){return(i,r,s,o)=>{if(t&&!t(i,r,s,o))return!1;let l=i>=o&&r<=o+s.length?s.slice(i-o,r-o):e.doc.sliceString(i,r);return n(l,e,i,r)}}function Qi(n,e,t,i){let r;return n.wholeWord&&(r=Ub(e.doc,e.charCategorizer(e.selection.main.head))),n.test&&(r=Eb(n.test,e,r)),new Yi(e.doc,n.unquoted,t,i,n.caseSensitive?void 0:s=>s.toLowerCase(),r)}function Ub(n,e){return(t,i,r,s)=>((s>t||s+r.length=t)return null;r.push(i.value)}return r}highlight(e,t,i,r){let s=Qi(this.spec,e,Math.max(0,t-this.spec.unquoted.length),Math.min(i+this.spec.unquoted.length,e.doc.length));for(;!s.next().done;)r(s.value.from,s.value.to)}}function Db(n,e,t){return(i,r,s)=>(!t||t(i,r,s))&&n(s[0],e,i,r)}function Pi(n,e,t,i){let r;return n.wholeWord&&(r=Xb(e.charCategorizer(e.selection.main.head))),n.test&&(r=Db(n.test,e,r)),new pd(e.doc,n.search,{ignoreCase:!n.caseSensitive,test:r},t,i)}function Jr(n,e){return n.slice(Ze(n,e,!1),e)}function es(n,e){return n.slice(e,Ze(n,e))}function Xb(n){return(e,t,i)=>!i[0].length||(n(Jr(i.input,i.index))!=ye.Word||n(es(i.input,i.index))!=ye.Word)&&(n(es(i.input,i.index+i[0].length))!=ye.Word||n(Jr(i.input,i.index+i[0].length))!=ye.Word)}class Lb extends gd{nextMatch(e,t,i){let r=Pi(this.spec,e,i,e.doc.length).next();return r.done&&(r=Pi(this.spec,e,0,t).next()),r.done?null:r.value}prevMatchInRange(e,t,i){for(let r=1;;r++){let s=Math.max(t,i-r*1e4),o=Pi(this.spec,e,s,i),l=null;for(;!o.next().done;)l=o.value;if(l&&(s==t||l.from>s+10))return l;if(s==t)return null}}prevMatch(e,t,i){return this.prevMatchInRange(e,0,t)||this.prevMatchInRange(e,i,e.doc.length)}getReplacement(e){return this.spec.unquote(this.spec.replace).replace(/\$([$&]|\d+)/g,(t,i)=>{if(i=="&")return e.match[0];if(i=="$")return"$";for(let r=i.length;r>0;r--){let s=+i.slice(0,r);if(s>0&&s=t)return null;r.push(i.value)}return r}highlight(e,t,i,r){let s=Pi(this.spec,e,Math.max(0,t-250),Math.min(i+250,e.doc.length));for(;!s.next().done;)r(s.value.from,s.value.to)}}const Zn=W.define(),Gl=W.define(),Ht=Te.define({create(n){return new lo(ll(n).create(),null)},update(n,e){for(let t of e.effects)t.is(Zn)?n=new lo(t.value.create(),n.panel):t.is(Gl)&&(n=new lo(n.query,t.value?Hl:null));return n},provide:n=>ji.from(n,e=>e.panel)});class lo{constructor(e,t){this.query=e,this.panel=t}}const Bb=B.mark({class:"cm-searchMatch"}),qb=B.mark({class:"cm-searchMatch cm-searchMatch-selected"}),Ib=xe.fromClass(class{constructor(n){this.view=n,this.decorations=this.highlight(n.state.field(Ht))}update(n){let e=n.state.field(Ht);(e!=n.startState.field(Ht)||n.docChanged||n.selectionSet||n.viewportChanged)&&(this.decorations=this.highlight(e))}highlight({query:n,panel:e}){if(!e||!n.spec.valid)return B.none;let{view:t}=this,i=new Bt;for(let r=0,s=t.visibleRanges,o=s.length;rs[r+1].from-500;)a=s[++r].to;n.highlight(t.state,l,a,(c,h)=>{let u=t.state.selection.ranges.some(f=>f.from==c&&f.to==h);i.add(c,h,u?qb:Bb)})}return i.finish()}},{decorations:n=>n.decorations});function zn(n){return e=>{let t=e.state.field(Ht,!1);return t&&t.query.spec.valid?n(e,t):bd(e)}}const ts=zn((n,{query:e})=>{let{to:t}=n.state.selection.main,i=e.nextMatch(n.state,t,t);if(!i)return!1;let r=A.single(i.from,i.to),s=n.state.facet(tn);return n.dispatch({selection:r,effects:[_l(n,i),s.scrollToMatch(r.main,n)],userEvent:"select.search"}),Ad(n),!0}),is=zn((n,{query:e})=>{let{state:t}=n,{from:i}=t.selection.main,r=e.prevMatch(t,i,i);if(!r)return!1;let s=A.single(r.from,r.to),o=n.state.facet(tn);return n.dispatch({selection:s,effects:[_l(n,r),o.scrollToMatch(s.main,n)],userEvent:"select.search"}),Ad(n),!0}),zb=zn((n,{query:e})=>{let t=e.matchAll(n.state,1e3);return!t||!t.length?!1:(n.dispatch({selection:A.create(t.map(i=>A.range(i.from,i.to))),userEvent:"select.search.matches"}),!0)}),Wb=({state:n,dispatch:e})=>{let t=n.selection;if(t.ranges.length>1||t.main.empty)return!1;let{from:i,to:r}=t.main,s=[],o=0;for(let l=new Yi(n.doc,n.sliceDoc(i,r));!l.next().done;){if(s.length>1e3)return!1;l.value.from==i&&(o=s.length),s.push(A.range(l.value.from,l.value.to))}return e(n.update({selection:A.create(s,o),userEvent:"select.search.matches"})),!0},th=zn((n,{query:e})=>{let{state:t}=n,{from:i,to:r}=t.selection.main;if(t.readOnly)return!1;let s=e.nextMatch(t,i,i);if(!s)return!1;let o=s,l=[],a,c,h=[];o.precise?o.from==i&&o.to==r&&(c=t.toText(e.getReplacement(o)),l.push({from:o.from,to:o.to,insert:c}),o=e.nextMatch(t,o.from,o.to),h.push(R.announce.of(t.phrase("replaced match on line $",t.doc.lineAt(i).number)+"."))):o=e.nextMatch(t,o.from,o.to);let u=n.state.changes(l);return o&&(a=A.single(o.from,o.to).map(u),h.push(_l(n,o)),h.push(t.facet(tn).scrollToMatch(a.main,n))),n.dispatch({changes:u,selection:a,effects:h,userEvent:"input.replace"}),!0}),jb=zn((n,{query:e})=>{if(n.state.readOnly)return!1;let t=[];for(let r of e.matchAll(n.state,1e9)){let{from:s,to:o,precise:l}=r;l&&t.push({from:s,to:o,insert:e.getReplacement(r)})}if(!t.length)return!1;let i=n.state.phrase("replaced $ matches",t.length)+".";return n.dispatch({changes:t,effects:R.announce.of(i),userEvent:"input.replace.all"}),!0});function Hl(n){return n.state.facet(tn).createPanel(n)}function ll(n,e){var t,i,r,s,o;let l=n.selection.main,a=l.empty||l.to>l.from+100?"":n.sliceDoc(l.from,l.to);if(e&&!a)return e;let c=n.facet(tn);return new Od({search:((t=e?.literal)!==null&&t!==void 0?t:c.literal)?a:a.replace(/\n/g,"\\n"),caseSensitive:(i=e?.caseSensitive)!==null&&i!==void 0?i:c.caseSensitive,literal:(r=e?.literal)!==null&&r!==void 0?r:c.literal,regexp:(s=e?.regexp)!==null&&s!==void 0?s:c.regexp,wholeWord:(o=e?.wholeWord)!==null&&o!==void 0?o:c.wholeWord})}function yd(n){let e=Fu(n,Hl);return e&&e.dom.querySelector("[main-field]")}function Ad(n){let e=yd(n);e&&e==n.root.activeElement&&e.select()}const bd=n=>{let e=n.state.field(Ht,!1);if(e&&e.panel){let t=yd(n);if(t&&t!=n.root.activeElement){let i=ll(n.state,e.query.spec);i.valid&&n.dispatch({effects:Zn.of(i)}),t.focus(),t.select()}}else n.dispatch({effects:[Gl.of(!0),e?Zn.of(ll(n.state,e.query.spec)):W.appendConfig.of(Gb)]});return!0},Sd=n=>{let e=n.state.field(Ht,!1);if(!e||!e.panel)return!1;let t=Fu(n,Hl);return t&&t.dom.contains(n.root.activeElement)&&n.focus(),n.dispatch({effects:Gl.of(!1)}),!0},Vb=[{key:"Mod-f",run:bd,scope:"editor search-panel"},{key:"F3",run:ts,shift:is,scope:"editor search-panel",preventDefault:!0},{key:"Mod-g",run:ts,shift:is,scope:"editor search-panel",preventDefault:!0},{key:"Escape",run:Sd,scope:"editor search-panel"},{key:"Mod-Shift-l",run:Wb},{key:"Mod-Alt-g",run:wb},{key:"Mod-d",run:Mb,preventDefault:!0}];class Yb{constructor(e){this.view=e;let t=this.query=e.state.field(Ht).query.spec;this.commit=this.commit.bind(this),this.searchField=he("input",{value:t.search,placeholder:rt(e,"Find"),"aria-label":rt(e,"Find"),class:"cm-textfield",name:"search",form:"","main-field":"true",onchange:this.commit,onkeyup:this.commit}),this.replaceField=he("input",{value:t.replace,placeholder:rt(e,"Replace"),"aria-label":rt(e,"Replace"),class:"cm-textfield",name:"replace",form:"",onchange:this.commit,onkeyup:this.commit}),this.caseField=he("input",{type:"checkbox",name:"case",form:"",checked:t.caseSensitive,onchange:this.commit}),this.reField=he("input",{type:"checkbox",name:"re",form:"",checked:t.regexp,onchange:this.commit}),this.wordField=he("input",{type:"checkbox",name:"word",form:"",checked:t.wholeWord,onchange:this.commit});function i(r,s,o){return he("button",{class:"cm-button",name:r,onclick:s,type:"button"},o)}this.dom=he("div",{onkeydown:r=>this.keydown(r),class:"cm-search"},[this.searchField,i("next",()=>ts(e),[rt(e,"next")]),i("prev",()=>is(e),[rt(e,"previous")]),i("select",()=>zb(e),[rt(e,"all")]),he("label",null,[this.caseField,rt(e,"match case")]),he("label",null,[this.reField,rt(e,"regexp")]),he("label",null,[this.wordField,rt(e,"by word")]),...e.state.readOnly?[]:[he("br"),this.replaceField,i("replace",()=>th(e),[rt(e,"replace")]),i("replaceAll",()=>jb(e),[rt(e,"replace all")])],he("button",{name:"close",onclick:()=>Sd(e),"aria-label":rt(e,"close"),type:"button"},["×"])])}commit(){let e=new Od({search:this.searchField.value,caseSensitive:this.caseField.checked,regexp:this.reField.checked,wholeWord:this.wordField.checked,replace:this.replaceField.value});e.eq(this.query)||(this.query=e,this.view.dispatch({effects:Zn.of(e)}))}keydown(e){rO(this.view,e,"search-panel")?e.preventDefault():e.keyCode==13&&e.target==this.searchField?(e.preventDefault(),(e.shiftKey?is:ts)(this.view)):e.keyCode==13&&e.target==this.replaceField&&(e.preventDefault(),th(this.view))}update(e){for(let t of e.transactions)for(let i of t.effects)i.is(Zn)&&!i.value.eq(this.query)&&this.setQuery(i.value)}setQuery(e){this.query=e,this.searchField.value=e.search,this.replaceField.value=e.replace,this.caseField.checked=e.caseSensitive,this.reField.checked=e.regexp,this.wordField.checked=e.wholeWord}mount(){this.searchField.select()}get pos(){return 80}get top(){return this.view.state.facet(tn).top}}function rt(n,e){return n.state.phrase(e)}const fr=30,dr=/[\s\.,:;?!]/;function _l(n,{from:e,to:t}){let i=n.state.doc.lineAt(e),r=n.state.doc.lineAt(t).to,s=Math.max(i.from,e-fr),o=Math.min(r,t+fr),l=n.state.sliceDoc(s,o);if(s!=i.from){for(let a=0;al.length-fr;a--)if(!dr.test(l[a-1])&&dr.test(l[a])){l=l.slice(0,a);break}}return R.announce.of(`${n.state.phrase("current match")}. ${l} ${n.state.phrase("on line")} ${i.number}.`)}const Nb=R.baseTheme({".cm-panel.cm-search":{padding:"2px 6px 4px",position:"relative","& [name=close]":{position:"absolute",top:"0",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",padding:0,margin:0},"& input, & button, & label":{margin:".2em .6em .2em 0"},"& input[type=checkbox]":{marginRight:".2em"},"& label":{fontSize:"80%",whiteSpace:"pre"}},"&light .cm-searchMatch":{backgroundColor:"#ffff0054"},"&dark .cm-searchMatch":{backgroundColor:"#00ffff8a"},"&light .cm-searchMatch-selected":{backgroundColor:"#ff6a0054"},"&dark .cm-searchMatch-selected":{backgroundColor:"#ff00ff8a"}}),Gb=[Ht,si.low(Ib),Nb];class ih{constructor(e,t,i){this.from=e,this.to=t,this.diagnostic=i}}class di{constructor(e,t,i){this.diagnostics=e,this.panel=t,this.selected=i}static init(e,t,i){let r=i.facet(Kt).markerFilter;r&&(e=r(e,i));let s=e.slice().sort((d,p)=>d.from-p.from||d.to-p.to),o=new Bt,l=[],a=0,c=i.doc.iter(),h=0,u=i.doc.length;for(let d=0;;){let p=d==s.length?null:s[d];if(!p&&!l.length)break;let m,O;if(l.length)m=a,O=l.reduce((x,C)=>Math.min(x,C.to),p&&p.from>m?p.from:1e8);else{if(m=p.from,m>u)break;O=p.to,l.push(p),d++}for(;dx.from||x.to==m))l.push(x),d++,O=Math.min(x.to,O);else{O=Math.min(x.from,O);break}}O=Math.min(O,u);let y=!1;if(l.some(x=>x.from==m&&(x.to==O||O==u))&&(y=m==O,!y&&O-m<10)){let x=m-(h+c.value.length);x>0&&(c.next(x),h=m);for(let C=m;;){if(C>=O){y=!0;break}if(!c.lineBreak&&h+c.value.length>C)break;C=h+c.value.length,h+=c.value.length,c.next()}}let b=Td(l);if(y)o.add(m,m,B.widget({widget:new n1(b),diagnostics:l.slice()}));else{let x=l.reduce((C,k)=>k.markClass?C+" "+k.markClass:C,"");o.add(m,O,B.mark({class:"cm-lintRange cm-lintRange-"+b+x,diagnostics:l.slice(),inclusiveEnd:l.some(C=>C.to>O)}))}if(a=O,a==u)break;for(let x=0;x{if(!(e&&o.diagnostics.indexOf(e)<0))if(!i)i=new ih(r,s,e||o.diagnostics[0]);else{if(o.diagnostics.indexOf(i.diagnostic)<0)return!1;i=new ih(i.from,s,i.diagnostic)}}),i}function xd(n,e){let t=e.pos,i=e.end||t,r=n.state.facet(Kt).hideOn(n,t,i);if(r!=null)return r;let s=n.startState.doc.lineAt(e.pos);return!!(n.effects.some(o=>o.is(Qs))||n.changes.touchesRange(s.from,Math.max(s.to,i)))}function Hb(n,e){return n.field(Ot,!1)?e:e.concat(W.appendConfig.of(Kd))}function wd(n,e){return{effects:Hb(n,[Qs.of(e)])}}const Qs=W.define(),vd=W.define(),kd=W.define(),Ot=Te.define({create(){return new di(B.none,null,null)},update(n,e){if(e.docChanged&&n.diagnostics.size){let t=n.diagnostics.map(e.changes),i=null,r=n.panel;if(n.selected){let s=e.changes.mapPos(n.selected.from,1);i=Ni(t,n.selected.diagnostic,s)||Ni(t,null,s)}!t.size&&r&&e.state.facet(Kt).autoPanel&&(r=null),n=new di(t,r,i)}for(let t of e.effects)if(t.is(Qs)){let i=e.state.facet(Kt).autoPanel?t.value.length?ns.open:null:n.panel;n=di.init(t.value,i,e.state)}else t.is(vd)?n=new di(n.diagnostics,t.value?ns.open:null,n.selected):t.is(kd)&&(n=new di(n.diagnostics,n.panel,t.value));return n},provide:n=>[ji.from(n,e=>e.panel),R.decorations.from(n,e=>e.diagnostics)]}),_b=B.mark({class:"cm-lintRange cm-lintRange-active"});function Jb(n,e,t){let{diagnostics:i}=n.state.field(Ot),r,s=-1,o=-1;i.between(e-(t<0?1:0),e+(t>0?1:0),(a,c,{spec:h})=>{if(e>=a&&e<=c&&(a==c||(e>a||t>0)&&(eCd(n,t,!1)))}const nh=n=>{let e=n.state.field(Ot,!1);return!e||!e.panel?!1:(n.dispatch({effects:vd.of(!1)}),!0)},e1=xe.fromClass(class{constructor(n){this.view=n,this.timeout=-1,this.set=!0;let{delay:e}=n.state.facet(Kt);this.lintTime=Date.now()+e,this.run=this.run.bind(this),this.timeout=setTimeout(this.run,e)}run(){clearTimeout(this.timeout);let n=Date.now();if(nPromise.resolve(i(this.view))),i=>{this.view.state.doc==e.doc&&this.view.dispatch(wd(this.view.state,i.reduce((r,s)=>r.concat(s))))},i=>{We(this.view.state,i)})}}update(n){let e=n.state.facet(Kt);(n.docChanged||e!=n.startState.facet(Kt)||e.needsRefresh&&e.needsRefresh(n))&&(this.lintTime=Date.now()+e.delay,this.set||(this.set=!0,this.timeout=setTimeout(this.run,e.delay)))}force(){this.set&&(this.lintTime=Date.now(),this.run())}destroy(){clearTimeout(this.timeout)}});function t1(n,e,t){let i=[],r=-1;for(let s of n)s.then(o=>{i.push(o),clearTimeout(r),i.length==n.length?e(i):r=setTimeout(()=>e(i),200)},t)}const Kt=K.define({combine(n){return{sources:n.map(e=>e.source).filter(e=>e!=null),...yt(n.map(e=>e.config),{delay:750,markerFilter:null,tooltipFilter:null,needsRefresh:null,hideOn:()=>null},{delay:Math.max,markerFilter:rh,tooltipFilter:rh,needsRefresh:(e,t)=>e?t?i=>e(i)||t(i):e:t,hideOn:(e,t)=>e?t?(i,r,s)=>e(i,r,s)||t(i,r,s):e:t,autoPanel:(e,t)=>e||t})}}});function rh(n,e){return n?e?(t,i)=>e(n(t,i),i):n:e}function i1(n,e={}){return[Kt.of({source:n,config:e}),e1,Kd]}function Pd(n){let e=[];if(n)e:for(let{name:t}of n){for(let i=0;is.toLowerCase()==r.toLowerCase())){e.push(r);continue e}}e.push("")}return e}function Cd(n,e,t){var i;let r=t?Pd(e.actions):[];return he("li",{class:"cm-diagnostic cm-diagnostic-"+e.severity},he("span",{class:"cm-diagnosticText"},e.renderMessage?e.renderMessage(n):e.message),(i=e.actions)===null||i===void 0?void 0:i.map((s,o)=>{let l=!1,a=d=>{if(d.preventDefault(),l)return;l=!0;let p=Ni(n.state.field(Ot).diagnostics,e);p&&s.apply(n,p.from,p.to)},{name:c}=s,h=r[o]?c.indexOf(r[o]):-1,u=h<0?c:[c.slice(0,h),he("u",c.slice(h,h+1)),c.slice(h+1)],f=s.markClass?" "+s.markClass:"";return he("button",{type:"button",class:"cm-diagnosticAction"+f,onclick:a,onmousedown:a,"aria-label":` Action: ${c}${h<0?"":` (access key "${r[o]})"`}.`},u)}),e.source&&he("div",{class:"cm-diagnosticSource"},e.source))}class n1 extends Ut{constructor(e){super(),this.sev=e}eq(e){return e.sev==this.sev}toDOM(){return he("span",{class:"cm-lintPoint cm-lintPoint-"+this.sev})}}class sh{constructor(e,t){this.diagnostic=t,this.id="item_"+Math.floor(Math.random()*4294967295).toString(16),this.dom=Cd(e,t,!0),this.dom.id=this.id,this.dom.setAttribute("role","option")}}class ns{constructor(e){this.view=e,this.items=[];let t=r=>{if(!(r.ctrlKey||r.altKey||r.metaKey)){if(r.keyCode==27)nh(this.view),this.view.focus();else if(r.keyCode==38||r.keyCode==33)this.moveSelection((this.selectedIndex-1+this.items.length)%this.items.length);else if(r.keyCode==40||r.keyCode==34)this.moveSelection((this.selectedIndex+1)%this.items.length);else if(r.keyCode==36)this.moveSelection(0);else if(r.keyCode==35)this.moveSelection(this.items.length-1);else if(r.keyCode==13)this.view.focus();else if(r.keyCode>=65&&r.keyCode<=90&&this.selectedIndex>=0){let{diagnostic:s}=this.items[this.selectedIndex],o=Pd(s.actions);for(let l=0;l{for(let s=0;snh(this.view)},"×")),this.update()}get selectedIndex(){let e=this.view.state.field(Ot).selected;if(!e)return-1;for(let t=0;t{for(let h of c.diagnostics){if(o.has(h))continue;o.add(h);let u=-1,f;for(let d=i;di&&(this.items.splice(i,u-i),r=!0)),t&&f.diagnostic==t.diagnostic?f.dom.hasAttribute("aria-selected")||(f.dom.setAttribute("aria-selected","true"),s=f):f.dom.hasAttribute("aria-selected")&&f.dom.removeAttribute("aria-selected"),i++}});i({sel:s.dom.getBoundingClientRect(),panel:this.list.getBoundingClientRect()}),write:({sel:l,panel:a})=>{let c=a.height/this.list.offsetHeight;l.topa.bottom&&(this.list.scrollTop+=(l.bottom-a.bottom)/c)}})):this.selectedIndex<0&&this.list.removeAttribute("aria-activedescendant"),r&&this.sync()}sync(){let e=this.list.firstChild;function t(){let i=e;e=i.nextSibling,i.remove()}for(let i of this.items)if(i.dom.parentNode==this.list){for(;e!=i.dom;)t();e=i.dom.nextSibling}else this.list.insertBefore(i.dom,e);for(;e;)t()}moveSelection(e){if(this.selectedIndex<0)return;let t=this.view.state.field(Ot),i=Ni(t.diagnostics,this.items[e].diagnostic);i&&this.view.dispatch({selection:{anchor:i.from,head:i.to},scrollIntoView:!0,effects:kd.of(i)})}static open(e){return new ns(e)}}function Qr(n,e='viewBox="0 0 40 40"'){return`url('data:image/svg+xml,${encodeURIComponent(n)}')`}function pr(n){return Qr(``,'width="6" height="3"')}const r1=R.baseTheme({".cm-diagnostic":{padding:"3px 6px 3px 8px",marginLeft:"-1px",display:"block",whiteSpace:"pre-wrap"},".cm-diagnostic-error":{borderLeft:"5px solid #d11"},".cm-diagnostic-warning":{borderLeft:"5px solid orange"},".cm-diagnostic-info":{borderLeft:"5px solid #999"},".cm-diagnostic-hint":{borderLeft:"5px solid #66d"},".cm-diagnosticAction":{font:"inherit",border:"none",padding:"2px 4px",backgroundColor:"#444",color:"white",borderRadius:"3px",marginLeft:"8px",cursor:"pointer"},".cm-diagnosticSource":{fontSize:"70%",opacity:.7},".cm-lintRange":{backgroundPosition:"left bottom",backgroundRepeat:"repeat-x",paddingBottom:"0.7px"},".cm-lintRange-error":{backgroundImage:pr("#f11")},".cm-lintRange-warning":{backgroundImage:pr("orange")},".cm-lintRange-info":{backgroundImage:pr("#999")},".cm-lintRange-hint":{backgroundImage:pr("#66d")},".cm-lintRange-active":{backgroundColor:"#ffdd9980"},".cm-tooltip-lint":{padding:0,margin:0},".cm-lintPoint":{position:"relative","&:after":{content:'""',position:"absolute",bottom:0,left:"-2px",borderLeft:"3px solid transparent",borderRight:"3px solid transparent",borderBottom:"4px solid #d11"}},".cm-lintPoint-warning":{"&:after":{borderBottomColor:"orange"}},".cm-lintPoint-info":{"&:after":{borderBottomColor:"#999"}},".cm-lintPoint-hint":{"&:after":{borderBottomColor:"#66d"}},".cm-panel.cm-panel-lint":{position:"relative","& ul":{maxHeight:"100px",overflowY:"auto","& [aria-selected]":{backgroundColor:"#ddd","& u":{textDecoration:"underline"}},"&:focus [aria-selected]":{background_fallback:"#bdf",backgroundColor:"Highlight",color_fallback:"white",color:"HighlightText"},"& u":{textDecoration:"none"},padding:0,margin:0},"& [name=close]":{position:"absolute",top:"0",right:"2px",background:"inherit",border:"none",font:"inherit",padding:0,margin:0}},"&dark .cm-lintRange-active":{backgroundColor:"#86714a80"},"&dark .cm-panel.cm-panel-lint ul":{"& [aria-selected]":{backgroundColor:"#2e343e"}}});function s1(n){return n=="error"?4:n=="warning"?3:n=="info"?2:1}function Td(n){let e="hint",t=1;for(let i of n){let r=s1(i.severity);r>t&&(t=r,e=i.severity)}return e}class $d extends Mt{constructor(e){super(),this.diagnostics=e,this.severity=Td(e)}toDOM(e){let t=document.createElement("div");t.className="cm-lint-marker cm-lint-marker-"+this.severity;let i=this.diagnostics,r=e.state.facet(Ps).tooltipFilter;return r&&(i=r(i,e.state)),i.length&&(t.onmouseover=()=>l1(e,t,i)),t}}function o1(n,e){let t=i=>{let r=e.getBoundingClientRect();if(!(i.clientX>r.left-10&&i.clientXr.top-10&&i.clientYe.getBoundingClientRect()}}})}),e.onmouseout=e.onmousemove=null,o1(n,e)}let{hoverTime:r}=n.state.facet(Ps),s=setTimeout(i,r);e.onmouseout=()=>{clearTimeout(s),e.onmouseout=e.onmousemove=null},e.onmousemove=()=>{clearTimeout(s),s=setTimeout(i,r)}}function a1(n,e){let t=Object.create(null);for(let r of e){let s=n.lineAt(r.from);(t[s.from]||(t[s.from]=[])).push(r)}let i=[];for(let r in t)i.push(new $d(t[r]).range(+r));return N.of(i,!0)}const c1=Bu({class:"cm-gutter-lint",markers:n=>n.state.field(al),widgetMarker:(n,e,t)=>{let i=[];return n.state.field(al).between(t.from,t.to,(r,s,o)=>{r>t.from&&ri.is(Jl)?i.value:t,n)},provide:n=>Os.from(n)}),h1=R.baseTheme({".cm-gutter-lint":{width:"1.4em","& .cm-gutterElement":{padding:".2em"}},".cm-lint-marker":{width:"1em",height:"1em"},".cm-lint-marker-info":{content:Qr('')},".cm-lint-marker-warning":{content:Qr('')},".cm-lint-marker-error":{content:Qr('')}}),u1=Eu(Jb,{hideOn:xd}),Kd=[Ot,R.decorations.compute([Ot],n=>{let{selected:e,panel:t}=n.field(Ot);return!e||!t||e.from==e.to?B.none:B.set([_b.range(e.from,e.to)])}),u1,r1],Ps=K.define({combine(n){return yt(n,{hoverTime:300,markerFilter:null,tooltipFilter:null})}});function f1(n={}){return[Ps.of(n),al,c1,h1,Rd]}const Zd=1,Ue=Object.freeze({ready:"studio.ready",documentOpen:"document.open",documentReplace:"document.replace",documentChanged:"document.changed",documentSave:"document.save",documentSetReadOnly:"document.setReadOnly",documentResources:"document.resources",editorInsert:"editor.insert",editorFormat:"editor.format",editorSetTheme:"editor.setTheme",editorGetState:"editor.getState",diagnosticsPublish:"diagnostics.publish"});function d1(n,e={},t=null,i=null){return{protocolVersion:Zd,type:n,documentID:t,...i===null?{}:{sessionID:i},payload:e}}function p1(n){if(window.WKWebViewJavascriptBridge){n(window.WKWebViewJavascriptBridge);return}if(window.WKWVJBCallbacks){window.WKWVJBCallbacks.push(n);return}window.webkit?.messageHandlers?.iOS_Native_InjectJavascript&&(window.WKWVJBCallbacks=[n],window.webkit.messageHandlers.iOS_Native_InjectJavascript.postMessage(null))}function nn(n,e,t={},i=null,r=()=>{},s=null){if(!n){r({result:"unavailable"});return}n.callHandler(e,d1(e,t,i,s),r)}function m1(n){const e={capabilities:["jsx","format","search","completion","diagnostics"]};nn(n,Ue.ready,e)}const O1=[{id:"$storage.getString",arguments:["key"],returns:"string",documentation:"Read a package-scoped string. Missing keys return an empty string."},{id:"$storage.setString",arguments:["key","value"],returns:"boolean",documentation:"Set a package-scoped string, limited to 256 KiB of UTF-8. Check the success result."},{id:"$storage.getJSON",arguments:["key"],returns:"json",documentation:"Read a package-scoped JSON object. Missing or invalid values return an empty object."},{id:"$storage.setJSON",arguments:["key","value"],returns:"boolean",documentation:"Store a valid JSON object, limited to 256 KiB encoded. Check the success result."},{id:"$storage.compareAndSetString",arguments:["key","expected","value"],returns:"boolean",minimumRuntimeVersion:"1.3",documentation:"Atomically replace a string only when it still matches expected. A missing key matches an empty string. Conflicts return false; reread and retry a bounded number of times. Requires the shared storage container."},{id:"$storage.compareAndSetJSON",arguments:["key","expected","value"],returns:"boolean",minimumRuntimeVersion:"1.3",documentation:"Atomically replace a JSON object if its current value equals expected, independent of property order. A missing key matches an empty object. Wrong types and conflicts return false. Requires the shared storage container."},{id:"$storage.remove",arguments:["key"],returns:"boolean",documentation:"Remove a key from this package only."},{id:"$storage.keys",arguments:[],returns:"string[]",documentation:"List keys in this package's namespace."},{id:"$storage.clear",arguments:[],returns:"boolean",documentation:"Clear this package's storage. Other packages are unaffected."}].map(n=>({minimumRuntimeVersion:"1.0",...n,preview:"initialState"})),cl="2026-01-01T00:00:00Z",Wt="2026-01-01T12:00:00Z",ao=(n,e)=>({value:n,unit:e,hasSamples:!0,start:cl,end:Wt}),oh=(n,e,t)=>Object.entries(e).map(([i,r])=>({id:`$${n}.${i}`,group:n,value:r,legacy:[t,i]})),g1=[{id:"$music.isAvailable",group:"music",value:!0},{id:"$music.authorizationStatus",group:"music",value:"authorized"},{id:"$music.current",group:"music",value:{provider:"appleMusic",state:"playing",hasItem:!0,title:"Morning Light",artist:"ScriptWidget Sessions",album:"Focus",duration:224,position:86,capturedAt:Wt}},{id:"$services.status",group:"services",value:"connected"},{id:"$services.profile",group:"services",value:{service:"github",subject:"42",username:"octocat",displayName:"Mona",publicRepositories:32,privateRepositories:8,followers:128,following:24}},...oh("device",{screen:{width:390,height:844,scale:3},language:"en",model:"iPhone",name:"My iPhone",battery:{level:.8,state:"unplugged"},systemVersion:"26.0",isdarkmode:!1,totalDiskSpace:128e9,freeDiskSpace:64e9},"device"),...oh("system",{appInfo:{name:"ScriptWidget",bundleId:"com.everettjf.scriptwidget",version:"27.1.2",build:"27012"},locale:"en_US",preferredLanguages:["en-US"],timeZone:{identifier:"UTC",abbreviation:"GMT",offsetSeconds:0},is24HourClock:!1,calendarInfo:{identifier:"gregorian",firstWeekday:1,minimumDaysInFirstWeek:1},systemUptime:3600,memory:{physical:8589934592},thermalState:"nominal",lowPowerMode:!1,brightness:.5,reduceMotionEnabled:!1,platform:"ios",hostName:"My iPhone",processName:"ScriptWidgetWidget",osVersionString:"Version 26.0",processorCount:6,activeProcessorCount:6},"system"),{id:"$photos.isAvailable",group:"photos",value:!0},{id:"$photos.authorizationStatus",group:"photos",value:"authorized"},{id:"$photos.requestAuthorization",group:"photos",value:!0},{id:"$photos.items",group:"photos",value:{status:"ready",items:[{id:"sample.photo",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/biiiiu08cKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/biiiiu08cKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKK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8ZPqjNpumMVthwzdC/8A9j/OnGNzOpUUFdkvjHxm2oFtL0psQdHcdX9h/s/z+leb0UVslY86c3N3YUUUUyAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvePgx8bNT+Gl8NN1Hdc6PM2ZIurRE9Xjz39V6H2PNeD0UjSlWlSkpwdmftNo+saZr+mQ6xo0y3FtcLvjkQ5BH9COhB5B4NaVflf8IPjJrHww1LyZN1zpU7Znt88g9N8eejDuOjDg9iP038P+INH8UaRDrmhTrcWs67kdf1BHUEdCDyDUNWPrsFjo4mPZ9Uf//S+tKKKK6z8/CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACmSSRwxtLKwVVGSTwABSTTRW8TTzsERBkseABXhHi3xfLrchsrIlLVT9C5Hc+3oPz9nGNzOpVUFdkvi/xjJrDmw08lbVTyehkI7n29B+ftwNFFbJWPNnNyd2FFFFMkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvWvhP8W9b+F+r+bBm40+dh9otieG7bk/uuB379D2x5LRSLp1JU5KcHZo//0/rSiiius/PwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACobi4gtYWublgiIMsx6AU26ureyt3urpwkaDJY14H4q8V3GvzeTDmO1Q/Kvdvdv8O1OMbmVWqoIk8WeLZtdlNrbZS1Q8Du59T/AEFcXRRWyVjzpScndhRRRTJCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9T60ooorrPz8KKKKACiiigAooooAKKKKACiiigAooooAKKKKACq15eW2n2z3d44SNBkk0y/v7XTbV7y9cJGg5J/kPU18/eJfE114hucnKW6H5I/6n1P8qqMbmVWqoLzJPFHim58QXGxcx2yH5E9fdvf+VcpRRWqVjzpScndhRRRTJCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9X60ooorrPz8KKKKACiiigAooooAKKKKACiiigAooooAKo6jqNppVo17etsRfzJ9B6mm6pqlnpFm17ettReg7k+gHrXz34h8Q3niC786c7Y1+5GOij+p9TVRjcxq1lBeY/xH4ku/EN1vk+SFD8kfYe59TXOUUVqedKTbuwooopiCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9b60oqRYZX+4pP0FWV03UX+5byH6Kf8K6z4CzKVFay6Drj/AHLKc/SNv8KsJ4V8Tv8Ac026P0hf/CgfJLsYNFdOngvxc/TTbn8Y2H8xVhPAHjN+mnTfiMfzoH7KfZnIUV3C/Dbxu/TT3/FkH82qyvws8dt/y44+skf/AMVQV7Cp/K/uPPqK9JT4S+OG62yL9ZE/oTVlfg94zbqkK/WQf0BosP6vU/lZ5bRXra/Bjxc3V7Zfq7f0U1YX4JeKD9+4tR/wJz/7JRYf1Wr/ACnjlZmravZaLZte3rYUcADqx9BXpXjnwZH8PNCfXPEeoQKv3Y4k3GSV/wC6gIH4noB1r4t1vXL3Xrw3V2cAcIg6KPQf1PeqjG5y4mTo+7Jaj9e1+91+8NzcnCDhEHRR/j6msOiitTy223dhRRRTEFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/Z",width:384,height:256},{id:"sample.photo.2",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/ayiiivVAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9D9rKKKK9UAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoooo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AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApGZUUu5wBySe1NkkSJDJIQqqMkngADua+f8Axv46fWGbStJYrag4Zuhk/wDsf51dOm5uyJbsT+OPHbamX0jR2xbDh5B1k9h/s/z+leV0UV6UIKKsjJu4UUUVYgooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvoL4IfHTVfhdfjTNS3XWiztmSHq0RPWSPPf1Xo3sea+faKzq0o1IuE1oVGbi+aJ+5Gi61pXiLS4Na0SdLm1uF3xyIcgj+hHQg8g8HmtSvyU+DHxs1r4Vap5Em660i4bM9tnkHp5keeA47jow4PYj9UPDviPRfFmjQa/4fnW5tbhdyOv6gjqCDwQeQa+ZxeElQl5Hq0ayqLzP//S+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACo5ZYoI2mmYIiDLMTgADuaSeeG2ha4uGCIgyzHgACvnbxp42m1+Q2NgSlmp+hkI7n29B+J9tKVJzdkTKViXxt44k1x203TSUtFPJ6GQjuf9n0H4n284oor0oxUVZGLdwoooqgCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAr2H4QfGLXvhTrPm2+bjTZ2H2m1J4btvT+64Hfv0PbHj1FROEZxcZrQcZOLuj//T+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKgubm3s7d7q6cRxxjLMegFNu7y2sLZ7y8cRxxjLMe1fN/jDxlc+JJ/IgzHaIflTux/vN7+g7VpSpObJlKxL4y8aXHiKY2lpmOzQ8L0Lkd2/oK4OiivSjFRVkYt3CiiiqAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//1PqCiiivszygooooAKKKKACiiigAooooAKKKKACiiigAooooAKqX19aabave3riOKMZJP+etN1HUbPSrN76/cRxoOSf5D1J9K+afFfiy88TXeTmO2Q/u4/6t6n+XataVJzfkTKViXxd4vu/EtzsTMdrGfkj9f9pvf+VcbRRXoxioqyMW7hRRRVAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH//V+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACs/VNUstHsnv799kafmT2AHcmmavq9jolk1/qD7UXoO7HsAO5NfM3iXxNfeJb3z7j5Il/1cY6KP6k9zW1Ki5vyJlKxL4o8VXvia78yX5IEP7uMdB7n1PvXLUUV6EYpKyMW7hRRRVAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//1vqCipFhlf7ik/QVZTTdRk+5byH6IT/SvsrnlWKVFay6Drr/AHLKc/SNv8Ksp4U8UP8Ac026P0hf/ClzLuOzMCiuoXwT4vfpplz+MbD+Yqynw/8AGj9NNm/EY/nS549x8j7HHUV3K/DXxy/TT3/FkH82qyvwq8ev/wAuGPrLEP8A2al7WHdByS7HntFelp8I/HLdbZF+sif0NWU+DnjRuqQr9ZB/QGl7aH8w/Zy7HldFeur8FfGDdXtl+rt/Rasr8D/FR+/c2o/4E5/9kpe3h3H7KXY8ZrJ1rWrDQbFr+/bCjhVHVj6AetemePvB0Xw20B9f8TajAq/diiTcZJX/ALqAgZ9z0A5NfCviDxBf+Ir43l6cAcIg+6g9B/U966sPBVfeWxlUvDRkniPxHf8AiS9N1dnai8Rxjoo/x9TXP0UV6aSSsjAKKKKYBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH//Z",width:384,height:256},{id:"sample.photo.3",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//R/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKrXl5a6fave3sgiijG5mY4AFOtbq3vbZLu0cSRSAMrLyCDTt1AnooopAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/9L9vKKKK7T0wooooAKKKKACiiigAooooAKKKKACs/VNUsNGsX1HUpBFDGMkn+Q9Sewqtruvab4c099S1STYi8AfxMeyqO5NfInjDxnqfi++865PlwIf3UIPCj1Pq3qa3o0XUfkXCHMX/HHjy/8AF115SZhsoz+7iz1/2m9T+g/U6/w38fv4buRpWqMTYSnr18pj3H+ye4/Ed8+U0V6DpR5eS2h0cqtY/QJHSVBJGQysMgjkEHuKdXzT8MPiF/Zjp4c1t/8ARmOIZGP+rJ/hP+ye3p9On0tXmVKbg7M5pR5XYKKKKzJCiiigAooooAKKKKACiiigAooooAKKKKACiiigApkkiRIZJDtUckmn1xXiK/aSb7DGflTlvc//AFqmcuVXPJzrNYZbhpYiau9ku7C/8RTSMY7H5F/vHqf8K52SeaY5ldmPuc1FRXHKTe5+LY/NsVjpOeIm35dF6IljnmhOYnZT7HFdFYeIpo2Ed986/wB4dR/jXMUURk1sGAzbFYGSnh5teXR+qPWY5ElQSRncp5BFPrivDt+0c32GQ/K/K+x/+vXa12QlzK5+05LmsMyw0cRBWezXZhRRRVHrBRRRQAUUUUAf/9P9vKKKK7T0wooooAKKKKACiiigAooooAK5vxP4p0vwpp5vtRbLHiOMfec+g/qe1U/GHjPTfCFj51yfMuHH7qEHlj6n0X1NfIeu69qXiPUH1LVJN8jcAfwqOyqOwH+ea6KNBz1expCnfVlvxP4o1TxXqBvtRbAHEcY+6i+g/qe9c5RRXpJJKyOlK2wUUUUwCvoj4XfEPzRH4Y1yT5x8tvK3f0Rj6/3T36elfO9KCQcjgioqU1NWZMoqSsz9A6K8c+GfxBGtxLoWsv8A6ZGP3bn/AJaqP/Zh39Rz617HXlTg4PlZyyjZ2YUUUVAgooooAKKKKACiiigAooooAKKKKACiiigAryieQzTPKerMT+der15RPGYZniPVWI/KsK/Q/O/EDm5KFtry+/S36kVFFFcx+ZhRRRQBLBIYZklHVWB/KvV68ogjM0yRDqzAfnXq9dNDqfpnh/zcle+14/frf9Aooorc/RAooooAKKKKAP/U/byiiiu09MKKKKACiiigAooooAK4Dxx49sPCNr5SYmvZB+7i9P8Aaf0H6n8zWd4++Itr4XibTtOIlv2HTqsYPdvf0H4njr8qXl5dahcve3shllkO5mY5JNdVDD83vS2NYU76sn1TVL/Wb59R1KQyzSHJY/yHoB2FZ9FFeilbY6AooooAKKKKACiiigCSGaW3lWeBijoQysDggjoQa+s/h349i8U2n2G/IW/hX5h08xR/EP6j+lfJFW7G+u9MvI7+xcxzRHcrDqDWVWkqisROPMj75orhvA3jW08X6duOI7uIATR/+zL/ALJ/TpXc15couLszlatowoooqQCiiigAooooAKKKKACiiigAoor5v+OXx2svh5av4f8ADzLPrUq/VbdSOGf1b+6v4njAOlOnKpLlgioU3N8sS58bfjlp/wANrJtH0crca1MvyJ1WEHo8nv8A3V79Tx14j4Z/GOx+IsK2eq7bfWEX94g4WbA++nv/AHl7dRx0+Ab+/vdUvZdR1GVp552LySOcszHkkk0y0u7mxuY7yykaKWJgyOpwykdCCK9eeWQlS5Hv3FnPDtLMsI8LN2e6fZ/5H6wUV4f8HfixB47iXw/rDLFq6D5QcKLgDunbeO6/iOMge6yQTQnEqMp9xivmq1CdGThNH4HmeS4vLqjpYqm1br0foyKipY4JpjiJGY+wzXRWHh2aRhJffIv90dT/AIVnGLexngMpxWOkoYeDfn0Xqw8O2DSTfbpB8qcL7n/61drTI40iQRxjao4AFPrshHlVj9pyXKoZbho4eDu92+7CiiiqPWCiiigAooooA//V/byiiiu09MKKKKACiiigArxz4h/EuLQw+jaEwe8PDydVi/xb+Xf0rK+InxPFr5mheGpMy8rLOv8AD6qh9fU9u3PT50JLEsxyTXbQw9/embU6fVj5ZZZ5WmnYu7klmY5JJ6kmo6KK7jcKKKKACiiigAooooAKKKKACiiigDU0bWL/AEHUY9U019ksZ/AjuCO4Pevsjwl4qsPFulrf2nyyLxLGTyjf4Hse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",width:384,height:256},{id:"sample.photo.4",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/biiiiuc9UKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/biiiiuc9UKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKK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0ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApGZUUu5wBySaR3SNDJIQqqMkngACvBvGnjZ9WZtM0titqDhm6GT/7H+dJuxrSpOo7Im8a+OG1ItpOkNi36O46v7D/Z/n9K8woorNu560IKC5YhRRRSLCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK98+Cvxw1T4ZXw0zUd1zo07Zki6tET1ePPf1XofrzXgdFMyq0o1YuE1oftro2s6X4h0yHWdFnW5tbhd8ciHII/oR0IPIPBrTr8o/g58Z9Z+Fup+TJuudJnbM9vnkHp5keeA47jow4PYj9QvD3iHRvFWjw69oE63NrcLuR1/UEdQQeCDyDTTPlMbgpYeXddGf/9L60ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACmSyxwxtNMwVFGSTwAB3NNnnhtoWuLhgiIMsx4AAr5+8Y+M5tekNlYkpaKfoXI7n29B+J9k3Y2o0XUdkS+MvGsmtO2nacSlop5PQyEdz7eg/E155RRWbdz1YQUFyxCiiikWFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXrvwk+L2ufCzWPNt83GnTsPtNqTw3ben91wO/foe2PIqKDOpTjUi4TV0f/T+tKKKKyPtQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACobi4gtIHublwkaDLMegFNu7u2sbZ7u7cRxoMsx7V87+LfF9z4in8iHMdoh+VO7H+839B2pN2NqNF1H5EvjDxjP4gmNra5jtEPC93I7t/QVw1FFZtnqwgorliFFFFIsKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9T60ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAqre3trp1q95euI40GST/nrTNQ1C00u0e9vnCRoOSf5D1Jr5y8U+KrvxJdZOY7dD+7j/qfU/wAqTdjejRdR+RL4r8WXXiO52LmO1Q/Inr/tN7/yrkKKKzPVjFRVkFFFFIoKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//V+tKKKKyPtQooooAKKKKACiiigAooooAKKKKACiiigAqhqep2ekWb318+yNPzJ9AO5NM1XVrLRbJr6/fai9B3Y9gB3NfN/iPxJe+I7zz7j5Yl/wBXGOij+p9TUt2OihQdR+RJ4m8T3niO78yT5IEP7uPsPc+prmaKKg9SMVFWQUUUUigooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/W+tKKkWKV/uKT9BVlNO1CT7kEjfRT/hWR9rcpUVqpoWuP9yznP0jb/CrK+FvE7/c066P0hf8AwoFzLuYNFdMvgvxc/TTbn8Y2H8xVlfAPjJ+mnTfiMfzoF7SPc5Ciu4X4b+N36ae/4sg/masL8LfHb/8ALjj6yR//ABVIXtYfzI8/or0lPhL44brbIv1kT+hqynwf8Zt1SFfrIP6A0C9tD+ZHltFetL8GPFzdXt1+rt/RTVlfgl4oP37i1H/AnP8A7JQL6xT/AJjxysrWNZsdDsmvr5sKOAB1Y+gHrXpXjvwfF8OdBfXfEmoQKv3Yok3GSV/7qAgZ9z0A618Q69r194gvTd3hwBwiDoo9B/U96TdjrwtNVveWw/xB4hvvEV6bm6O1F4SMdFH+PqawaKKg9dJJWQUUUUhhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH/9k=",width:384,height:256}],unavailableCount:0,fetchedAt:Wt,source:"system",stale:!1}},{id:"$calendar.isAvailable",group:"calendar",value:!0},{id:"$calendar.authorizationStatus",group:"calendar",value:"authorized"},{id:"$calendar.requestAuthorization",group:"calendar",value:!0},{id:"$calendar.calendars",group:"calendar",value:[{id:"sample.personal",title:"Personal"}]},{id:"$calendar.events",group:"calendar",value:{status:"ready",items:[{id:"sample.event",calendarID:"sample.personal",title:"Read and recharge",start:"2026-01-01T15:00:00Z",end:"2026-01-01T15:30:00Z",allDay:!1}],fetchedAt:Wt,source:"system",stale:!1,truncated:!1}},{id:"$reminders.isAvailable",group:"reminders",value:!0},{id:"$reminders.authorizationStatus",group:"reminders",value:"authorized"},{id:"$reminders.requestAuthorization",group:"reminders",value:!0},{id:"$reminders.lists",group:"reminders",value:[{id:"sample.reminders",title:"Personal"}]},{id:"$reminders.items",group:"reminders",value:{status:"ready",items:[{id:"sample.reminder",listID:"sample.reminders",title:"Read a chapter",due:"2026-01-01T16:00:00Z",hasTime:!0,completed:!1,completedAt:null,priority:0}],fetchedAt:Wt,source:"system",stale:!1,truncated:!1}},{id:"$reminders.create",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$reminders.update",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$reminders.setCompleted",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$health.authorizationStatus",group:"health",value:"notDisclosed"},{id:"$health.sleepSummary",group:"health",value:{start:cl,end:Wt,timeZone:"UTC",hasSamples:!0,totalSleepSeconds:27e3,inBedSeconds:30600,awakeSeconds:1800,stages:{unspecified:0,core:16200,deep:5400,rem:5400},sourceCount:1,truncated:!1,conflictSeconds:0,aggregation:"union-awake-rem-deep-core-unspecified"}},{id:"$health.activitySummary",group:"health",value:{date:"2026-01-01",timeZone:"UTC",hasSamples:!0,moveMode:"activeEnergy",move:{value:480,goal:600,unit:"kcal"},exercise:{value:26,goal:30,unit:"min"},stand:{value:9,goal:12,unit:"count"}}},{id:"$health.workouts",group:"health",value:{start:cl,end:Wt,timeZone:"UTC",hasSamples:!0,truncated:!1,items:[{id:"550e8400-e29b-41d4-a716-446655440000",activity:"running",start:"2026-01-01T09:00:00.000Z",end:"2026-01-01T09:45:00.000Z",duration:2700,energyKcal:285,distanceMeters:5200}]}},{id:"$health.isAvailable",group:"health",value:!0},{id:"$health.requestAuthorization",group:"health",value:!0,legacy:["health"]},{id:"$health.stepCountToday",group:"health",value:ao(6840,"count"),legacy:["health.stepCountToday"]},{id:"$health.activeEnergyToday",group:"health",value:ao(420,"kcal"),legacy:["health.activeEnergyToday"]},{id:"$health.heartRateLatest",group:"health",value:ao(72,"bpm"),legacy:["health.heartRateLatest"]},{id:"$location.isAvailable",group:"location",value:!0},{id:"$location.authorizationStatus",group:"location",value:"authorizedWhenInUse"},{id:"$location.isAuthorizedForWidgetUpdates",group:"location",value:!0},{id:"$location.requestAuthorization",group:"location",value:!0,legacy:["location.authorization"]},{id:"$location.current",group:"location",value:{latitude:37.3349,longitude:-122.009,altitude:0,accuracy:10,verticalAccuracy:10,speed:-1,course:-1,timestamp:Wt,accuracyAuthorization:"full",age:0,isStale:!1},legacy:["location"]}],y1={music:{platforms:["ios"],authorization:"musicRead",notes:"Runtime 1.4. Read-only Music app playback, not every audio player. Requires an installed owner, music permission and Music library approval in native Settings. current() is asynchronous and returns provider, state, hasItem, nullable title/artist/album/duration/position and capturedAt. State may be playing while details are absent (Home Sharing). Native player access is main-thread-only: synchronous hosts prepare one snapshot before JSC waits; background previews capture on the main queue. No playback/queue mutation, scans, artwork, credential or subscription inference. No live queries in future entries/Live Activities. Main system-player API is unavailable on macOS; Web supplies editable samples, never actual device access."},services:{platforms:["ios","macos"],authorization:"serviceAccount",notes:"Runtime 1.4: status(service) and profile(service) are asynchronous. Only provider: github. Unknown providers reject with service_operation. Requires an installed owner and services permission. profile additionally requires network and api.github.com in networkDomains, a native connected GitHub account and explicit owner grant. Native Settings handles credentials; scripts cannot log in or obtain tokens. profile returns service, subject, username, displayName, publicRepositories, privateRepositories, followers and following (missing values are null). No live queries in future entries or Live Activities. Eight shared data queries; native requests also share the eight concurrent/32 total network budget, while Web uses its existing preview network budget. Web uses editable best-state samples only; it never authenticates or sends these requests."},photos:{platforms:["ios","macos"],authorization:"photosRead",notes:"Requires photos permission and explicit native photo/album selection (none by default). Limited Photos authorization is supported. items({limit:1..4,offset:0..63,maxAgeSeconds:0..86400}) returns bounded, metadata-stripped JPEG thumbnails as image data URLs. Four 768px / 128KiB thumbnails at most; private snapshots expire after 24h and are rechecked against current access, selected sources and asset revisions. Cloud-only items report unavailableCount; download them in Photos first. Future timeline/Live Activity presentations read matching snapshots only. Web uses editable sample data and photosSelection IDs; no actual library access."},reminders:{platforms:["ios","macos"],authorization:"remindersRead",notes:"Requires reminders in widget.json and full system Reminders access. Reads require native Settings selected lists. create/update/setCompleted additionally require remindersWrite and a native declared action, stable requestID, durable replay receipts and an unchanged selected list. Web writes update preview-only items/receipts; receipt identities are derived from that state, with configurable API errors. Use lists() for IDs. items() selects pending due dates or completed completion dates in a positive window of at most 31 days, up to 50 items. Undated pending reminders are excluded. remindersSelection mocks the user scope without granting permission."},calendar:{platforms:["ios","macos"],authorization:"calendarRead",notes:"Requires calendar in widget.json and full system Calendar access. The user's native Settings source selection limits metadata, events and cached snapshots; scripts cannot enlarge it. Use calendars() for permitted IDs and show setup when no sources are selected. Queries are read-only, limited to 31 days and 50 items. Preview calendarSelection simulates this scope without granting package permission."},device:{platforms:["ios","macos"],authorization:"none",notes:"Device values depend on hardware. Some legacy Mac values are placeholders; inspect the API documentation before using them as measurements."},system:{platforms:["ios","macos"],authorization:"none",notes:"System metadata, not whole-device CPU, FPS or network traffic monitoring. Hardware-specific values may be unavailable."},health:{platforms:["ios"],authorization:"healthRead",notes:"Read-only HealthKit. Request access in the app. Runtime 1.4 adds sleepSummary (up to 7 days / 256 samples, union with explicit stage-conflict precedence), activitySummary (one local Gregorian day including active-energy or move-time mode and optional goals), and workouts (up to 31 days / 50 fully-contained summaries, optional kcal/meters). These require an explicit health manifest; all share the query budget and cannot run in future/Live Activity presentations. No Health readings are persisted by the API. Authorization completion does not reveal whether individual read types were granted; empty results must not be labelled permission denied. API 1.3 readings add hasSamples: false means no readable samples, never a read-denial verdict. Existing value/unit/start/end fields remain."},location:{platforms:["ios"],authorization:"locationWhenInUse",notes:"Package 2.0 requires location permission. Request system access in the app. Widgets also require isAuthorizedForWidgetUpdates. Cache is rechecked against authorization and precision, discarded after 24 hours or revocation, and reports age/isStale."}},A1=g1.map(n=>{const e=n.id.split(".")[1],t=n.group==="music"&&e==="current"||n.group==="services"||["calendar","reminders","photos"].includes(n.group)&&!["isAvailable","authorizationStatus"].includes(e)||n.group==="health"&&!["isAvailable","authorizationStatus"].includes(e)||n.group==="location"&&["current","requestAuthorization"].includes(e),i=n.group==="health"&&["sleepSummary","activitySummary","workouts"].includes(e)||n.group==="location"&&["current","requestAuthorization"].includes(e)||["calendar","reminders","photos"].includes(n.group)&&["events","items"].includes(e);return{...n,...y1[n.group],minimumRuntimeVersion:["music","services","reminders","photos"].includes(n.group)||n.group==="health"&&["sleepSummary","activitySummary","workouts"].includes(e)?"1.4":["calendar"].includes(n.group)||["$health.authorizationStatus","$location.isAuthorizedForWidgetUpdates"].includes(n.id)?"1.3":"1.0",arguments:n.group==="services"?["service"]:n.group==="reminders"&&["create","update","setCompleted"].includes(e)?["options"]:i?["options?"]:[],...n.group==="reminders"&&["create","update","setCompleted"].includes(e)?{authorization:"remindersWrite"}:{},asynchronous:t,nativeSource:`Shared/ScriptWidgetRuntime/Widget/API/ScriptWidgetRuntime${n.group[0].toUpperCase()}${n.group.slice(1)}.swift`,operation:n.group==="reminders"&&["create","update","setCompleted"].includes(e)?"write":e==="requestAuthorization"?"authorization":["isAvailable","authorizationStatus","isAuthorizedForWidgetUpdates"].includes(e)?"availability":"read",mock:"editable"}}),Re={globalProperties:{background:{type:"color",documentation:"Background color, hex value, or named color."},backgroundMaterial:{type:"enum",values:["none","ultraThin","thin","regular","thick","ultraThick"],documentation:"Runtime API 1.4. Public SwiftUI frosted material inside the widget, not Home Screen wallpaper blur or system Liquid Glass. At most four material backgrounds per tree. background supplies an opaque-composited Reduce Transparency fallback. In accented/vibrant mode local material is omitted for system rendering; none keeps the ordinary background."},color:{type:"color",documentation:"Foreground color, hex value, or named color."},font:{type:"enum",values:["largeTitle","title","title2","title3","headline","subheadline","body","callout","footnote","caption","caption2"],documentation:"Semantic text style."},fontWeight:{type:"enum",values:["ultraLight","thin","light","regular","medium","semibold","bold","heavy","black"],documentation:"Font weight."},fontDesign:{type:"enum",values:["default","rounded","serif","monospaced"],documentation:"Font design."},frame:{type:"number|string",documentation:'Square size (64 or "64"), width,height ("64,48"), or flexible frame ("max", "max,48").'},padding:{type:"number|string",documentation:"Padding amount or edge description."},corner:{type:"number",documentation:"Corner radius."},opacity:{type:"number",documentation:"Opacity from 0 through 1."},alignment:{type:"enum",values:["center","leading","trailing","top","bottom","topLeading","topTrailing","bottomLeading","bottomTrailing","firstTextBaseline","lastTextBaseline"],documentation:"Layout alignment."},clip:{type:"enum",values:["circle","rect","capsule","ellipse"],documentation:"Clip the view to a shape."},rotation:{type:"number",documentation:"Rotation in degrees."},rotation3d:{type:"string",documentation:"Static degree,x,y,z rotation with four finite numbers and a nonzero axis. Invalid values are ignored; axis magnitude is normalized. Reduce Motion removes the tilt. This does not read device motion or provide parallax."},shadow:{type:"string",documentation:"Shadow color, radius, and offset description."},animation:{type:"string",documentation:"iOS public SwiftUI scheduled clock/swing effect. Mac renders static standard-clock poses; other effects remain at rest. Signed clockCustom intervals specify a half-turn (full cycle = 2 * abs(interval)), minimum magnitude 0.25 seconds. App previews can animate; WidgetKit decides cadence and may freeze. Reduced motion/luminance freezes standard clock poses and keeps custom/swing effects at rest. Dynamic scripts are not rerun on animation ticks."},widgetAccentable:{type:"boolean",documentation:"Group this view into the accent layer in tinted and vibrant widget appearances."},linkurl:{type:"url",documentation:"Widget deep link URL."}},components:{VStack:{documentation:"Arrange children vertically.",children:!0,properties:{spacing:{type:"number"}}},HStack:{documentation:"Arrange children horizontally.",children:!0,properties:{spacing:{type:"number"}}},ZStack:{documentation:"Overlay children.",children:!0,properties:{}},VGrid:{documentation:"Vertical lazy grid.",children:!0,properties:{columns:{type:"string",required:!0},spacing:{type:"number"},alignment:{type:"enum",values:["leading","center","trailing"]}}},HGrid:{documentation:"Horizontal lazy grid.",children:!0,properties:{columns:{type:"string",required:!0},spacing:{type:"number"},alignment:{type:"enum",values:["top","center","bottom","firstTextBaseline","lastTextBaseline"]}}},Text:{documentation:"Display text content.",children:!1,properties:{text:{type:"string"}}},Date:{documentation:"Display a formatted date.",children:!1,properties:{date:{type:"date|number",required:!0},style:{type:"enum",values:["time","date","relative","offset","timer"]}}},Image:{documentation:"Display a bundled, remote, or SF Symbol image.",children:!1,properties:{systemName:{type:"string"},name:{type:"string"},id:{type:"string",deprecated:"Use name."},url:{type:"url"},src:{type:"url",deprecated:"Use url."},ratio:{type:"number"},mode:{type:"enum",values:["fit","fill"]},accentedRenderingMode:{type:"enum",values:["accented","desaturated","accentedDesaturated","fullColor"],documentation:"Control how the image is rendered in tinted and clear widget appearances."}}},Gif:{documentation:"Display a bounded package GIF using system decoding: up to 8 MiB input and 1,048,576 canvas pixels. iOS retains at most 60 downsampled frames / 4 MiB decoded pixels; more frames fall back to the first frame. Mac, Reduce Motion and reduced luminance use a static first frame. Active iOS previews use public SwiftUI timing with variable frame delays. Snapshots, inactive scenes, Mac and accessibility fallbacks use the first frame. No private clock bridge or continuous WidgetKit guarantee.",children:!1,properties:{file:{type:"string",required:!0}}},Spacer:{documentation:"Flexible layout space.",children:!1,properties:{minLength:{type:"number"}}},Rect:{documentation:"Rectangle shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},RoundedRect:{documentation:"Filled rounded rectangle.",children:!1,properties:{radius:{type:"number"}}},Capsule:{documentation:"Capsule shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Ellipse:{documentation:"Ellipse shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Circle:{documentation:"Circle shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Gauge:{documentation:"Display a gauge or instrument.",children:!1,properties:{type:{type:"string"},value:{type:"number"},style:{type:"enum",values:["circular","linear"]},text:{type:"string"},current:{type:"string"},min:{type:"string"},max:{type:"string"},angle:{type:"number"},thickness:{type:"number"},needleColor:{type:"color"},label:{type:"string"},title:{type:"string"},sections:{type:"string"}}},Chart:{documentation:"Render chart data.",children:!1,availability:{ios:"16.0",macos:"26.0"},properties:{type:{type:"enum",values:["bar","bar-x","bar-y","bar-gantt","line","point","line-point","area","rect","rule-x"]},data:{type:"json",required:!0},category:{type:"boolean"},hideLegend:{type:"boolean"},hideXAxis:{type:"boolean"},hideYAxis:{type:"boolean"}}},Link:{documentation:"Open a URL when selected.",children:!0,properties:{url:{type:"url",required:!0}}},Divider:{documentation:"Horizontal or vertical separator.",children:!1,properties:{thickness:{type:"number"},axis:{type:"enum",values:["horizontal","vertical"]}}},Line:{documentation:"Fixed-length line.",children:!1,properties:{thickness:{type:"number"},length:{type:"number"},axis:{type:"enum",values:["horizontal","vertical"]}}},Icon:{documentation:"SF Symbol icon.",children:!1,properties:{systemName:{type:"string",required:!0},size:{type:"number"}}},Label:{documentation:"Text paired with an SF Symbol.",children:!1,properties:{title:{type:"string"},systemName:{type:"string",required:!0}}},Progress:{documentation:"Linear or circular progress.",children:!1,properties:{value:{type:"number",required:!0},total:{type:"number"},label:{type:"string"},style:{type:"enum",values:["linear","circular"]},trackColor:{type:"color"},thickness:{type:"number"}}},Ring:{documentation:"Circular progress ring.",children:!1,properties:{value:{type:"number",required:!0},thickness:{type:"number"},trackColor:{type:"color"}}},Badge:{documentation:"Compact status badge.",children:!1,properties:{text:{type:"string"},radius:{type:"number"}}},Chip:{documentation:"Outlined metadata chip.",children:!1,properties:{text:{type:"string"},radius:{type:"number"},borderColor:{type:"color"}}},Stat:{documentation:"Title, value, and subtitle statistic.",children:!1,properties:{title:{type:"string"},value:{type:"string",required:!0},subtitle:{type:"string"},mutedColor:{type:"color"}}},Button:{documentation:"Interactive widget button. Use actionID to share a declared Package 2.0 action with Siri, Shortcuts, and Control Widgets.",children:!0,availability:{interactiveIos:"17.0"},requiredAny:["action","onClick","actionID"],properties:{action:{type:"enum",values:["reload"],conflicts:["actionID"]},onClick:{type:"function",conflicts:["actionID"]},actionID:{type:"action-id",conflicts:["action","onClick"]}}},Toggle:{documentation:"Interactive widget toggle. A declared actionID requires a package-relative storage stateKey.",children:!0,availability:{interactiveIos:"17.0"},requiredAny:["onClick","actionID"],properties:{on:{type:"boolean",required:!0},onClick:{type:"function",conflicts:["actionID"]},actionID:{type:"action-id",requires:["stateKey"],conflicts:["onClick"]},stateKey:{type:"storage-key",requires:["actionID"]}}}},functions:{...Object.fromEntries(O1.map(n=>[n.id,n])),...Object.fromEntries(A1.map(n=>[n.id,{documentation:n.notes,arguments:n.arguments,returns:n.asynchronous?"Promise":typeof n.value=="object"?"json":typeof n.value}])),"$file.exists":{documentation:"Return whether a regular file is locally present inside this package, including binary assets. Missing files, directories, unsafe paths and links return false. Does not download iCloud files or read outside the package. Web preview uses workspace file presence.",arguments:["path"],returns:"boolean",minimumRuntimeVersion:"1.3"},"$file.read":{documentation:"Read package-relative UTF-8 text. Missing or unavailable files return an empty string.",arguments:["path"],returns:"string"},"$file.readJSON":{documentation:"Read a package-relative JSON object.",arguments:["path"],returns:"json"},fetch:{documentation:"Perform a network request and return its response text. Existing HTTP methods keep this string result."},"$http.getResponse":{documentation:"GET response metadata: {status, body, headers}. Headers are limited to content-type, cache-control, expires, last-modified, date and retry-after (lowercase; each at most 2048 UTF-8 bytes). Network permissions, public-host policy, redirects, body limits and timeline restrictions still apply. HTTP error statuses resolve; transport errors reject.",arguments:["url","options?"],returns:"Promise",minimumRuntimeVersion:"1.3"},importJS:{documentation:"Import a package-relative JavaScript file."},"$dataSource.request":{documentation:"Call a declared Data Source Plugin operation. Requires Package 2.0 plugins, network permission, and networkDomains declarations.",arguments:["pluginID","operationID","parameters"],returns:"Promise"},"$xml.parse":{documentation:"Parse up to 256 KiB of XML into {name, attributes, text, children}. DTDs and external entities are rejected; depth and node limits apply.",arguments:["source"],returns:"XMLNode"}}};function b1(n,e){const t=Object.entries(e.properties??{}).filter(([,r])=>r.required).map(([r,s])=>`${r}=${s.type.includes("number")||s.type==="boolean"?"{${}}":'"${}"'}`).join(" "),i=t?` ${t}`:"";return e.children?`<${n}${i}> +`:i=="r"?"\r":i=="t"?" ":"\\")}eq(e){return this.search==e.search&&this.replace==e.replace&&this.caseSensitive==e.caseSensitive&&this.regexp==e.regexp&&this.wholeWord==e.wholeWord&&this.test==e.test}create(){return this.regexp?new Lb(this):new Fb(this)}getCursor(e,t=0,i){let r=e.doc?e:_.create({doc:e});return i==null&&(i=r.doc.length),this.regexp?Pi(this,r,t,i):Qi(this,r,t,i)}}class gd{constructor(e){this.spec=e}}function Eb(n,e,t){return(i,r,s,o)=>{if(t&&!t(i,r,s,o))return!1;let l=i>=o&&r<=o+s.length?s.slice(i-o,r-o):e.doc.sliceString(i,r);return n(l,e,i,r)}}function Qi(n,e,t,i){let r;return n.wholeWord&&(r=Ub(e.doc,e.charCategorizer(e.selection.main.head))),n.test&&(r=Eb(n.test,e,r)),new Yi(e.doc,n.unquoted,t,i,n.caseSensitive?void 0:s=>s.toLowerCase(),r)}function Ub(n,e){return(t,i,r,s)=>((s>t||s+r.length=t)return null;r.push(i.value)}return r}highlight(e,t,i,r){let s=Qi(this.spec,e,Math.max(0,t-this.spec.unquoted.length),Math.min(i+this.spec.unquoted.length,e.doc.length));for(;!s.next().done;)r(s.value.from,s.value.to)}}function Db(n,e,t){return(i,r,s)=>(!t||t(i,r,s))&&n(s[0],e,i,r)}function Pi(n,e,t,i){let r;return n.wholeWord&&(r=Xb(e.charCategorizer(e.selection.main.head))),n.test&&(r=Db(n.test,e,r)),new pd(e.doc,n.search,{ignoreCase:!n.caseSensitive,test:r},t,i)}function Jr(n,e){return n.slice(Ze(n,e,!1),e)}function es(n,e){return n.slice(e,Ze(n,e))}function Xb(n){return(e,t,i)=>!i[0].length||(n(Jr(i.input,i.index))!=ye.Word||n(es(i.input,i.index))!=ye.Word)&&(n(es(i.input,i.index+i[0].length))!=ye.Word||n(Jr(i.input,i.index+i[0].length))!=ye.Word)}class Lb extends gd{nextMatch(e,t,i){let r=Pi(this.spec,e,i,e.doc.length).next();return r.done&&(r=Pi(this.spec,e,0,t).next()),r.done?null:r.value}prevMatchInRange(e,t,i){for(let r=1;;r++){let s=Math.max(t,i-r*1e4),o=Pi(this.spec,e,s,i),l=null;for(;!o.next().done;)l=o.value;if(l&&(s==t||l.from>s+10))return l;if(s==t)return null}}prevMatch(e,t,i){return this.prevMatchInRange(e,0,t)||this.prevMatchInRange(e,i,e.doc.length)}getReplacement(e){return this.spec.unquote(this.spec.replace).replace(/\$([$&]|\d+)/g,(t,i)=>{if(i=="&")return e.match[0];if(i=="$")return"$";for(let r=i.length;r>0;r--){let s=+i.slice(0,r);if(s>0&&s=t)return null;r.push(i.value)}return r}highlight(e,t,i,r){let s=Pi(this.spec,e,Math.max(0,t-250),Math.min(i+250,e.doc.length));for(;!s.next().done;)r(s.value.from,s.value.to)}}const Zn=W.define(),Gl=W.define(),Ht=Te.define({create(n){return new lo(ll(n).create(),null)},update(n,e){for(let t of e.effects)t.is(Zn)?n=new lo(t.value.create(),n.panel):t.is(Gl)&&(n=new lo(n.query,t.value?Hl:null));return n},provide:n=>ji.from(n,e=>e.panel)});class lo{constructor(e,t){this.query=e,this.panel=t}}const Bb=B.mark({class:"cm-searchMatch"}),qb=B.mark({class:"cm-searchMatch cm-searchMatch-selected"}),Ib=xe.fromClass(class{constructor(n){this.view=n,this.decorations=this.highlight(n.state.field(Ht))}update(n){let e=n.state.field(Ht);(e!=n.startState.field(Ht)||n.docChanged||n.selectionSet||n.viewportChanged)&&(this.decorations=this.highlight(e))}highlight({query:n,panel:e}){if(!e||!n.spec.valid)return B.none;let{view:t}=this,i=new Bt;for(let r=0,s=t.visibleRanges,o=s.length;rs[r+1].from-500;)a=s[++r].to;n.highlight(t.state,l,a,(c,h)=>{let u=t.state.selection.ranges.some(f=>f.from==c&&f.to==h);i.add(c,h,u?qb:Bb)})}return i.finish()}},{decorations:n=>n.decorations});function zn(n){return e=>{let t=e.state.field(Ht,!1);return t&&t.query.spec.valid?n(e,t):bd(e)}}const ts=zn((n,{query:e})=>{let{to:t}=n.state.selection.main,i=e.nextMatch(n.state,t,t);if(!i)return!1;let r=A.single(i.from,i.to),s=n.state.facet(tn);return n.dispatch({selection:r,effects:[_l(n,i),s.scrollToMatch(r.main,n)],userEvent:"select.search"}),Ad(n),!0}),is=zn((n,{query:e})=>{let{state:t}=n,{from:i}=t.selection.main,r=e.prevMatch(t,i,i);if(!r)return!1;let s=A.single(r.from,r.to),o=n.state.facet(tn);return n.dispatch({selection:s,effects:[_l(n,r),o.scrollToMatch(s.main,n)],userEvent:"select.search"}),Ad(n),!0}),zb=zn((n,{query:e})=>{let t=e.matchAll(n.state,1e3);return!t||!t.length?!1:(n.dispatch({selection:A.create(t.map(i=>A.range(i.from,i.to))),userEvent:"select.search.matches"}),!0)}),Wb=({state:n,dispatch:e})=>{let t=n.selection;if(t.ranges.length>1||t.main.empty)return!1;let{from:i,to:r}=t.main,s=[],o=0;for(let l=new Yi(n.doc,n.sliceDoc(i,r));!l.next().done;){if(s.length>1e3)return!1;l.value.from==i&&(o=s.length),s.push(A.range(l.value.from,l.value.to))}return e(n.update({selection:A.create(s,o),userEvent:"select.search.matches"})),!0},th=zn((n,{query:e})=>{let{state:t}=n,{from:i,to:r}=t.selection.main;if(t.readOnly)return!1;let s=e.nextMatch(t,i,i);if(!s)return!1;let o=s,l=[],a,c,h=[];o.precise?o.from==i&&o.to==r&&(c=t.toText(e.getReplacement(o)),l.push({from:o.from,to:o.to,insert:c}),o=e.nextMatch(t,o.from,o.to),h.push(R.announce.of(t.phrase("replaced match on line $",t.doc.lineAt(i).number)+"."))):o=e.nextMatch(t,o.from,o.to);let u=n.state.changes(l);return o&&(a=A.single(o.from,o.to).map(u),h.push(_l(n,o)),h.push(t.facet(tn).scrollToMatch(a.main,n))),n.dispatch({changes:u,selection:a,effects:h,userEvent:"input.replace"}),!0}),jb=zn((n,{query:e})=>{if(n.state.readOnly)return!1;let t=[];for(let r of e.matchAll(n.state,1e9)){let{from:s,to:o,precise:l}=r;l&&t.push({from:s,to:o,insert:e.getReplacement(r)})}if(!t.length)return!1;let i=n.state.phrase("replaced $ matches",t.length)+".";return n.dispatch({changes:t,effects:R.announce.of(i),userEvent:"input.replace.all"}),!0});function Hl(n){return n.state.facet(tn).createPanel(n)}function ll(n,e){var t,i,r,s,o;let l=n.selection.main,a=l.empty||l.to>l.from+100?"":n.sliceDoc(l.from,l.to);if(e&&!a)return e;let c=n.facet(tn);return new Od({search:((t=e?.literal)!==null&&t!==void 0?t:c.literal)?a:a.replace(/\n/g,"\\n"),caseSensitive:(i=e?.caseSensitive)!==null&&i!==void 0?i:c.caseSensitive,literal:(r=e?.literal)!==null&&r!==void 0?r:c.literal,regexp:(s=e?.regexp)!==null&&s!==void 0?s:c.regexp,wholeWord:(o=e?.wholeWord)!==null&&o!==void 0?o:c.wholeWord})}function yd(n){let e=Fu(n,Hl);return e&&e.dom.querySelector("[main-field]")}function Ad(n){let e=yd(n);e&&e==n.root.activeElement&&e.select()}const bd=n=>{let e=n.state.field(Ht,!1);if(e&&e.panel){let t=yd(n);if(t&&t!=n.root.activeElement){let i=ll(n.state,e.query.spec);i.valid&&n.dispatch({effects:Zn.of(i)}),t.focus(),t.select()}}else n.dispatch({effects:[Gl.of(!0),e?Zn.of(ll(n.state,e.query.spec)):W.appendConfig.of(Gb)]});return!0},Sd=n=>{let e=n.state.field(Ht,!1);if(!e||!e.panel)return!1;let t=Fu(n,Hl);return t&&t.dom.contains(n.root.activeElement)&&n.focus(),n.dispatch({effects:Gl.of(!1)}),!0},Vb=[{key:"Mod-f",run:bd,scope:"editor search-panel"},{key:"F3",run:ts,shift:is,scope:"editor search-panel",preventDefault:!0},{key:"Mod-g",run:ts,shift:is,scope:"editor search-panel",preventDefault:!0},{key:"Escape",run:Sd,scope:"editor search-panel"},{key:"Mod-Shift-l",run:Wb},{key:"Mod-Alt-g",run:wb},{key:"Mod-d",run:Mb,preventDefault:!0}];class Yb{constructor(e){this.view=e;let t=this.query=e.state.field(Ht).query.spec;this.commit=this.commit.bind(this),this.searchField=he("input",{value:t.search,placeholder:rt(e,"Find"),"aria-label":rt(e,"Find"),class:"cm-textfield",name:"search",form:"","main-field":"true",onchange:this.commit,onkeyup:this.commit}),this.replaceField=he("input",{value:t.replace,placeholder:rt(e,"Replace"),"aria-label":rt(e,"Replace"),class:"cm-textfield",name:"replace",form:"",onchange:this.commit,onkeyup:this.commit}),this.caseField=he("input",{type:"checkbox",name:"case",form:"",checked:t.caseSensitive,onchange:this.commit}),this.reField=he("input",{type:"checkbox",name:"re",form:"",checked:t.regexp,onchange:this.commit}),this.wordField=he("input",{type:"checkbox",name:"word",form:"",checked:t.wholeWord,onchange:this.commit});function i(r,s,o){return he("button",{class:"cm-button",name:r,onclick:s,type:"button"},o)}this.dom=he("div",{onkeydown:r=>this.keydown(r),class:"cm-search"},[this.searchField,i("next",()=>ts(e),[rt(e,"next")]),i("prev",()=>is(e),[rt(e,"previous")]),i("select",()=>zb(e),[rt(e,"all")]),he("label",null,[this.caseField,rt(e,"match case")]),he("label",null,[this.reField,rt(e,"regexp")]),he("label",null,[this.wordField,rt(e,"by word")]),...e.state.readOnly?[]:[he("br"),this.replaceField,i("replace",()=>th(e),[rt(e,"replace")]),i("replaceAll",()=>jb(e),[rt(e,"replace all")])],he("button",{name:"close",onclick:()=>Sd(e),"aria-label":rt(e,"close"),type:"button"},["×"])])}commit(){let e=new Od({search:this.searchField.value,caseSensitive:this.caseField.checked,regexp:this.reField.checked,wholeWord:this.wordField.checked,replace:this.replaceField.value});e.eq(this.query)||(this.query=e,this.view.dispatch({effects:Zn.of(e)}))}keydown(e){rO(this.view,e,"search-panel")?e.preventDefault():e.keyCode==13&&e.target==this.searchField?(e.preventDefault(),(e.shiftKey?is:ts)(this.view)):e.keyCode==13&&e.target==this.replaceField&&(e.preventDefault(),th(this.view))}update(e){for(let t of e.transactions)for(let i of t.effects)i.is(Zn)&&!i.value.eq(this.query)&&this.setQuery(i.value)}setQuery(e){this.query=e,this.searchField.value=e.search,this.replaceField.value=e.replace,this.caseField.checked=e.caseSensitive,this.reField.checked=e.regexp,this.wordField.checked=e.wholeWord}mount(){this.searchField.select()}get pos(){return 80}get top(){return this.view.state.facet(tn).top}}function rt(n,e){return n.state.phrase(e)}const fr=30,dr=/[\s\.,:;?!]/;function _l(n,{from:e,to:t}){let i=n.state.doc.lineAt(e),r=n.state.doc.lineAt(t).to,s=Math.max(i.from,e-fr),o=Math.min(r,t+fr),l=n.state.sliceDoc(s,o);if(s!=i.from){for(let a=0;al.length-fr;a--)if(!dr.test(l[a-1])&&dr.test(l[a])){l=l.slice(0,a);break}}return R.announce.of(`${n.state.phrase("current match")}. ${l} ${n.state.phrase("on line")} ${i.number}.`)}const Nb=R.baseTheme({".cm-panel.cm-search":{padding:"2px 6px 4px",position:"relative","& [name=close]":{position:"absolute",top:"0",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",padding:0,margin:0},"& input, & button, & label":{margin:".2em .6em .2em 0"},"& input[type=checkbox]":{marginRight:".2em"},"& label":{fontSize:"80%",whiteSpace:"pre"}},"&light .cm-searchMatch":{backgroundColor:"#ffff0054"},"&dark .cm-searchMatch":{backgroundColor:"#00ffff8a"},"&light .cm-searchMatch-selected":{backgroundColor:"#ff6a0054"},"&dark .cm-searchMatch-selected":{backgroundColor:"#ff00ff8a"}}),Gb=[Ht,si.low(Ib),Nb];class ih{constructor(e,t,i){this.from=e,this.to=t,this.diagnostic=i}}class di{constructor(e,t,i){this.diagnostics=e,this.panel=t,this.selected=i}static init(e,t,i){let r=i.facet(Kt).markerFilter;r&&(e=r(e,i));let s=e.slice().sort((d,p)=>d.from-p.from||d.to-p.to),o=new Bt,l=[],a=0,c=i.doc.iter(),h=0,u=i.doc.length;for(let d=0;;){let p=d==s.length?null:s[d];if(!p&&!l.length)break;let m,O;if(l.length)m=a,O=l.reduce((x,C)=>Math.min(x,C.to),p&&p.from>m?p.from:1e8);else{if(m=p.from,m>u)break;O=p.to,l.push(p),d++}for(;dx.from||x.to==m))l.push(x),d++,O=Math.min(x.to,O);else{O=Math.min(x.from,O);break}}O=Math.min(O,u);let y=!1;if(l.some(x=>x.from==m&&(x.to==O||O==u))&&(y=m==O,!y&&O-m<10)){let x=m-(h+c.value.length);x>0&&(c.next(x),h=m);for(let C=m;;){if(C>=O){y=!0;break}if(!c.lineBreak&&h+c.value.length>C)break;C=h+c.value.length,h+=c.value.length,c.next()}}let b=Td(l);if(y)o.add(m,m,B.widget({widget:new n1(b),diagnostics:l.slice()}));else{let x=l.reduce((C,k)=>k.markClass?C+" "+k.markClass:C,"");o.add(m,O,B.mark({class:"cm-lintRange cm-lintRange-"+b+x,diagnostics:l.slice(),inclusiveEnd:l.some(C=>C.to>O)}))}if(a=O,a==u)break;for(let x=0;x{if(!(e&&o.diagnostics.indexOf(e)<0))if(!i)i=new ih(r,s,e||o.diagnostics[0]);else{if(o.diagnostics.indexOf(i.diagnostic)<0)return!1;i=new ih(i.from,s,i.diagnostic)}}),i}function xd(n,e){let t=e.pos,i=e.end||t,r=n.state.facet(Kt).hideOn(n,t,i);if(r!=null)return r;let s=n.startState.doc.lineAt(e.pos);return!!(n.effects.some(o=>o.is(Qs))||n.changes.touchesRange(s.from,Math.max(s.to,i)))}function Hb(n,e){return n.field(Ot,!1)?e:e.concat(W.appendConfig.of(Kd))}function wd(n,e){return{effects:Hb(n,[Qs.of(e)])}}const Qs=W.define(),vd=W.define(),kd=W.define(),Ot=Te.define({create(){return new di(B.none,null,null)},update(n,e){if(e.docChanged&&n.diagnostics.size){let t=n.diagnostics.map(e.changes),i=null,r=n.panel;if(n.selected){let s=e.changes.mapPos(n.selected.from,1);i=Ni(t,n.selected.diagnostic,s)||Ni(t,null,s)}!t.size&&r&&e.state.facet(Kt).autoPanel&&(r=null),n=new di(t,r,i)}for(let t of e.effects)if(t.is(Qs)){let i=e.state.facet(Kt).autoPanel?t.value.length?ns.open:null:n.panel;n=di.init(t.value,i,e.state)}else t.is(vd)?n=new di(n.diagnostics,t.value?ns.open:null,n.selected):t.is(kd)&&(n=new di(n.diagnostics,n.panel,t.value));return n},provide:n=>[ji.from(n,e=>e.panel),R.decorations.from(n,e=>e.diagnostics)]}),_b=B.mark({class:"cm-lintRange cm-lintRange-active"});function Jb(n,e,t){let{diagnostics:i}=n.state.field(Ot),r,s=-1,o=-1;i.between(e-(t<0?1:0),e+(t>0?1:0),(a,c,{spec:h})=>{if(e>=a&&e<=c&&(a==c||(e>a||t>0)&&(eCd(n,t,!1)))}const nh=n=>{let e=n.state.field(Ot,!1);return!e||!e.panel?!1:(n.dispatch({effects:vd.of(!1)}),!0)},e1=xe.fromClass(class{constructor(n){this.view=n,this.timeout=-1,this.set=!0;let{delay:e}=n.state.facet(Kt);this.lintTime=Date.now()+e,this.run=this.run.bind(this),this.timeout=setTimeout(this.run,e)}run(){clearTimeout(this.timeout);let n=Date.now();if(nPromise.resolve(i(this.view))),i=>{this.view.state.doc==e.doc&&this.view.dispatch(wd(this.view.state,i.reduce((r,s)=>r.concat(s))))},i=>{We(this.view.state,i)})}}update(n){let e=n.state.facet(Kt);(n.docChanged||e!=n.startState.facet(Kt)||e.needsRefresh&&e.needsRefresh(n))&&(this.lintTime=Date.now()+e.delay,this.set||(this.set=!0,this.timeout=setTimeout(this.run,e.delay)))}force(){this.set&&(this.lintTime=Date.now(),this.run())}destroy(){clearTimeout(this.timeout)}});function t1(n,e,t){let i=[],r=-1;for(let s of n)s.then(o=>{i.push(o),clearTimeout(r),i.length==n.length?e(i):r=setTimeout(()=>e(i),200)},t)}const Kt=K.define({combine(n){return{sources:n.map(e=>e.source).filter(e=>e!=null),...yt(n.map(e=>e.config),{delay:750,markerFilter:null,tooltipFilter:null,needsRefresh:null,hideOn:()=>null},{delay:Math.max,markerFilter:rh,tooltipFilter:rh,needsRefresh:(e,t)=>e?t?i=>e(i)||t(i):e:t,hideOn:(e,t)=>e?t?(i,r,s)=>e(i,r,s)||t(i,r,s):e:t,autoPanel:(e,t)=>e||t})}}});function rh(n,e){return n?e?(t,i)=>e(n(t,i),i):n:e}function i1(n,e={}){return[Kt.of({source:n,config:e}),e1,Kd]}function Pd(n){let e=[];if(n)e:for(let{name:t}of n){for(let i=0;is.toLowerCase()==r.toLowerCase())){e.push(r);continue e}}e.push("")}return e}function Cd(n,e,t){var i;let r=t?Pd(e.actions):[];return he("li",{class:"cm-diagnostic cm-diagnostic-"+e.severity},he("span",{class:"cm-diagnosticText"},e.renderMessage?e.renderMessage(n):e.message),(i=e.actions)===null||i===void 0?void 0:i.map((s,o)=>{let l=!1,a=d=>{if(d.preventDefault(),l)return;l=!0;let p=Ni(n.state.field(Ot).diagnostics,e);p&&s.apply(n,p.from,p.to)},{name:c}=s,h=r[o]?c.indexOf(r[o]):-1,u=h<0?c:[c.slice(0,h),he("u",c.slice(h,h+1)),c.slice(h+1)],f=s.markClass?" "+s.markClass:"";return he("button",{type:"button",class:"cm-diagnosticAction"+f,onclick:a,onmousedown:a,"aria-label":` Action: ${c}${h<0?"":` (access key "${r[o]})"`}.`},u)}),e.source&&he("div",{class:"cm-diagnosticSource"},e.source))}class n1 extends Ut{constructor(e){super(),this.sev=e}eq(e){return e.sev==this.sev}toDOM(){return he("span",{class:"cm-lintPoint cm-lintPoint-"+this.sev})}}class sh{constructor(e,t){this.diagnostic=t,this.id="item_"+Math.floor(Math.random()*4294967295).toString(16),this.dom=Cd(e,t,!0),this.dom.id=this.id,this.dom.setAttribute("role","option")}}class ns{constructor(e){this.view=e,this.items=[];let t=r=>{if(!(r.ctrlKey||r.altKey||r.metaKey)){if(r.keyCode==27)nh(this.view),this.view.focus();else if(r.keyCode==38||r.keyCode==33)this.moveSelection((this.selectedIndex-1+this.items.length)%this.items.length);else if(r.keyCode==40||r.keyCode==34)this.moveSelection((this.selectedIndex+1)%this.items.length);else if(r.keyCode==36)this.moveSelection(0);else if(r.keyCode==35)this.moveSelection(this.items.length-1);else if(r.keyCode==13)this.view.focus();else if(r.keyCode>=65&&r.keyCode<=90&&this.selectedIndex>=0){let{diagnostic:s}=this.items[this.selectedIndex],o=Pd(s.actions);for(let l=0;l{for(let s=0;snh(this.view)},"×")),this.update()}get selectedIndex(){let e=this.view.state.field(Ot).selected;if(!e)return-1;for(let t=0;t{for(let h of c.diagnostics){if(o.has(h))continue;o.add(h);let u=-1,f;for(let d=i;di&&(this.items.splice(i,u-i),r=!0)),t&&f.diagnostic==t.diagnostic?f.dom.hasAttribute("aria-selected")||(f.dom.setAttribute("aria-selected","true"),s=f):f.dom.hasAttribute("aria-selected")&&f.dom.removeAttribute("aria-selected"),i++}});i({sel:s.dom.getBoundingClientRect(),panel:this.list.getBoundingClientRect()}),write:({sel:l,panel:a})=>{let c=a.height/this.list.offsetHeight;l.topa.bottom&&(this.list.scrollTop+=(l.bottom-a.bottom)/c)}})):this.selectedIndex<0&&this.list.removeAttribute("aria-activedescendant"),r&&this.sync()}sync(){let e=this.list.firstChild;function t(){let i=e;e=i.nextSibling,i.remove()}for(let i of this.items)if(i.dom.parentNode==this.list){for(;e!=i.dom;)t();e=i.dom.nextSibling}else this.list.insertBefore(i.dom,e);for(;e;)t()}moveSelection(e){if(this.selectedIndex<0)return;let t=this.view.state.field(Ot),i=Ni(t.diagnostics,this.items[e].diagnostic);i&&this.view.dispatch({selection:{anchor:i.from,head:i.to},scrollIntoView:!0,effects:kd.of(i)})}static open(e){return new ns(e)}}function Qr(n,e='viewBox="0 0 40 40"'){return`url('data:image/svg+xml,${encodeURIComponent(n)}')`}function pr(n){return Qr(``,'width="6" height="3"')}const r1=R.baseTheme({".cm-diagnostic":{padding:"3px 6px 3px 8px",marginLeft:"-1px",display:"block",whiteSpace:"pre-wrap"},".cm-diagnostic-error":{borderLeft:"5px solid #d11"},".cm-diagnostic-warning":{borderLeft:"5px solid orange"},".cm-diagnostic-info":{borderLeft:"5px solid #999"},".cm-diagnostic-hint":{borderLeft:"5px solid #66d"},".cm-diagnosticAction":{font:"inherit",border:"none",padding:"2px 4px",backgroundColor:"#444",color:"white",borderRadius:"3px",marginLeft:"8px",cursor:"pointer"},".cm-diagnosticSource":{fontSize:"70%",opacity:.7},".cm-lintRange":{backgroundPosition:"left bottom",backgroundRepeat:"repeat-x",paddingBottom:"0.7px"},".cm-lintRange-error":{backgroundImage:pr("#f11")},".cm-lintRange-warning":{backgroundImage:pr("orange")},".cm-lintRange-info":{backgroundImage:pr("#999")},".cm-lintRange-hint":{backgroundImage:pr("#66d")},".cm-lintRange-active":{backgroundColor:"#ffdd9980"},".cm-tooltip-lint":{padding:0,margin:0},".cm-lintPoint":{position:"relative","&:after":{content:'""',position:"absolute",bottom:0,left:"-2px",borderLeft:"3px solid transparent",borderRight:"3px solid transparent",borderBottom:"4px solid #d11"}},".cm-lintPoint-warning":{"&:after":{borderBottomColor:"orange"}},".cm-lintPoint-info":{"&:after":{borderBottomColor:"#999"}},".cm-lintPoint-hint":{"&:after":{borderBottomColor:"#66d"}},".cm-panel.cm-panel-lint":{position:"relative","& ul":{maxHeight:"100px",overflowY:"auto","& [aria-selected]":{backgroundColor:"#ddd","& u":{textDecoration:"underline"}},"&:focus [aria-selected]":{background_fallback:"#bdf",backgroundColor:"Highlight",color_fallback:"white",color:"HighlightText"},"& u":{textDecoration:"none"},padding:0,margin:0},"& [name=close]":{position:"absolute",top:"0",right:"2px",background:"inherit",border:"none",font:"inherit",padding:0,margin:0}},"&dark .cm-lintRange-active":{backgroundColor:"#86714a80"},"&dark .cm-panel.cm-panel-lint ul":{"& [aria-selected]":{backgroundColor:"#2e343e"}}});function s1(n){return n=="error"?4:n=="warning"?3:n=="info"?2:1}function Td(n){let e="hint",t=1;for(let i of n){let r=s1(i.severity);r>t&&(t=r,e=i.severity)}return e}class $d extends Mt{constructor(e){super(),this.diagnostics=e,this.severity=Td(e)}toDOM(e){let t=document.createElement("div");t.className="cm-lint-marker cm-lint-marker-"+this.severity;let i=this.diagnostics,r=e.state.facet(Ps).tooltipFilter;return r&&(i=r(i,e.state)),i.length&&(t.onmouseover=()=>l1(e,t,i)),t}}function o1(n,e){let t=i=>{let r=e.getBoundingClientRect();if(!(i.clientX>r.left-10&&i.clientXr.top-10&&i.clientYe.getBoundingClientRect()}}})}),e.onmouseout=e.onmousemove=null,o1(n,e)}let{hoverTime:r}=n.state.facet(Ps),s=setTimeout(i,r);e.onmouseout=()=>{clearTimeout(s),e.onmouseout=e.onmousemove=null},e.onmousemove=()=>{clearTimeout(s),s=setTimeout(i,r)}}function a1(n,e){let t=Object.create(null);for(let r of e){let s=n.lineAt(r.from);(t[s.from]||(t[s.from]=[])).push(r)}let i=[];for(let r in t)i.push(new $d(t[r]).range(+r));return N.of(i,!0)}const c1=Bu({class:"cm-gutter-lint",markers:n=>n.state.field(al),widgetMarker:(n,e,t)=>{let i=[];return n.state.field(al).between(t.from,t.to,(r,s,o)=>{r>t.from&&ri.is(Jl)?i.value:t,n)},provide:n=>Os.from(n)}),h1=R.baseTheme({".cm-gutter-lint":{width:"1.4em","& .cm-gutterElement":{padding:".2em"}},".cm-lint-marker":{width:"1em",height:"1em"},".cm-lint-marker-info":{content:Qr('')},".cm-lint-marker-warning":{content:Qr('')},".cm-lint-marker-error":{content:Qr('')}}),u1=Eu(Jb,{hideOn:xd}),Kd=[Ot,R.decorations.compute([Ot],n=>{let{selected:e,panel:t}=n.field(Ot);return!e||!t||e.from==e.to?B.none:B.set([_b.range(e.from,e.to)])}),u1,r1],Ps=K.define({combine(n){return yt(n,{hoverTime:300,markerFilter:null,tooltipFilter:null})}});function f1(n={}){return[Ps.of(n),al,c1,h1,Rd]}const Zd=1,Ue=Object.freeze({ready:"studio.ready",documentOpen:"document.open",documentReplace:"document.replace",documentChanged:"document.changed",documentSave:"document.save",documentSetReadOnly:"document.setReadOnly",documentResources:"document.resources",editorInsert:"editor.insert",editorFormat:"editor.format",editorSetTheme:"editor.setTheme",editorGetState:"editor.getState",diagnosticsPublish:"diagnostics.publish"});function d1(n,e={},t=null,i=null){return{protocolVersion:Zd,type:n,documentID:t,...i===null?{}:{sessionID:i},payload:e}}function p1(n){if(window.WKWebViewJavascriptBridge){n(window.WKWebViewJavascriptBridge);return}if(window.WKWVJBCallbacks){window.WKWVJBCallbacks.push(n);return}window.webkit?.messageHandlers?.iOS_Native_InjectJavascript&&(window.WKWVJBCallbacks=[n],window.webkit.messageHandlers.iOS_Native_InjectJavascript.postMessage(null))}function nn(n,e,t={},i=null,r=()=>{},s=null){if(!n){r({result:"unavailable"});return}n.callHandler(e,d1(e,t,i,s),r)}function m1(n){const e={capabilities:["jsx","format","search","completion","diagnostics"]};nn(n,Ue.ready,e)}const O1=[{id:"$storage.getString",arguments:["key"],returns:"string",documentation:"Read a package-scoped string. Missing keys return an empty string."},{id:"$storage.setString",arguments:["key","value"],returns:"boolean",documentation:"Set a package-scoped string, limited to 256 KiB of UTF-8. Check the success result."},{id:"$storage.getJSON",arguments:["key"],returns:"json",documentation:"Read a package-scoped JSON object. Missing or invalid values return an empty object."},{id:"$storage.setJSON",arguments:["key","value"],returns:"boolean",documentation:"Store a valid JSON object, limited to 256 KiB encoded. Check the success result."},{id:"$storage.compareAndSetString",arguments:["key","expected","value"],returns:"boolean",minimumRuntimeVersion:"1.3",documentation:"Atomically replace a string only when it still matches expected. A missing key matches an empty string. Conflicts return false; reread and retry a bounded number of times. Requires the shared storage container."},{id:"$storage.compareAndSetJSON",arguments:["key","expected","value"],returns:"boolean",minimumRuntimeVersion:"1.3",documentation:"Atomically replace a JSON object if its current value equals expected, independent of property order. A missing key matches an empty object. Wrong types and conflicts return false. Requires the shared storage container."},{id:"$storage.remove",arguments:["key"],returns:"boolean",documentation:"Remove a key from this package only."},{id:"$storage.keys",arguments:[],returns:"string[]",documentation:"List keys in this package's namespace."},{id:"$storage.clear",arguments:[],returns:"boolean",documentation:"Clear this package's storage. Other packages are unaffected."}].map(n=>({minimumRuntimeVersion:"1.0",...n,preview:"initialState"})),cl="2026-01-01T00:00:00Z",Wt="2026-01-01T12:00:00Z",ao=(n,e)=>({value:n,unit:e,hasSamples:!0,start:cl,end:Wt}),oh=(n,e,t)=>Object.entries(e).map(([i,r])=>({id:`$${n}.${i}`,group:n,value:r,legacy:[t,i]})),g1=[{id:"$music.isAvailable",group:"music",value:!0},{id:"$music.authorizationStatus",group:"music",value:"authorized"},{id:"$music.current",group:"music",value:{provider:"appleMusic",state:"playing",hasItem:!0,title:"Morning Light",artist:"ScriptWidget Sessions",album:"Focus",duration:224,position:86,capturedAt:Wt}},{id:"$services.status",group:"services",value:"connected"},{id:"$services.profile",group:"services",value:{service:"github",subject:"42",username:"octocat",displayName:"Mona",publicRepositories:32,privateRepositories:8,followers:128,following:24}},...oh("device",{screen:{width:390,height:844,scale:3},language:"en",model:"iPhone",name:"My iPhone",battery:{level:.8,state:"unplugged"},systemVersion:"26.0",isdarkmode:!1,totalDiskSpace:128e9,freeDiskSpace:64e9},"device"),...oh("system",{appInfo:{name:"ScriptWidget",bundleId:"com.everettjf.scriptwidget",version:"27.1.2",build:"27012"},locale:"en_US",preferredLanguages:["en-US"],timeZone:{identifier:"UTC",abbreviation:"GMT",offsetSeconds:0},is24HourClock:!1,calendarInfo:{identifier:"gregorian",firstWeekday:1,minimumDaysInFirstWeek:1},systemUptime:3600,memory:{physical:8589934592},thermalState:"nominal",lowPowerMode:!1,brightness:.5,reduceMotionEnabled:!1,platform:"ios",hostName:"My iPhone",processName:"ScriptWidgetWidget",osVersionString:"Version 26.0",processorCount:6,activeProcessorCount:6},"system"),{id:"$photos.isAvailable",group:"photos",value:!0},{id:"$photos.authorizationStatus",group:"photos",value:"authorized"},{id:"$photos.requestAuthorization",group:"photos",value:!0},{id:"$photos.items",group:"photos",value:{status:"ready",items:[{id:"sample.photo",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/biiiiu08cKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/biiiiu08cKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKK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8ZPqjNpumMVthwzdC/8A9j/OnGNzOpUUFdkvjHxm2oFtL0psQdHcdX9h/s/z+leb0UVslY86c3N3YUUUUyAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvePgx8bNT+Gl8NN1Hdc6PM2ZIurRE9Xjz39V6H2PNeD0UjSlWlSkpwdmftNo+saZr+mQ6xo0y3FtcLvjkQ5BH9COhB5B4NaVflf8IPjJrHww1LyZN1zpU7Znt88g9N8eejDuOjDg9iP038P+INH8UaRDrmhTrcWs67kdf1BHUEdCDyDUNWPrsFjo4mPZ9Uf//S+tKKKK6z8/CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACmSSRwxtLKwVVGSTwABSTTRW8TTzsERBkseABXhHi3xfLrchsrIlLVT9C5Hc+3oPz9nGNzOpVUFdkvi/xjJrDmw08lbVTyehkI7n29B+ftwNFFbJWPNnNyd2FFFFMkKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvWvhP8W9b+F+r+bBm40+dh9otieG7bk/uuB379D2x5LRSLp1JU5KcHZo//0/rSiiius/PwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACobi4gtYWublgiIMsx6AU26ureyt3urpwkaDJY14H4q8V3GvzeTDmO1Q/Kvdvdv8O1OMbmVWqoIk8WeLZtdlNrbZS1Q8Du59T/AEFcXRRWyVjzpScndhRRRTJCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9T60ooorrPz8KKKKACiiigAooooAKKKKACiiigAooooAKKKKACq15eW2n2z3d44SNBkk0y/v7XTbV7y9cJGg5J/kPU18/eJfE114hucnKW6H5I/6n1P8qqMbmVWqoLzJPFHim58QXGxcx2yH5E9fdvf+VcpRRWqVjzpScndhRRRTJCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9X60ooorrPz8KKKKACiiigAooooAKKKKACiiigAooooAKo6jqNppVo17etsRfzJ9B6mm6pqlnpFm17ettReg7k+gHrXz34h8Q3niC786c7Y1+5GOij+p9TVRjcxq1lBeY/xH4ku/EN1vk+SFD8kfYe59TXOUUVqedKTbuwooopiCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9b60oqRYZX+4pP0FWV03UX+5byH6Kf8K6z4CzKVFay6Drj/AHLKc/SNv8KsJ4V8Tv8Ac026P0hf/CgfJLsYNFdOngvxc/TTbn8Y2H8xVhPAHjN+mnTfiMfzoH7KfZnIUV3C/Dbxu/TT3/FkH82qyvws8dt/y44+skf/AMVQV7Cp/K/uPPqK9JT4S+OG62yL9ZE/oTVlfg94zbqkK/WQf0BosP6vU/lZ5bRXra/Bjxc3V7Zfq7f0U1YX4JeKD9+4tR/wJz/7JRYf1Wr/ACnjlZmravZaLZte3rYUcADqx9BXpXjnwZH8PNCfXPEeoQKv3Y4k3GSV/wC6gIH4noB1r4t1vXL3Xrw3V2cAcIg6KPQf1PeqjG5y4mTo+7Jaj9e1+91+8NzcnCDhEHRR/j6msOiitTy223dhRRRTEFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/Z",width:384,height:256},{id:"sample.photo.2",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/ayiiivVAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9D9rKKKK9UAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoooo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AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApGZUUu5wBySe1NkkSJDJIQqqMkngADua+f8Axv46fWGbStJYrag4Zuhk/wDsf51dOm5uyJbsT+OPHbamX0jR2xbDh5B1k9h/s/z+leV0UV6UIKKsjJu4UUUVYgooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvoL4IfHTVfhdfjTNS3XWiztmSHq0RPWSPPf1Xo3sea+faKzq0o1IuE1oVGbi+aJ+5Gi61pXiLS4Na0SdLm1uF3xyIcgj+hHQg8g8HmtSvyU+DHxs1r4Vap5Em660i4bM9tnkHp5keeA47jow4PYj9UPDviPRfFmjQa/4fnW5tbhdyOv6gjqCDwQeQa+ZxeElQl5Hq0ayqLzP//S+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACo5ZYoI2mmYIiDLMTgADuaSeeG2ha4uGCIgyzHgACvnbxp42m1+Q2NgSlmp+hkI7n29B+J9tKVJzdkTKViXxt44k1x203TSUtFPJ6GQjuf9n0H4n284oor0oxUVZGLdwoooqgCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAr2H4QfGLXvhTrPm2+bjTZ2H2m1J4btvT+64Hfv0PbHj1FROEZxcZrQcZOLuj//T+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKgubm3s7d7q6cRxxjLMegFNu7y2sLZ7y8cRxxjLMe1fN/jDxlc+JJ/IgzHaIflTux/vN7+g7VpSpObJlKxL4y8aXHiKY2lpmOzQ8L0Lkd2/oK4OiivSjFRVkYt3CiiiqAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//1PqCiiivszygooooAKKKKACiiigAooooAKKKKACiiigAooooAKqX19aabave3riOKMZJP+etN1HUbPSrN76/cRxoOSf5D1J9K+afFfiy88TXeTmO2Q/u4/6t6n+XataVJzfkTKViXxd4vu/EtzsTMdrGfkj9f9pvf+VcbRRXoxioqyMW7hRRRVAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH//V+oKKKK+zPKCiiigAooooAKKKKACiiigAooooAKKKKACs/VNUstHsnv799kafmT2AHcmmavq9jolk1/qD7UXoO7HsAO5NfM3iXxNfeJb3z7j5Il/1cY6KP6k9zW1Ki5vyJlKxL4o8VXvia78yX5IEP7uMdB7n1PvXLUUV6EYpKyMW7hRRRVAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//1vqCipFhlf7ik/QVZTTdRk+5byH6IT/SvsrnlWKVFay6Drr/AHLKc/SNv8Ksp4U8UP8Ac026P0hf/ClzLuOzMCiuoXwT4vfpplz+MbD+Yqynw/8AGj9NNm/EY/nS549x8j7HHUV3K/DXxy/TT3/FkH82qyvwq8ev/wAuGPrLEP8A2al7WHdByS7HntFelp8I/HLdbZF+sif0NWU+DnjRuqQr9ZB/QGl7aH8w/Zy7HldFeur8FfGDdXtl+rt/Rasr8D/FR+/c2o/4E5/9kpe3h3H7KXY8ZrJ1rWrDQbFr+/bCjhVHVj6AetemePvB0Xw20B9f8TajAq/diiTcZJX/ALqAgZ9z0A5NfCviDxBf+Ir43l6cAcIg+6g9B/U966sPBVfeWxlUvDRkniPxHf8AiS9N1dnai8Rxjoo/x9TXP0UV6aSSsjAKKKKYBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH//Z",width:384,height:256},{id:"sample.photo.3",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//R/byiiiu09MKKKKACiiigAooooAKKKKACiiigAooooAKKrXl5a6fave3sgiijG5mY4AFOtbq3vbZLu0cSRSAMrLyCDTt1AnooopAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/9L9vKKKK7T0wooooAKKKKACiiigAooooAKKKKACs/VNUsNGsX1HUpBFDGMkn+Q9Sewqtruvab4c099S1STYi8AfxMeyqO5NfInjDxnqfi++865PlwIf3UIPCj1Pq3qa3o0XUfkXCHMX/HHjy/8AF115SZhsoz+7iz1/2m9T+g/U6/w38fv4buRpWqMTYSnr18pj3H+ye4/Ed8+U0V6DpR5eS2h0cqtY/QJHSVBJGQysMgjkEHuKdXzT8MPiF/Zjp4c1t/8ARmOIZGP+rJ/hP+ye3p9On0tXmVKbg7M5pR5XYKKKKzJCiiigAooooAKKKKACiiigAooooAKKKKACiiigApkkiRIZJDtUckmn1xXiK/aSb7DGflTlvc//AFqmcuVXPJzrNYZbhpYiau9ku7C/8RTSMY7H5F/vHqf8K52SeaY5ldmPuc1FRXHKTe5+LY/NsVjpOeIm35dF6IljnmhOYnZT7HFdFYeIpo2Ed986/wB4dR/jXMUURk1sGAzbFYGSnh5teXR+qPWY5ElQSRncp5BFPrivDt+0c32GQ/K/K+x/+vXa12QlzK5+05LmsMyw0cRBWezXZhRRRVHrBRRRQAUUUUAf/9P9vKKKK7T0wooooAKKKKACiiigAooooAK5vxP4p0vwpp5vtRbLHiOMfec+g/qe1U/GHjPTfCFj51yfMuHH7qEHlj6n0X1NfIeu69qXiPUH1LVJN8jcAfwqOyqOwH+ea6KNBz1expCnfVlvxP4o1TxXqBvtRbAHEcY+6i+g/qe9c5RRXpJJKyOlK2wUUUUwCvoj4XfEPzRH4Y1yT5x8tvK3f0Rj6/3T36elfO9KCQcjgioqU1NWZMoqSsz9A6K8c+GfxBGtxLoWsv8A6ZGP3bn/AJaqP/Zh39Rz617HXlTg4PlZyyjZ2YUUUVAgooooAKKKKACiiigAooooAKKKKACiiigAryieQzTPKerMT+der15RPGYZniPVWI/KsK/Q/O/EDm5KFtry+/S36kVFFFcx+ZhRRRQBLBIYZklHVWB/KvV68ogjM0yRDqzAfnXq9dNDqfpnh/zcle+14/frf9Aooorc/RAooooAKKKKAP/U/byiiiu09MKKKKACiiigAooooAK4Dxx49sPCNr5SYmvZB+7i9P8Aaf0H6n8zWd4++Itr4XibTtOIlv2HTqsYPdvf0H4njr8qXl5dahcve3shllkO5mY5JNdVDD83vS2NYU76sn1TVL/Wb59R1KQyzSHJY/yHoB2FZ9FFeilbY6AooooAKKKKACiiigCSGaW3lWeBijoQysDggjoQa+s/h349i8U2n2G/IW/hX5h08xR/EP6j+lfJFW7G+u9MvI7+xcxzRHcrDqDWVWkqisROPMj75orhvA3jW08X6duOI7uIATR/+zL/ALJ/TpXc15couLszlatowoooqQCiiigAooooAKKKKACiiigAoor5v+OXx2svh5av4f8ADzLPrUq/VbdSOGf1b+6v4njAOlOnKpLlgioU3N8sS58bfjlp/wANrJtH0crca1MvyJ1WEHo8nv8A3V79Tx14j4Z/GOx+IsK2eq7bfWEX94g4WbA++nv/AHl7dRx0+Ab+/vdUvZdR1GVp552LySOcszHkkk0y0u7mxuY7yykaKWJgyOpwykdCCK9eeWQlS5Hv3FnPDtLMsI8LN2e6fZ/5H6wUV4f8HfixB47iXw/rDLFq6D5QcKLgDunbeO6/iOMge6yQTQnEqMp9xivmq1CdGThNH4HmeS4vLqjpYqm1br0foyKipY4JpjiJGY+wzXRWHh2aRhJffIv90dT/AIVnGLexngMpxWOkoYeDfn0Xqw8O2DSTfbpB8qcL7n/61drTI40iQRxjao4AFPrshHlVj9pyXKoZbho4eDu92+7CiiiqPWCiiigAooooA//V/byiiiu09MKKKKACiiigArxz4h/EuLQw+jaEwe8PDydVi/xb+Xf0rK+InxPFr5mheGpMy8rLOv8AD6qh9fU9u3PT50JLEsxyTXbQw9/embU6fVj5ZZZ5WmnYu7klmY5JJ6kmo6KK7jcKKKKACiiigAooooAKKKKACiiigDU0bWL/AEHUY9U019ksZ/AjuCO4Pevsjwl4qsPFulrf2nyyLxLGTyjf4Hse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",width:384,height:256},{id:"sample.photo.4",image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBARXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAABgKADAAQAAAABAAABAAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgBAAGAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICBAICBAYEBAQGCAYGBgYICggICAgICgwKCgoKCgoMDAwMDAwMDA4ODg4ODhAQEBAQEhISEhISEhISEv/bAEMBAwMDBQQFCAQECBMNCw0TExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE//dAAQAGP/aAAwDAQACEQMRAD8A/biiiiuc9UKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Q/biiiiuc9UKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKK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0ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigApGZUUu5wBySaR3SNDJIQqqMkngACvBvGnjZ9WZtM0titqDhm6GT/7H+dJuxrSpOo7Im8a+OG1ItpOkNi36O46v7D/Z/n9K8woorNu560IKC5YhRRRSLCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK98+Cvxw1T4ZXw0zUd1zo07Zki6tET1ePPf1XofrzXgdFMyq0o1YuE1oftro2s6X4h0yHWdFnW5tbhd8ciHII/oR0IPIPBrTr8o/g58Z9Z+Fup+TJuudJnbM9vnkHp5keeA47jow4PYj9QvD3iHRvFWjw69oE63NrcLuR1/UEdQQeCDyDTTPlMbgpYeXddGf/9L60ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACmSyxwxtNMwVFGSTwAB3NNnnhtoWuLhgiIMsx4AAr5+8Y+M5tekNlYkpaKfoXI7n29B+J9k3Y2o0XUdkS+MvGsmtO2nacSlop5PQyEdz7eg/E155RRWbdz1YQUFyxCiiikWFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXrvwk+L2ufCzWPNt83GnTsPtNqTw3ben91wO/foe2PIqKDOpTjUi4TV0f/T+tKKKKyPtQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACobi4gtIHublwkaDLMegFNu7u2sbZ7u7cRxoMsx7V87+LfF9z4in8iHMdoh+VO7H+839B2pN2NqNF1H5EvjDxjP4gmNra5jtEPC93I7t/QVw1FFZtnqwgorliFFFFIsKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9T60ooorI+1CiiigAooooAKKKKACiiigAooooAKKKKACiiigAqre3trp1q95euI40GST/nrTNQ1C00u0e9vnCRoOSf5D1Jr5y8U+KrvxJdZOY7dD+7j/qfU/wAqTdjejRdR+RL4r8WXXiO52LmO1Q/Inr/tN7/yrkKKKzPVjFRVkFFFFIoKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//V+tKKKKyPtQooooAKKKKACiiigAooooAKKKKACiiigAqhqep2ekWb318+yNPzJ9AO5NM1XVrLRbJr6/fai9B3Y9gB3NfN/iPxJe+I7zz7j5Yl/wBXGOij+p9TUt2OihQdR+RJ4m8T3niO78yT5IEP7uPsPc+prmaKKg9SMVFWQUUUUigooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/W+tKKkWKV/uKT9BVlNO1CT7kEjfRT/hWR9rcpUVqpoWuP9yznP0jb/CrK+FvE7/c066P0hf8AwoFzLuYNFdMvgvxc/TTbn8Y2H8xVlfAPjJ+mnTfiMfzoF7SPc5Ciu4X4b+N36ae/4sg/masL8LfHb/8ALjj6yR//ABVIXtYfzI8/or0lPhL44brbIv1kT+hqynwf8Zt1SFfrIP6A0C9tD+ZHltFetL8GPFzdXt1+rt/RTVlfgl4oP37i1H/AnP8A7JQL6xT/AJjxysrWNZsdDsmvr5sKOAB1Y+gHrXpXjvwfF8OdBfXfEmoQKv3Yok3GSV/7qAgZ9z0A618Q69r194gvTd3hwBwiDoo9B/U96TdjrwtNVveWw/xB4hvvEV6bm6O1F4SMdFH+PqawaKKg9dJJWQUUUUhhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH/9k=",width:384,height:256}],unavailableCount:0,fetchedAt:Wt,source:"system",stale:!1}},{id:"$calendar.isAvailable",group:"calendar",value:!0},{id:"$calendar.authorizationStatus",group:"calendar",value:"authorized"},{id:"$calendar.requestAuthorization",group:"calendar",value:!0},{id:"$calendar.calendars",group:"calendar",value:[{id:"sample.personal",title:"Personal"}]},{id:"$calendar.events",group:"calendar",value:{status:"ready",items:[{id:"sample.event",calendarID:"sample.personal",title:"Read and recharge",start:"2026-01-01T15:00:00Z",end:"2026-01-01T15:30:00Z",allDay:!1}],fetchedAt:Wt,source:"system",stale:!1,truncated:!1}},{id:"$reminders.isAvailable",group:"reminders",value:!0},{id:"$reminders.authorizationStatus",group:"reminders",value:"authorized"},{id:"$reminders.requestAuthorization",group:"reminders",value:!0},{id:"$reminders.lists",group:"reminders",value:[{id:"sample.reminders",title:"Personal"}]},{id:"$reminders.items",group:"reminders",value:{status:"ready",items:[{id:"sample.reminder",listID:"sample.reminders",title:"Read a chapter",due:"2026-01-01T16:00:00Z",hasTime:!0,completed:!1,completedAt:null,priority:0}],fetchedAt:Wt,source:"system",stale:!1,truncated:!1}},{id:"$reminders.create",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$reminders.update",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$reminders.setCompleted",group:"reminders",value:{id:"sample.reminder",listID:"sample.reminders",status:"applied",replayed:!1}},{id:"$health.authorizationStatus",group:"health",value:"notDisclosed"},{id:"$health.sleepSummary",group:"health",value:{start:cl,end:Wt,timeZone:"UTC",hasSamples:!0,totalSleepSeconds:27e3,inBedSeconds:30600,awakeSeconds:1800,stages:{unspecified:0,core:16200,deep:5400,rem:5400},sourceCount:1,truncated:!1,conflictSeconds:0,aggregation:"union-awake-rem-deep-core-unspecified"}},{id:"$health.activitySummary",group:"health",value:{date:"2026-01-01",timeZone:"UTC",hasSamples:!0,moveMode:"activeEnergy",move:{value:480,goal:600,unit:"kcal"},exercise:{value:26,goal:30,unit:"min"},stand:{value:9,goal:12,unit:"count"}}},{id:"$health.workouts",group:"health",value:{start:cl,end:Wt,timeZone:"UTC",hasSamples:!0,truncated:!1,items:[{id:"550e8400-e29b-41d4-a716-446655440000",activity:"running",start:"2026-01-01T09:00:00.000Z",end:"2026-01-01T09:45:00.000Z",duration:2700,energyKcal:285,distanceMeters:5200}]}},{id:"$health.isAvailable",group:"health",value:!0},{id:"$health.requestAuthorization",group:"health",value:!0,legacy:["health"]},{id:"$health.stepCountToday",group:"health",value:ao(6840,"count"),legacy:["health.stepCountToday"]},{id:"$health.activeEnergyToday",group:"health",value:ao(420,"kcal"),legacy:["health.activeEnergyToday"]},{id:"$health.heartRateLatest",group:"health",value:ao(72,"bpm"),legacy:["health.heartRateLatest"]},{id:"$location.isAvailable",group:"location",value:!0},{id:"$location.authorizationStatus",group:"location",value:"authorizedWhenInUse"},{id:"$location.isAuthorizedForWidgetUpdates",group:"location",value:!0},{id:"$location.requestAuthorization",group:"location",value:!0,legacy:["location.authorization"]},{id:"$location.current",group:"location",value:{latitude:37.3349,longitude:-122.009,altitude:0,accuracy:10,verticalAccuracy:10,speed:-1,course:-1,timestamp:Wt,accuracyAuthorization:"full",age:0,isStale:!1},legacy:["location"]}],y1={music:{platforms:["ios"],authorization:"musicRead",notes:"Runtime 1.4. Read-only Music app playback, not every audio player. Requires an installed owner, music permission and Music library approval in native Settings. current() is asynchronous and returns provider, state, hasItem, nullable title/artist/album/duration/position and capturedAt. State may be playing while details are absent (Home Sharing). Native player access is main-thread-only: synchronous hosts prepare one snapshot before JSC waits; background previews capture on the main queue. No playback/queue mutation, scans, artwork, credential or subscription inference. No live queries in future entries/Live Activities. Main system-player API is unavailable on macOS; Web supplies editable samples, never actual device access."},services:{platforms:["ios","macos"],authorization:"serviceAccount",notes:"Runtime 1.4: status(service) and profile(service) are asynchronous. Only provider: github. Unknown providers reject with service_operation. Requires an installed owner and services permission. profile additionally requires network and api.github.com in networkDomains, a native connected GitHub account and explicit owner grant. Native Settings handles credentials; scripts cannot log in or obtain tokens. profile returns service, subject, username, displayName, publicRepositories, privateRepositories, followers and following (missing values are null). No live queries in future entries or Live Activities. Eight shared data queries; native requests also share the eight concurrent/32 total network budget, while Web uses its existing preview network budget. Web uses editable best-state samples only; it never authenticates or sends these requests."},photos:{platforms:["ios","macos"],authorization:"photosRead",notes:"Requires photos permission and explicit native photo/album selection (none by default). Limited Photos authorization is supported. items({limit:1..4,offset:0..63,maxAgeSeconds:0..86400}) returns bounded, metadata-stripped JPEG thumbnails as image data URLs. Four 768px / 128KiB thumbnails at most; private snapshots expire after 24h and are rechecked against current access, selected sources and asset revisions. Cloud-only items report unavailableCount; download them in Photos first. Future timeline/Live Activity presentations read matching snapshots only. Web uses editable sample data and photosSelection IDs; no actual library access."},reminders:{platforms:["ios","macos"],authorization:"remindersRead",notes:"Requires reminders in widget.json and full system Reminders access. Reads require native Settings selected lists. create/update/setCompleted additionally require remindersWrite and a native declared action, stable requestID, durable replay receipts and an unchanged selected list. Web writes update preview-only items/receipts; receipt identities are derived from that state, with configurable API errors. Use lists() for IDs. items() selects pending due dates or completed completion dates in a positive window of at most 31 days, up to 50 items. Undated pending reminders are excluded. remindersSelection mocks the user scope without granting permission."},calendar:{platforms:["ios","macos"],authorization:"calendarRead",notes:"Requires calendar in widget.json and full system Calendar access. The user's native Settings source selection limits metadata, events and cached snapshots; scripts cannot enlarge it. Use calendars() for permitted IDs and show setup when no sources are selected. Queries are read-only, limited to 31 days and 50 items. Preview calendarSelection simulates this scope without granting package permission."},device:{platforms:["ios","macos"],authorization:"none",notes:"Device values depend on hardware. Some legacy Mac values are placeholders; inspect the API documentation before using them as measurements."},system:{platforms:["ios","macos"],authorization:"none",notes:"System metadata, not whole-device CPU, FPS or network traffic monitoring. Hardware-specific values may be unavailable."},health:{platforms:["ios"],authorization:"healthRead",notes:"Read-only HealthKit. Request access in the app. Runtime 1.4 adds sleepSummary (up to 7 days / 256 samples, union with explicit stage-conflict precedence), activitySummary (one local Gregorian day including active-energy or move-time mode and optional goals), and workouts (up to 31 days / 50 fully-contained summaries, optional kcal/meters). These require an explicit health manifest; all share the query budget and cannot run in future/Live Activity presentations. No Health readings are persisted by the API. Authorization completion does not reveal whether individual read types were granted; empty results must not be labelled permission denied. API 1.3 readings add hasSamples: false means no readable samples, never a read-denial verdict. Existing value/unit/start/end fields remain."},location:{platforms:["ios"],authorization:"locationWhenInUse",notes:"Package 2.0 requires location permission. Request system access in the app. Widgets also require isAuthorizedForWidgetUpdates. Cache is rechecked against authorization and precision, discarded after 24 hours or revocation, and reports age/isStale."}},A1=g1.map(n=>{const e=n.id.split(".")[1],t=n.group==="music"&&e==="current"||n.group==="services"||["calendar","reminders","photos"].includes(n.group)&&!["isAvailable","authorizationStatus"].includes(e)||n.group==="health"&&!["isAvailable","authorizationStatus"].includes(e)||n.group==="location"&&["current","requestAuthorization"].includes(e),i=n.group==="health"&&["sleepSummary","activitySummary","workouts"].includes(e)||n.group==="location"&&["current","requestAuthorization"].includes(e)||["calendar","reminders","photos"].includes(n.group)&&["events","items"].includes(e);return{...n,...y1[n.group],minimumRuntimeVersion:["music","services","reminders","photos"].includes(n.group)||n.group==="health"&&["sleepSummary","activitySummary","workouts"].includes(e)?"1.4":["calendar"].includes(n.group)||["$health.authorizationStatus","$location.isAuthorizedForWidgetUpdates"].includes(n.id)?"1.3":"1.0",arguments:n.group==="services"?["service"]:n.group==="reminders"&&["create","update","setCompleted"].includes(e)?["options"]:i?["options?"]:[],...n.group==="reminders"&&["create","update","setCompleted"].includes(e)?{authorization:"remindersWrite"}:{},asynchronous:t,nativeSource:`Shared/ScriptWidgetRuntime/Widget/API/ScriptWidgetRuntime${n.group[0].toUpperCase()}${n.group.slice(1)}.swift`,operation:n.group==="reminders"&&["create","update","setCompleted"].includes(e)?"write":e==="requestAuthorization"?"authorization":["isAvailable","authorizationStatus","isAuthorizedForWidgetUpdates"].includes(e)?"availability":"read",mock:"editable"}}),Re={globalProperties:{background:{type:"color",documentation:"Background color, hex value, or named color."},backgroundMaterial:{type:"enum",values:["none","ultraThin","thin","regular","thick","ultraThick"],documentation:"Runtime API 1.4. Public SwiftUI frosted material inside the widget, not Home Screen wallpaper blur or system Liquid Glass. At most four material backgrounds per tree. background supplies an opaque-composited Reduce Transparency fallback. In accented/vibrant mode local material is omitted for system rendering; none keeps the ordinary background."},color:{type:"color",documentation:"Foreground color, hex value, or named color."},font:{type:"enum",values:["largeTitle","title","title2","title3","headline","subheadline","body","callout","footnote","caption","caption2"],documentation:"Semantic text style."},fontWeight:{type:"enum",values:["ultraLight","thin","light","regular","medium","semibold","bold","heavy","black"],documentation:"Font weight."},fontDesign:{type:"enum",values:["default","rounded","serif","monospaced"],documentation:"Font design."},frame:{type:"number|string",documentation:'Square size (64 or "64"), width,height ("64,48"), or flexible frame ("max", "max,48").'},padding:{type:"number|string",documentation:"Padding amount or edge description."},corner:{type:"number",documentation:"Corner radius."},opacity:{type:"number",documentation:"Opacity from 0 through 1."},alignment:{type:"enum",values:["center","leading","trailing","top","bottom","topLeading","topTrailing","bottomLeading","bottomTrailing","firstTextBaseline","lastTextBaseline"],documentation:"Layout alignment."},clip:{type:"enum",values:["circle","rect","capsule","ellipse"],documentation:"Clip the view to a shape."},rotation:{type:"number",documentation:"Rotation in degrees."},rotation3d:{type:"string",documentation:"Static degree,x,y,z rotation with four finite numbers and a nonzero axis. Invalid values are ignored; axis magnitude is normalized. Reduce Motion removes the tilt. This does not read device motion or provide parallax."},shadow:{type:"string",documentation:"Shadow color, radius, and offset description."},animation:{type:"string",documentation:"iOS public SwiftUI scheduled clock/swing effect. Mac renders static standard-clock poses; other effects remain at rest. Signed clockCustom intervals specify a half-turn (full cycle = 2 * abs(interval)), minimum magnitude 0.25 seconds. App previews can animate; WidgetKit decides cadence and may freeze. Reduced motion/luminance freezes standard clock poses and keeps custom/swing effects at rest. Dynamic scripts are not rerun on animation ticks."},widgetAccentable:{type:"boolean",documentation:"Group this view into the accent layer in tinted and vibrant widget appearances."},linkurl:{type:"url",documentation:"Widget deep link URL."}},components:{VStack:{documentation:"Arrange children vertically.",children:!0,properties:{spacing:{type:"number"}}},HStack:{documentation:"Arrange children horizontally.",children:!0,properties:{spacing:{type:"number"}}},ZStack:{documentation:"Overlay children.",children:!0,properties:{}},VGrid:{documentation:"Vertical lazy grid.",children:!0,properties:{columns:{type:"string",required:!0},spacing:{type:"number"},alignment:{type:"enum",values:["leading","center","trailing"]}}},HGrid:{documentation:"Horizontal lazy grid.",children:!0,properties:{columns:{type:"string",required:!0},spacing:{type:"number"},alignment:{type:"enum",values:["top","center","bottom","firstTextBaseline","lastTextBaseline"]}}},Text:{documentation:"Display text content.",children:!1,properties:{text:{type:"string"}}},Date:{documentation:"Display a formatted date.",children:!1,properties:{date:{type:"date|number",required:!0},style:{type:"enum",values:["time","date","relative","offset","timer"]}}},Image:{documentation:"Display a bundled, remote, or SF Symbol image.",children:!1,properties:{systemName:{type:"string"},name:{type:"string"},id:{type:"string",deprecated:"Use name."},url:{type:"url"},src:{type:"url",deprecated:"Use url."},ratio:{type:"number"},mode:{type:"enum",values:["fit","fill"]},accentedRenderingMode:{type:"enum",values:["accented","desaturated","accentedDesaturated","fullColor"],documentation:"Control how the image is rendered in tinted and clear widget appearances."}}},Gif:{documentation:"Display a bounded package GIF using system decoding: up to 8 MiB input and 1,048,576 canvas pixels. iOS retains at most 60 downsampled frames / 4 MiB decoded pixels; more frames fall back to the first frame. Mac, Reduce Motion and reduced luminance use a static first frame. Active iOS previews use public SwiftUI timing with variable frame delays. Snapshots, inactive scenes, Mac and accessibility fallbacks use the first frame. No private clock bridge or continuous WidgetKit guarantee.",children:!1,properties:{file:{type:"string",required:!0}}},Spacer:{documentation:"Flexible layout space.",children:!1,properties:{minLength:{type:"number"}}},Rect:{documentation:"Rectangle shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},RoundedRect:{documentation:"Filled rounded rectangle.",children:!1,properties:{radius:{type:"number"}}},Capsule:{documentation:"Capsule shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Ellipse:{documentation:"Ellipse shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Circle:{documentation:"Circle shape.",children:!1,properties:{trim:{type:"number|string"},stroke:{type:"string"}}},Gauge:{documentation:"Display a gauge or instrument.",children:!1,properties:{type:{type:"string"},value:{type:"number"},style:{type:"enum",values:["circular","linear"]},text:{type:"string"},current:{type:"string"},min:{type:"string"},max:{type:"string"},angle:{type:"number"},thickness:{type:"number"},needleColor:{type:"color"},label:{type:"string"},title:{type:"string"},sections:{type:"string"}}},Chart:{documentation:"Render chart data.",children:!1,availability:{ios:"16.0",macos:"26.0"},properties:{type:{type:"enum",values:["bar","bar-x","bar-y","bar-gantt","line","point","line-point","area","rect","rule-x"]},data:{type:"json",required:!0},category:{type:"boolean"},hideLegend:{type:"boolean"},hideXAxis:{type:"boolean"},hideYAxis:{type:"boolean"}}},Link:{documentation:"Open a URL when selected.",children:!0,properties:{url:{type:"url",required:!0}}},Divider:{documentation:"Horizontal or vertical separator.",children:!1,properties:{thickness:{type:"number"},axis:{type:"enum",values:["horizontal","vertical"]}}},Line:{documentation:"Fixed-length line.",children:!1,properties:{thickness:{type:"number"},length:{type:"number"},axis:{type:"enum",values:["horizontal","vertical"]}}},Icon:{documentation:"SF Symbol icon.",children:!1,properties:{systemName:{type:"string",required:!0},size:{type:"number"}}},Label:{documentation:"Text paired with an SF Symbol.",children:!1,properties:{title:{type:"string"},systemName:{type:"string",required:!0}}},Progress:{documentation:"Linear or circular progress.",children:!1,properties:{value:{type:"number",required:!0},total:{type:"number"},label:{type:"string"},style:{type:"enum",values:["linear","circular"]},trackColor:{type:"color"},thickness:{type:"number"}}},Ring:{documentation:"Circular progress ring.",children:!1,properties:{value:{type:"number",required:!0},thickness:{type:"number"},trackColor:{type:"color"}}},Badge:{documentation:"Compact status badge.",children:!1,properties:{text:{type:"string"},radius:{type:"number"}}},Chip:{documentation:"Outlined metadata chip.",children:!1,properties:{text:{type:"string"},radius:{type:"number"},borderColor:{type:"color"}}},Stat:{documentation:"Title, value, and subtitle statistic.",children:!1,properties:{title:{type:"string"},value:{type:"string",required:!0},subtitle:{type:"string"},mutedColor:{type:"color"}}},Button:{documentation:"Interactive widget button. Use actionID to share a declared Package 2.0 action with Siri, Shortcuts, and Control Widgets.",children:!0,availability:{interactiveIos:"17.0"},requiredAny:["action","onClick","actionID"],properties:{action:{type:"enum",values:["reload"],conflicts:["actionID"]},onClick:{type:"function",conflicts:["actionID"]},actionID:{type:"action-id",conflicts:["action","onClick"]}}},Toggle:{documentation:"Interactive widget toggle. A declared actionID requires a package-relative storage stateKey.",children:!0,availability:{interactiveIos:"17.0"},requiredAny:["onClick","actionID"],properties:{on:{type:"boolean",required:!0},onClick:{type:"function",conflicts:["actionID"]},actionID:{type:"action-id",requires:["stateKey"],conflicts:["onClick"]},stateKey:{type:"storage-key",requires:["actionID"]}}}},functions:{...Object.fromEntries(O1.map(n=>[n.id,n])),...Object.fromEntries(A1.map(n=>[n.id,{documentation:n.notes,arguments:n.arguments,returns:n.asynchronous?"Promise":typeof n.value=="object"?"json":typeof n.value}])),"$icloud.readJSON":{documentation:"Read cached JSON and revision; conflicts preserve all heads. Requires icloud permission and a native connection. Never certifies upload.",arguments:["name"],returns:"Promise",minimumRuntimeVersion:"1.5"},"$icloud.writeJSON":{documentation:"Save a new immutable JSON revision locally from a user action. Requires the revision from readJSON. Background exchange is eventual.",arguments:["name","value","expectedRevision"],returns:"Promise",minimumRuntimeVersion:"1.5"},"$icloud.resolveJSON":{documentation:"Explicitly resolve all observed conflicting heads into a new revision. Stale tokens fail; originals remain.",arguments:["name","value","expectedRevision"],returns:"Promise",minimumRuntimeVersion:"1.5"},"$icloud.sync":{documentation:"Exchange materialized revisions with iCloud; requires a user action. Upload completion is unknown.",arguments:[],returns:"Promise",minimumRuntimeVersion:"1.5"},"$icloud.status":{documentation:"Read native connection state. Cloud store IDs are granted in the app, never by the script. Web uses preview mocks.",arguments:[],returns:"Promise",minimumRuntimeVersion:"1.5"},"$file.exists":{documentation:"Return whether a regular file is locally present inside this package, including binary assets. Missing files, directories, unsafe paths and links return false. Does not download iCloud files or read outside the package. Web preview uses workspace file presence.",arguments:["path"],returns:"boolean",minimumRuntimeVersion:"1.3"},"$file.read":{documentation:"Read package-relative UTF-8 text. Missing or unavailable files return an empty string.",arguments:["path"],returns:"string"},"$file.readJSON":{documentation:"Read a package-relative JSON object.",arguments:["path"],returns:"json"},fetch:{documentation:"Perform a network request and return its response text. Existing HTTP methods keep this string result."},"$http.getResponse":{documentation:"GET response metadata: {status, body, headers}. Headers are limited to content-type, cache-control, expires, last-modified, date and retry-after (lowercase; each at most 2048 UTF-8 bytes). Network permissions, public-host policy, redirects, body limits and timeline restrictions still apply. HTTP error statuses resolve; transport errors reject.",arguments:["url","options?"],returns:"Promise",minimumRuntimeVersion:"1.3"},importJS:{documentation:"Import a package-relative JavaScript file."},"$dataSource.request":{documentation:"Call a declared Data Source Plugin operation. Requires Package 2.0 plugins, network permission, and networkDomains declarations.",arguments:["pluginID","operationID","parameters"],returns:"Promise"},"$xml.parse":{documentation:"Parse up to 256 KiB of XML into {name, attributes, text, children}. DTDs and external entities are rejected; depth and node limits apply.",arguments:["source"],returns:"XMLNode"}}};function b1(n,e){const t=Object.entries(e.properties??{}).filter(([,r])=>r.required).map(([r,s])=>`${r}=${s.type.includes("number")||s.type==="boolean"?"{${}}":'"${}"'}`).join(" "),i=t?` ${t}`:"";return e.children?`<${n}${i}> \${} `:`<${n}${i} />`}const lh=Object.entries(Re.components).map(([n,e])=>({label:n,type:"class",detail:`ScriptWidget · ${Object.keys(e.properties??{}).length} properties`,info:e.documentation,apply:b1(n,e)}));function S1(n){const e=n.match(/<([A-Za-z][\w]*)\b([^<>]*)$/);return!e||e[2].includes(">")?null:{name:e[1],attributes:e[2]}}function x1(n,e){const t=Re.components[n];return t?Object.entries({...Re.globalProperties,...t.properties??{}}).filter(([i])=>!e.has(i)).map(([i,r])=>({label:i,type:"property",detail:r.required?`${r.type} · required`:r.type,info:r.deprecated?`Deprecated: ${r.deprecated}`:r.documentation,apply:`${i}=${r.type.includes("number")||r.type==="boolean"?"{${}}":'"${}"'}`})):[]}function w1(n){const e=n.match(/<([A-Za-z][\w]*)\b[^<>]*\b([A-Za-z][\w]*)\s*=\s*["']([^"']*)$/);if(!e)return null;const t=Re.components[e[1]]?.properties?.[e[2]]??Re.globalProperties[e[2]];return t?.values?{typed:e[3],values:t.values}:null}function v1(n){const e=n.state.doc.sliceString(Math.max(0,n.pos-1200),n.pos),t=w1(e);if(t)return{from:n.pos-t.typed.length,options:t.values.map(o=>({label:o,type:"enum"})),validFor:/^[\w-]*$/};const i=n.matchBefore(/<[A-Za-z]*$/);if(i)return{from:i.from+1,options:lh,validFor:/^[A-Za-z]*$/};const r=S1(e);if(r&&/(?:\s|^)\w*$/.test(r.attributes)){const o=n.matchBefore(/[A-Za-z][\w]*$/),l=new Set([...r.attributes.matchAll(/\b([A-Za-z][\w]*)\s*=/g)].map(a=>a[1]));return{from:o?.from??n.pos,options:x1(r.name,l),validFor:/^[A-Za-z][\w]*$/}}const s=n.matchBefore(/\$[A-Za-z]+\.[A-Za-z]*$/);if(s){const o=s.text.slice(0,s.text.indexOf(".")+1),l=Object.entries(Re.functions).filter(([a])=>a.startsWith(o));return l.length?{from:s.from+o.length,options:l.map(([a,c])=>({label:a.slice(o.length),type:"function",detail:c.returns,info:c.documentation})),validFor:/^[A-Za-z]*$/}:null}return n.explicit?{from:n.pos,options:lh,validFor:/^[A-Za-z]*$/}:null}function ah(n,e){const t=[];for(let i=n.node.firstChild;i;i=i.nextSibling)i.name===e&&t.push(i);return t}function Md(n){const e=[];return Ce(n).iterate({enter(t){if(t.name!=="JSXOpenTag"&&t.name!=="JSXSelfClosingTag")return;const r=ah(t,"JSXIdentifier")[0];r&&e.push({name:n.doc.sliceString(r.from,r.to),from:r.from,to:r.to,attributes:ah(t,"JSXAttribute").map(s=>{const o=s.firstChild;return o?{name:n.doc.sliceString(o.from,o.to),from:o.from,to:o.to}:null}).filter(Boolean)})}}),e}function Ed(n){const e=[];for(const t of Md(n.state)){const i=Re.components[t.name];if(!i)continue;const r=new Set([...Object.keys(Re.globalProperties),...Object.keys(i.properties??{})]),s=new Set(t.attributes.map(o=>o.name));for(const o of t.attributes){r.has(o.name)||e.push({from:o.from,to:o.to,severity:"warning",message:`“${o.name}” is not supported by <${t.name}>.`});const l=i.properties?.[o.name]??Re.globalProperties[o.name];l?.deprecated&&e.push({from:o.from,to:o.to,severity:"warning",message:`${o.name} is deprecated. ${l.deprecated}`})}for(const[o,l]of Object.entries(i.properties??{}))if(l.required&&!s.has(o)&&e.push({from:t.from,to:t.to,severity:"error",message:`<${t.name}> requires the “${o}” property.`}),s.has(o)){for(const a of l.requires??[])s.has(a)||e.push({from:t.from,to:t.to,severity:"error",message:`<${t.name}> “${o}” requires the “${a}” property.`});for(const a of l.conflicts??[])s.has(a)&&o cannot combine “${o}” with “${a}”.`})}i.requiredAny?.length&&!i.requiredAny.some(o=>s.has(o))&&e.push({from:t.from,to:t.to,severity:"error",message:`<${t.name}> requires one of: ${i.requiredAny.map(o=>`“${o}”`).join(", ")}.`})}return e}function k1(n,e){const t=Md(n.state).find(r=>e>=r.from&&e<=r.to);if(!t)return null;const i=Re.components[t.name];return i?{pos:t.from,end:t.to,above:!0,create(){const r=document.createElement("div");r.className="studio-hover";const s=document.createElement("strong");s.textContent=`<${t.name}>`;const o=document.createElement("p");return o.textContent=i.documentation,r.append(s,o),{dom:r}}}:null}const ch={light:{background:"#FAFAFC",foreground:"#202124",gutter:"#F3F3F6",gutterText:"#8A8A93",activeLine:"#EDEEF3",selection:"#B9D7FF",caret:"#0067C0"},dark:{background:"#1E1F22",foreground:"#E8E8ED",gutter:"#191A1D",gutterText:"#777982",activeLine:"#292A2F",selection:"#315A85",caret:"#65B7FF"}};function Q1(n,e){return R.theme({"&":{height:"100%",color:n.foreground,backgroundColor:n.background,fontSize:"13px"},".cm-scroller":{fontFamily:'ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, monospace',lineHeight:"1.58",overscrollBehavior:"contain"},".cm-content":{caretColor:n.caret,padding:"12px 0 32px"},".cm-line":{padding:"0 8px"},".cm-cursor, .cm-dropCursor":{borderLeftColor:n.caret},"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection":{backgroundColor:n.selection},".cm-activeLine":{backgroundColor:n.activeLine},".cm-gutters":{backgroundColor:n.gutter,color:n.gutterText,border:"none",paddingLeft:"env(safe-area-inset-left)"},".cm-activeLineGutter":{backgroundColor:n.activeLine,color:n.foreground},".cm-foldPlaceholder":{backgroundColor:"transparent",border:`1px solid ${n.gutterText}`,color:n.gutterText},".cm-tooltip":{border:e?"1px solid #41434A":"1px solid #D2D3D8",backgroundColor:e?"#292A2F":"#FFFFFF",borderRadius:"8px",overflow:"hidden",boxShadow:"0 8px 28px rgba(0, 0, 0, 0.2)"},".cm-tooltip-autocomplete ul li[aria-selected]":{backgroundColor:e?"#315A85":"#DCEBFA",color:n.foreground},".cm-panels":{backgroundColor:n.gutter,color:n.foreground},".cm-searchMatch":{backgroundColor:e?"#725C1D":"#FFE69A"},".cm-searchMatch.cm-searchMatch-selected":{backgroundColor:e?"#8D6D20":"#FFD45C"}},{dark:e})}const P1=Ln.define([{tag:[g.keyword,g.controlKeyword,g.moduleKeyword,g.operatorKeyword,g.bool,g.null],color:"#FF7AB2"},{tag:[g.string,g.special(g.string)],color:"#FF8170"},{tag:[g.number,g.regexp],color:"#D9C97C"},{tag:[g.comment,g.lineComment,g.blockComment],color:"#7F8C98",fontStyle:"italic"},{tag:[g.tagName,g.typeName,g.className],color:"#5DD8FF"},{tag:g.attributeName,color:"#B281EB"},{tag:[g.propertyName,g.function(g.variableName),g.function(g.propertyName)],color:"#67B7A4"},{tag:g.definition(g.variableName),color:"#4EB0CC"},{tag:[g.variableName,g.operator,g.punctuation,g.bracket],color:"#E8E8ED"},{tag:g.invalid,color:"#FF453A"}]);function Ud(n){const e=n==="dark";return[Q1(e?ch.dark:ch.light,e),Jg(e?P1:i0)]}const C1=Lt.parser.configure({dialect:"jsx"}),T1=new Map(Object.keys(Re.components).map(n=>[n.toLowerCase(),n])),$1=new Set(["VStack","HStack","ZStack","VGrid","HGrid"]),R1=512*1024,K1=512,Z1=32,ci=n=>{const e=[];for(let t=n.firstChild;t;t=t.nextSibling)e.push(t);return e},hi=(n,e)=>n.slice(e.from,e.to),de=n=>{throw new Error(n)},Fd=n=>{let e=!0;const t=n.replace(/&([^;\s]+);/g,(i,r)=>{const s={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:" "};if(Object.hasOwn(s,r))return s[r];if(/^#(?:[0-9]+|x[0-9a-f]+)$/i.test(r)){const o=r[1].toLowerCase()==="x"?parseInt(r.slice(2),16):Number(r.slice(1));if(o>0&&o<=1114111&&!(o>=55296&&o<=57343))return String.fromCodePoint(o)}return e=!1,i});return e?t:void 0};function co(n,e=!1){if(e)return Fd(n.slice(1,-1));const t=n.trim();if(/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(t)){const i=Number(t);return Number.isFinite(i)?i:void 0}if(t==="true"||t==="false")return t==="true";if(t.startsWith('"'))try{const i=JSON.parse(t);return typeof i=="string"?i:void 0}catch{}}function wi(n){const e={formatVersion:1,source:n,nodes:[],roots:[],issue:""};if(typeof n!="string"||new TextEncoder().encode(n).length>R1)return{...e,issue:"Layout editing supports source files up to 512 KiB."};const t=C1.parse(n);let i=!1;if(t.iterate({enter:s=>{s.type.isError&&(i=!0)}}),i)return{...e,issue:"Fix the JavaScript/JSX syntax before using Layout. Your source is preserved."};function r(s,o,l){(l>Z1||e.nodes.length>=K1)&&de("Layout editing supports up to 512 layers and 32 nesting levels.");const a=ci(s),c=a[0],h=a.at(-1),u=ci(c).find(S=>["JSXBuiltin","JSXIdentifier","JSXNamespacedName","JSXMemberExpression"].includes(S.name)),f=u?hi(n,u):"Fragment",d=T1.get(f.toLowerCase()),p=Re.components[d],m={...Re.globalProperties,...p?.properties??{}},O=c.name==="JSXOpenTag"&&h?.name==="JSXCloseTag",y=O?ci(h).find(S=>["JSXBuiltin","JSXIdentifier","JSXNamespacedName","JSXMemberExpression"].includes(S.name)):null;u&&O&&(!y||hi(n,y)!==f)&&de("Opening and closing JSX tags must match before using Layout.");const b={id:String(s.from),parentID:o,tag:f,component:d??null,from:s.from,to:s.to,openingEnd:c.to,insertAt:c.to-(O?1:2),contentFrom:c.to,contentTo:O?h.from:c.to,children:[],properties:[],locked:!p,structure:!1,text:null,textEditable:!1,depth:l};let x=!1;for(const S of ci(c)){if(S.name==="JSXSpreadAttribute"&&(x=!0),S.name!=="JSXAttribute")continue;const v=ci(S),U=hi(n,v[0]),F=v[2],j=F?co(hi(n,F),F.name==="JSXAttributeValue"):!0,E=F?.name==="JSXEscape"?co(n.slice(F.from+1,F.to-1)):j,M=Object.hasOwn(m,U)?m[U]:null;b.properties.push({name:U,value:E??null,from:S.from,to:S.to,editable:!!(p&&M&&M.type!=="function"&&E!==void 0),definition:M??null})}const C=new Set(b.properties.map(S=>S.name)).size!==b.properties.length;b.locked||=x||C,b.reason=x?"Spread properties are preserved; edit this element in Code.":C?"Duplicate properties are preserved; resolve them in Code.":p?"":"Custom components and fragments are preserved; edit them in Code.",b.locked&&b.properties.forEach(S=>{S.editable=!1}),e.nodes.push(b);const k=O?a.slice(1,-1):[];for(const S of k)S.name==="JSXElement"&&b.children.push(r(S,b.id,l+1).id);if(b.structure=!b.locked&&O&&$1.has(d)&&k.every(S=>S.name==="JSXElement"||S.name==="JSXText"&&!hi(n,S).trim())&&b.children.every(S=>!e.nodes.find(v=>v.id===S).locked),!b.locked&&d==="Text"&&O&&k.length===1){const S=k[0],v=hi(n,S),U=S.name==="JSXText"&&!/[\r\n]/.test(v)?Fd(v):S.name==="JSXEscape"?co(v.slice(1,-1)):void 0;typeof U=="string"&&(b.text=U,b.textEditable=!0)}return b}try{t.iterate({enter(s){if(s.name!=="CallExpression")return;const o=ci(s.node);if(o[0]?.name!=="VariableName"||hi(n,o[0])!=="$render"||o[1]?.name!=="ArgList")return;const l=ci(o[1]).filter(a=>!["(",")"].includes(a.name));if(l.length===1&&l[0].name==="JSXElement")return e.roots.push(r(l[0],null,0).id),!1}})}catch(s){return{...e,nodes:[],roots:[],issue:s.message}}return e.roots.length||(e.issue="Layout needs a direct $render(…) or another native JSX element. Variables, loops and functions stay editable in Code."),e}function hl(n){return!n?.component||n.locked?{}:{...Re.globalProperties,...Re.components[n.component]?.properties??{}}}function M1(n,e,t){(n.source!==e||n.issue)&&de("Layout changed. Select the layer again before editing.");const i=n.nodes.find(a=>a.id===t.id);(!i||i.locked)&&de("This layer is locked. Edit its source in Code.");let r=[],s=i.from;if(t.type==="property"){const a=hl(i),c=Object.hasOwn(a,t.name)?a[t.name]:null,h=i.properties.find(d=>d.name===t.name);(!c||c.type==="function"||h&&!h.editable)&&de("Dynamic or unsupported properties must be edited in Code.");const u=new Set(i.properties.map(d=>d.name)),f=Re.components[i.component];if(t.remove?(f.requiredAny?.includes(t.name)&&!f.requiredAny.some(d=>d!==t.name&&u.has(d))&&de("Keep at least one required component property."),Object.entries(f.properties??{}).some(([d,p])=>u.has(d)&&p.requires?.includes(t.name))&&de("Another property requires this value. Edit the dependent properties together in Code.")):(c.requires?.some(d=>!u.has(d))&&de("Add the required related properties together in Code."),(c.conflicts?.some(d=>u.has(d))||Object.entries(f.properties??{}).some(([d,p])=>d!==t.name&&u.has(d)&&p.conflicts?.includes(t.name)))&&de("This property conflicts with another component property.")),t.remove)(!h||c.required)&&de("This property cannot be removed."),r=[{from:h.from,to:h.to,insert:""}];else{const d=t.value;(!["string","number","boolean"].includes(typeof d)||typeof d=="number"&&!Number.isFinite(d)||typeof d=="string"&&d.length>8192)&&de("Use a bounded string, finite number or Boolean."),c.values&&!c.values.includes(d)&&de("Choose one of the supported property values."),c.type==="boolean"&&typeof d!="boolean"&&de("This property needs true or false."),c.type==="number"&&!(typeof d=="number"||typeof d=="string"&&d.trim()&&Number.isFinite(Number(d)))&&de("This property needs a number."),typeof d=="boolean"&&c.type!=="boolean"&&de("This property does not accept a Boolean."),typeof d=="number"&&!c.type.includes("number")&&de("This property needs a string value."),t.name==="opacity"&&!(Number(d)>=0&&Number(d)<=1)&&de("Opacity must be between zero and one.");const p=`${t.name}={${JSON.stringify(d)}}`;r=h?[{from:h.from,to:h.to,insert:p}]:[{from:i.insertAt,to:i.insertAt,insert:` ${p}`}]}}else if(t.type==="text")(!i.textEditable||typeof t.value!="string"||t.value.length>8192)&&de("Dynamic text is preserved. Edit it in Code."),r=[{from:i.contentFrom,to:i.contentTo,insert:`{${JSON.stringify(t.value)}}`}];else if(t.type==="move"||t.type==="remove")if(n.nodes.find(c=>c.id===i.parentID)?.structure||de("Only literal children of a supported stack/grid can be moved or removed."),t.type==="remove")r=[{from:i.from,to:i.to,insert:""}];else{const c=n.nodes.find(p=>p.id===t.parentID);(!c?.structure||c.from>=i.from&&c.to<=i.to)&&de("Choose a supported container outside this layer.");const h=p=>{for(;p.parentID!==null;)p=n.nodes.find(m=>m.id===p.parentID);return p.id};h(i)!==h(c)&&de("Layers cannot move between render branches or JavaScript scopes.");const u=c.children.filter(p=>p!==i.id),f=t.index;(!Number.isInteger(f)||f<0||f>u.length)&&de("Invalid layer position.");const d=f===u.length?c.contentTo:n.nodes.find(p=>p.id===u[f]).from;if(d===i.from||d===i.to)return{changes:[],selectedFrom:s};r=[{from:i.from,to:i.to,insert:""},{from:d,to:d,insert:e.slice(i.from,i.to)}],s=d-(d>i.to?i.to-i.from:0)}else if(t.type==="add"){i.structure||de("Choose a supported container with literal JSX children.");const a={text:'New text',spacer:"",stack:'',icon:''};Object.hasOwn(a,t.kind)||de("Unsupported new layer."),r=[{from:i.contentTo,to:i.contentTo,insert:a[t.kind]}],s=i.contentTo}else de("Unsupported layout operation.");r.sort((a,c)=>a.from-c.from);let o=e;for(const a of[...r].reverse())o=o.slice(0,a.from)+a.insert+o.slice(a.to);return wi(o).issue&&de("This edit would leave the supported layout. Your source was not changed."),{changes:r,selectedFrom:s}}const Dd=Lt.parser.configure({dialect:"jsx"}),Ci=n=>{const e=[];for(let t=n.firstChild;t;t=t.nextSibling)e.push(t);return e},Qe=n=>{throw new Error(n)},Gi=Object.freeze({background:{type:"color"},surface:{type:"color"},foreground:{type:"color"},muted:{type:"color"},accent:{type:"color"},titleFont:{values:Re.globalProperties.font.values},bodyFont:{values:Re.globalProperties.font.values},fontDesign:{values:Re.globalProperties.fontDesign.values},corner:{type:"number",maximum:48},padding:{type:"number",maximum:48},spacing:{type:"number",maximum:32}}),mr={titleFont:"headline",bodyFont:"caption",fontDesign:"rounded",corner:20,padding:16,spacing:10},rs=Object.freeze({Meadow:Object.freeze({...mr,background:"#F2FAF5",surface:"#FFFFFF",foreground:"#193F31",muted:"#607C70",accent:"#328963"}),Paper:Object.freeze({...mr,background:"#F4F7FD",surface:"#FFFFFF",foreground:"#243454",muted:"#687795",accent:"#4777D6"}),Peach:Object.freeze({...mr,background:"#FFF5EE",surface:"#FFFFFF",foreground:"#56392C",muted:"#967461",accent:"#C86B43"}),Midnight:Object.freeze({...mr,background:"#192332",surface:"#283548",foreground:"#F1F5FC",muted:"#AABAD0",accent:"#8EB7FF"})});function Xd(n,e){Object.hasOwn(Gi,n)||Qe("Unknown theme token.");const t=Gi[n];t.type==="color"&&!(typeof e=="string"&&/^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/i.test(e))&&Qe(`${n} needs a six or eight digit hex color.`),t.values&&!t.values.includes(e)&&Qe(`${n} needs a supported font value.`),t.type==="number"&&!(typeof e=="number"&&Number.isFinite(e)&&e>=0&&e<=t.maximum)&&Qe(`${n} needs a number from zero to ${t.maximum}.`)}function ea(n){(!n||typeof n!="object"||Array.isArray(n)||Object.keys(n).length!==Object.keys(Gi).length)&&Qe("A theme needs exactly the documented tokens.");for(const e of Object.keys(n))Xd(e,n[e]);return n}function Ld(n){(typeof n!="string"||new TextEncoder().encode(n).length>4096)&&Qe("Theme JSON is limited to 4 KiB.");const e=JSON.parse(n);return(!e||Array.isArray(e)||Object.keys(e).sort().join()!=="formatVersion,name,tokens"||e.formatVersion!==1||typeof e.name!="string"||!e.name.trim()||/[\u0000-\u001f]/.test(e.name)||new TextEncoder().encode(e.name).length>64)&&Qe("Use a version 1 theme with a name of at most 64 bytes."),ea(e.tokens),e}function E1(n,e="My theme"){const t=JSON.stringify({formatVersion:1,name:e,tokens:n},null,2);return Ld(t),t}function ss(n){const e={source:n,tokens:null,ranges:{},issue:"",canInitialize:!1};try{(typeof n!="string"||new TextEncoder().encode(n).length>512*1024)&&Qe("Theme editing supports files up to 512 KiB.");const t=Dd.parse(n),i=[],r=[];if(t.iterate({enter(a){a.type.isError&&Qe("Fix JavaScript syntax before editing the theme."),["VariableDefinition","VariableName"].includes(a.name)&&n.slice(a.from,a.to).includes("\\")&&Qe("Escaped identifiers stay in Code."),n.slice(a.from,a.to)==="widgetTheme"&&["VariableDefinition","VariableName"].includes(a.name)&&(a.name==="VariableDefinition"?i:r).push(a.node)}}),!i.length&&!r.length){const a=wi(n);e.canInitialize=!a.issue&&a.roots.length===1&&!a.nodes[0].locked;let c=!1;for(const h of Ci(t.topNode)){const u=h.firstChild;a.nodes[0]&&h.name==="ExpressionStatement"&&u?.name==="CallExpression"&&n.slice(u.firstChild.from,u.firstChild.to)==="$render"&&u.froma.nodes[0].to&&(c=!0)}return e.canInitialize&&=c,e}i.length!==1&&Qe("Keep one top-level const widgetTheme object. Other bindings stay in Code.");const s=i[0].parent,o=Ci(s);(s.name!=="VariableDeclaration"||s.parent?.name!=="Script"||o[0]?.name!=="const"||o[1]?.from!==i[0].from||o[2]?.name!=="Equals"||o[3]?.name!=="ObjectExpression"||o.slice(4).some(a=>a.name!==";"))&&Qe("Theme editing needs a single top-level const widgetTheme = { … } declaration.");const l={};for(const a of Ci(o[3])){if(["{","}",","].includes(a.name))continue;const c=Ci(a),h=c[0]?n.slice(c[0].from,c[0].to):"",u=c[0]?.name==="String"&&h.startsWith('"')?JSON.parse(h):h;(a.name!=="Property"||c.length!==3||!["PropertyDefinition","String"].includes(c[0].name)||c[1].name!==":"||!["String","Number"].includes(c[2].name)||Object.hasOwn(l,u))&&Qe("Theme tokens must be unique literal properties. Computed or dynamic values stay in Code.");let f;try{f=JSON.parse(n.slice(c[2].from,c[2].to))}catch{Qe("Theme values use JSON strings or numbers. Edit this declaration in Code.")}Xd(u,f),l[u]=f,e.ranges[u]={from:c[2].from,to:c[2].to}}ea(l);for(const a of r){const c=a.parent,h=Ci(c),u=h[2]?n.slice(h[2].from,h[2].to):"";(c.name!=="MemberExpression"||h[0].from!==a.from||h[1]?.name!=="."||h[2]?.name!=="PropertyName"||!Object.hasOwn(Gi,u))&&Qe("Theme references must read widgetTheme.token. Other uses stay in Code."),c.parent?.name==="MemberExpression"&&Qe("Mutable theme references stay in Code.");for(let f=c.parent;f;f=f.parent)(["UpdateExpression","PostfixExpression"].includes(f.name)||f.name==="UnaryExpression"&&/^(?:delete|\+\+|--)/.test(n.slice(f.from,c.from).trim())||f.name==="AssignmentExpression"&&f.firstChild.from<=c.from&&f.firstChild.to>=c.to||f.name==="ForOfSpec"||f.name==="ForInSpec")&&Qe("Mutable theme references stay in Code.")}e.tokens=l}catch(t){e.issue=t.message}return e}function ho(n,e,t){return(n.source!==e||n.issue||!n.tokens)&&Qe("Theme changed or is unsupported. Reopen Theme before editing."),ea(t),{changes:Object.keys(Gi).filter(r=>t[r]!==n.tokens[r]).map(r=>({...n.ranges[r],insert:JSON.stringify(t[r])})).sort((r,s)=>r.from-s.from)}}function U1(n,e="Meadow"){(!ss(n).canInitialize||!Object.hasOwn(rs,e))&&Qe("Start with a literal top-level layout without an existing widgetTheme binding.");const i=wi(n),r=i.nodes[0],s=[];function o(c,h,u,f=!1){const d=c.properties.find(m=>m.name===h);if(c.locked||d&&!d.editable||!d&&!f)return;const p=`${h}={widgetTheme.${u}}`;s.push(d?{from:d.from,to:d.to,insert:p}:{from:c.insertAt,to:c.insertAt,insert:` ${p}`})}for(const c of["background","corner","padding"])o(r,c,c,!0);r.structure&&o(r,"spacing","spacing",!0);for(const c of i.nodes)if(c.component==="Text"){const h=c.properties.find(f=>f.name==="font")?.value,u=["headline","largeTitle","title","title2","title3"].includes(h);o(c,"font",u?"titleFont":"bodyFont",!0),o(c,"color",u?"foreground":"muted",!0),o(c,"fontDesign","fontDesign",!0)}else c.component==="Icon"&&o(c,"color","accent",!0);let l=0;for(const c of Ci(Dd.parse(n).topNode))if(!(c.name==="LineComment"||c.name==="BlockComment"))if(c.name==="ExpressionStatement"&&c.firstChild?.name==="String")l=c.to;else break;s.push({from:l,to:l,insert:` const widgetTheme = ${JSON.stringify(rs[e],null,2)}; diff --git a/macOS/ScriptWidgetMac/StudioEditor.bundle/index.html b/macOS/ScriptWidgetMac/StudioEditor.bundle/index.html index af995b5a..f08a8fe2 100644 --- a/macOS/ScriptWidgetMac/StudioEditor.bundle/index.html +++ b/macOS/ScriptWidgetMac/StudioEditor.bundle/index.html @@ -5,7 +5,7 @@ ScriptWidget Web Studio - +