efficient-shell 0.0.0 → 4.126.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +758 -3
- package/dist/Browser-LM4RRZIL.js +478 -0
- package/dist/Browser-LM4RRZIL.js.map +1 -0
- package/dist/Calculator-HCKFTSJR.js +188 -0
- package/dist/Calculator-HCKFTSJR.js.map +1 -0
- package/dist/ConfirmDialog-LJXMB45A.js +3 -0
- package/dist/ConfirmDialog-LJXMB45A.js.map +1 -0
- package/dist/CurrencyConverter-BKKORN6K.js +232 -0
- package/dist/CurrencyConverter-BKKORN6K.js.map +1 -0
- package/dist/Documents-DD3QI5ER.js +437 -0
- package/dist/Documents-DD3QI5ER.js.map +1 -0
- package/dist/Files-BOPCPMJE.js +801 -0
- package/dist/Files-BOPCPMJE.js.map +1 -0
- package/dist/Notepad-KJOLF63O.js +406 -0
- package/dist/Notepad-KJOLF63O.js.map +1 -0
- package/dist/PdfViewer-LSP3YNFC.js +4 -0
- package/dist/PdfViewer-LSP3YNFC.js.map +1 -0
- package/dist/PomodoroTimer-SVJ62RXM.js +629 -0
- package/dist/PomodoroTimer-SVJ62RXM.js.map +1 -0
- package/dist/Preview-5UOC7ZRU.js +2955 -0
- package/dist/Preview-5UOC7ZRU.js.map +1 -0
- package/dist/Sidebar-J76PV4QF.js +290 -0
- package/dist/Sidebar-J76PV4QF.js.map +1 -0
- package/dist/Spreadsheet-J7BH7UHC.js +515 -0
- package/dist/Spreadsheet-J7BH7UHC.js.map +1 -0
- package/dist/Stock-CDUS4VCZ.js +154 -0
- package/dist/Stock-CDUS4VCZ.js.map +1 -0
- package/dist/Weather-EEDBWBPQ.js +427 -0
- package/dist/Weather-EEDBWBPQ.js.map +1 -0
- package/dist/WorldClock-FFFULVTJ.js +198 -0
- package/dist/WorldClock-FFFULVTJ.js.map +1 -0
- package/dist/apps/index.d.ts +267 -0
- package/dist/apps/index.js +147 -0
- package/dist/apps/index.js.map +1 -0
- package/dist/brand.css +2 -0
- package/dist/chunk-2VM2FRZJ.js +215 -0
- package/dist/chunk-2VM2FRZJ.js.map +1 -0
- package/dist/chunk-32W6VY2J.js +549 -0
- package/dist/chunk-32W6VY2J.js.map +1 -0
- package/dist/chunk-3CUSBZWG.js +59 -0
- package/dist/chunk-3CUSBZWG.js.map +1 -0
- package/dist/chunk-3L463T5H.js +26 -0
- package/dist/chunk-3L463T5H.js.map +1 -0
- package/dist/chunk-4TVV2BIU.js +13334 -0
- package/dist/chunk-4TVV2BIU.js.map +1 -0
- package/dist/chunk-6XQHH7D2.js +105 -0
- package/dist/chunk-6XQHH7D2.js.map +1 -0
- package/dist/chunk-7CCHEEYC.js +94 -0
- package/dist/chunk-7CCHEEYC.js.map +1 -0
- package/dist/chunk-7KIOEKHS.js +617 -0
- package/dist/chunk-7KIOEKHS.js.map +1 -0
- package/dist/chunk-AXWC3332.js +1103 -0
- package/dist/chunk-AXWC3332.js.map +1 -0
- package/dist/chunk-BFBWLCO6.js +16 -0
- package/dist/chunk-BFBWLCO6.js.map +1 -0
- package/dist/chunk-CT3HVZRZ.js +26 -0
- package/dist/chunk-CT3HVZRZ.js.map +1 -0
- package/dist/chunk-D7PYW2QS.js +265 -0
- package/dist/chunk-D7PYW2QS.js.map +1 -0
- package/dist/chunk-DMCFOWOJ.js +153 -0
- package/dist/chunk-DMCFOWOJ.js.map +1 -0
- package/dist/chunk-FQB24B42.js +416 -0
- package/dist/chunk-FQB24B42.js.map +1 -0
- package/dist/chunk-GA35NEND.js +56 -0
- package/dist/chunk-GA35NEND.js.map +1 -0
- package/dist/chunk-HNH7FOYB.js +69 -0
- package/dist/chunk-HNH7FOYB.js.map +1 -0
- package/dist/chunk-HYZ5QMVO.js +462 -0
- package/dist/chunk-HYZ5QMVO.js.map +1 -0
- package/dist/chunk-I464GYJJ.js +17 -0
- package/dist/chunk-I464GYJJ.js.map +1 -0
- package/dist/chunk-JFQGPY7W.js +819 -0
- package/dist/chunk-JFQGPY7W.js.map +1 -0
- package/dist/chunk-MGGY74RY.js +14 -0
- package/dist/chunk-MGGY74RY.js.map +1 -0
- package/dist/chunk-N3CG5HG5.js +144 -0
- package/dist/chunk-N3CG5HG5.js.map +1 -0
- package/dist/chunk-QRGFTK2X.js +79 -0
- package/dist/chunk-QRGFTK2X.js.map +1 -0
- package/dist/chunk-SZ4WMHUX.js +7 -0
- package/dist/chunk-SZ4WMHUX.js.map +1 -0
- package/dist/chunk-W6XTG23K.js +3708 -0
- package/dist/chunk-W6XTG23K.js.map +1 -0
- package/dist/chunk-YG45DAUT.js +96 -0
- package/dist/chunk-YG45DAUT.js.map +1 -0
- package/dist/chunk-YNGPTOL6.js +46 -0
- package/dist/chunk-YNGPTOL6.js.map +1 -0
- package/dist/chunk-YNHLQ24V.js +400 -0
- package/dist/chunk-YNHLQ24V.js.map +1 -0
- package/dist/chunk-YNKK7NE3.js +171 -0
- package/dist/chunk-YNKK7NE3.js.map +1 -0
- package/dist/chunk-YPO6RDMX.js +20 -0
- package/dist/chunk-YPO6RDMX.js.map +1 -0
- package/dist/file-intake/index.d.ts +87 -0
- package/dist/file-intake/index.js +3 -0
- package/dist/file-intake/index.js.map +1 -0
- package/dist/index-dQia6NpB.d.ts +5904 -0
- package/dist/index.d.ts +2495 -0
- package/dist/index.js +7460 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown/index.d.ts +118 -0
- package/dist/markdown/index.js +184 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/markup/index.d.ts +235 -0
- package/dist/markup/index.js +135 -0
- package/dist/markup/index.js.map +1 -0
- package/dist/page-tabs/index.d.ts +154 -0
- package/dist/page-tabs/index.js +706 -0
- package/dist/page-tabs/index.js.map +1 -0
- package/dist/shell.css +105 -0
- package/dist/sounds-NT4DEZGD.js +3 -0
- package/dist/sounds-NT4DEZGD.js.map +1 -0
- package/dist/styles.css +22 -0
- package/dist/themes.css +249 -0
- package/dist/types-BOinJXUf.d.ts +118 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui.css +1518 -0
- package/package.json +172 -3
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/forms/BrandMark.tsx
|
|
5
|
+
var SLOT_CLASS = {
|
|
6
|
+
favicon: "h-8 w-8",
|
|
7
|
+
compact: "h-10 w-10",
|
|
8
|
+
wordmark: "h-10 w-40"
|
|
9
|
+
};
|
|
10
|
+
function monogram(alt) {
|
|
11
|
+
return alt.trim().charAt(0).toLocaleUpperCase() || "\xB7";
|
|
12
|
+
}
|
|
13
|
+
function resolveBrandMarkTreatment(hasAlpha, isLight, surface) {
|
|
14
|
+
if (hasAlpha === false) return "framed";
|
|
15
|
+
if (hasAlpha == null || isLight == null) return "plate-light";
|
|
16
|
+
if (isLight) return surface === "light" ? "plate-dark" : "bare";
|
|
17
|
+
return surface === "dark" ? "plate-light" : "bare";
|
|
18
|
+
}
|
|
19
|
+
var TREATMENT_CLASS = {
|
|
20
|
+
"plate-light": "bg-white shadow-[0_2px_12px_rgba(0,0,0,0.18)]",
|
|
21
|
+
"plate-dark": "bg-neutral-900 shadow-[0_2px_12px_rgba(0,0,0,0.25)]",
|
|
22
|
+
framed: "border border-black/10 shadow-[0_2px_12px_rgba(0,0,0,0.18)]",
|
|
23
|
+
bare: ""
|
|
24
|
+
};
|
|
25
|
+
function treatmentStyle(treatment, radius, padding) {
|
|
26
|
+
if (treatment === "bare") return {};
|
|
27
|
+
return treatment === "framed" ? { borderRadius: radius } : { borderRadius: radius, padding };
|
|
28
|
+
}
|
|
29
|
+
function BrandMark({
|
|
30
|
+
src,
|
|
31
|
+
fallbackSrc,
|
|
32
|
+
fallbackHasAlpha = null,
|
|
33
|
+
fallbackIsLight = null,
|
|
34
|
+
fallbackAdaptive,
|
|
35
|
+
alt,
|
|
36
|
+
slot = "compact",
|
|
37
|
+
surface = "light",
|
|
38
|
+
size,
|
|
39
|
+
width,
|
|
40
|
+
height,
|
|
41
|
+
decorative = false,
|
|
42
|
+
fallbackMode = "monogram",
|
|
43
|
+
adaptive = false,
|
|
44
|
+
hasAlpha = null,
|
|
45
|
+
isLight = null,
|
|
46
|
+
treatmentPadding = 4,
|
|
47
|
+
treatmentRadius = 8,
|
|
48
|
+
className = "",
|
|
49
|
+
style
|
|
50
|
+
}) {
|
|
51
|
+
const [activeSrc, setActiveSrc] = useState(src || fallbackSrc || "");
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
setActiveSrc(src || fallbackSrc || "");
|
|
54
|
+
}, [src, fallbackSrc]);
|
|
55
|
+
const usingFallback = Boolean(
|
|
56
|
+
fallbackSrc && activeSrc === fallbackSrc && activeSrc !== src
|
|
57
|
+
);
|
|
58
|
+
const activeAdaptive = usingFallback ? fallbackAdaptive ?? adaptive : adaptive;
|
|
59
|
+
const activeHasAlpha = usingFallback ? fallbackHasAlpha : hasAlpha;
|
|
60
|
+
const activeIsLight = usingFallback ? fallbackIsLight : isLight;
|
|
61
|
+
const treatment = activeAdaptive ? resolveBrandMarkTreatment(activeHasAlpha, activeIsLight, surface) : "bare";
|
|
62
|
+
const frameClass = [
|
|
63
|
+
"inline-flex shrink-0 items-center justify-center overflow-hidden",
|
|
64
|
+
size == null && width == null && height == null ? SLOT_CLASS[slot] : "",
|
|
65
|
+
surface === "dark" ? "text-white" : "text-gray-700",
|
|
66
|
+
TREATMENT_CLASS[treatment],
|
|
67
|
+
className
|
|
68
|
+
].filter(Boolean).join(" ");
|
|
69
|
+
const frameStyle = {
|
|
70
|
+
...treatmentStyle(treatment, treatmentRadius, treatmentPadding),
|
|
71
|
+
...size == null ? { width, height } : { width: size, height: size },
|
|
72
|
+
...style
|
|
73
|
+
};
|
|
74
|
+
if (!activeSrc) {
|
|
75
|
+
if (fallbackMode === "none") return null;
|
|
76
|
+
return /* @__PURE__ */ jsx(
|
|
77
|
+
"span",
|
|
78
|
+
{
|
|
79
|
+
"data-brand-fallback": true,
|
|
80
|
+
"data-brand-slot": slot,
|
|
81
|
+
"aria-label": decorative ? void 0 : alt,
|
|
82
|
+
"aria-hidden": decorative || void 0,
|
|
83
|
+
role: decorative ? void 0 : "img",
|
|
84
|
+
className: `${frameClass} rounded-md bg-gray-100 font-semibold`,
|
|
85
|
+
style: frameStyle,
|
|
86
|
+
children: decorative ? null : monogram(alt)
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return /* @__PURE__ */ jsx("span", { className: frameClass, "data-brand-treatment": treatment, style: frameStyle, children: /* @__PURE__ */ jsx(
|
|
91
|
+
"img",
|
|
92
|
+
{
|
|
93
|
+
src: activeSrc,
|
|
94
|
+
alt: decorative ? "" : alt,
|
|
95
|
+
"data-brand-slot": slot,
|
|
96
|
+
className: "block max-h-full max-w-full",
|
|
97
|
+
style: { objectFit: "contain" },
|
|
98
|
+
onError: () => setActiveSrc((current) => fallbackSrc && current !== fallbackSrc ? fallbackSrc : "")
|
|
99
|
+
}
|
|
100
|
+
) });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { BrandMark, resolveBrandMarkTreatment };
|
|
104
|
+
//# sourceMappingURL=chunk-6XQHH7D2.js.map
|
|
105
|
+
//# sourceMappingURL=chunk-6XQHH7D2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/forms/BrandMark.tsx"],"names":[],"mappings":"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","file":"chunk-6XQHH7D2.js","sourcesContent":["import { useEffect, useState, type CSSProperties } from 'react';\n\nexport type BrandMarkSlot = 'favicon' | 'compact' | 'wordmark';\nexport type BrandMarkSurface = 'light' | 'dark';\nexport type BrandMarkTreatment = 'plate-light' | 'plate-dark' | 'framed' | 'bare';\n\nexport interface BrandMarkProps {\n src?: string | null;\n fallbackSrc?: string | null;\n fallbackHasAlpha?: boolean | null;\n fallbackIsLight?: boolean | null;\n fallbackAdaptive?: boolean;\n alt: string;\n slot?: BrandMarkSlot;\n surface?: BrandMarkSurface;\n /** Override the square slot size in pixels for compact shell chrome. */\n size?: number;\n /** Override a natural-aspect slot width without forcing square dimensions. */\n width?: number | string;\n /** Override a natural-aspect slot height without forcing square dimensions. */\n height?: number | string;\n /** Mark the image decorative when the adjacent text already names it. */\n decorative?: boolean;\n /** Hide the slot after load failure when surrounding UI owns the fallback. */\n fallbackMode?: 'monogram' | 'none';\n /** Keep an arbitrary tenant mark readable without recolouring the artwork. */\n adaptive?: boolean;\n /** Server-detected transparency hint for the selected asset. */\n hasAlpha?: boolean | null;\n /** Server-detected tone hint for the selected asset. */\n isLight?: boolean | null;\n treatmentPadding?: number | string;\n treatmentRadius?: number;\n className?: string;\n style?: CSSProperties;\n}\n\nconst SLOT_CLASS: Record<BrandMarkSlot, string> = {\n favicon: 'h-8 w-8',\n compact: 'h-10 w-10',\n wordmark: 'h-10 w-40',\n};\n\nfunction monogram(alt: string) {\n return alt.trim().charAt(0).toLocaleUpperCase() || '·';\n}\n\n/**\n * Chooses the smallest backing needed to preserve contrast. Opaque artwork\n * carries its own background; transparent artwork only needs a plate when its\n * tone would disappear into the current surface. Unknown artwork gets the\n * conservative light plate used by every portal.\n */\nexport function resolveBrandMarkTreatment(\n hasAlpha: boolean | null,\n isLight: boolean | null,\n surface: BrandMarkSurface,\n): BrandMarkTreatment {\n if (hasAlpha === false) return 'framed';\n if (hasAlpha == null || isLight == null) return 'plate-light';\n if (isLight) return surface === 'light' ? 'plate-dark' : 'bare';\n return surface === 'dark' ? 'plate-light' : 'bare';\n}\n\n/** The plate's colour and border are classes, so they follow the theme like\n * the rest of the file; only the caller-tunable radius and padding are\n * inline, because those arrive as numbers. */\nconst TREATMENT_CLASS: Record<BrandMarkTreatment, string> = {\n 'plate-light': 'bg-white shadow-[0_2px_12px_rgba(0,0,0,0.18)]',\n 'plate-dark': 'bg-neutral-900 shadow-[0_2px_12px_rgba(0,0,0,0.25)]',\n framed: 'border border-black/10 shadow-[0_2px_12px_rgba(0,0,0,0.18)]',\n bare: '',\n};\n\nfunction treatmentStyle(\n treatment: BrandMarkTreatment,\n radius: number,\n padding: number | string,\n): CSSProperties {\n if (treatment === 'bare') return {};\n return treatment === 'framed'\n ? { borderRadius: radius }\n : { borderRadius: radius, padding };\n}\n\n/**\n * Renders a brand asset inside a stable portal slot without ever cropping or\n * stretching the source. Raster normalisation belongs to the server; this\n * component owns only the presentation contract shared by portal surfaces.\n */\nexport default function BrandMark({\n src,\n fallbackSrc,\n fallbackHasAlpha = null,\n fallbackIsLight = null,\n fallbackAdaptive,\n alt,\n slot = 'compact',\n surface = 'light',\n size,\n width,\n height,\n decorative = false,\n fallbackMode = 'monogram',\n adaptive = false,\n hasAlpha = null,\n isLight = null,\n treatmentPadding = 4,\n treatmentRadius = 8,\n className = '',\n style,\n}: BrandMarkProps) {\n const [activeSrc, setActiveSrc] = useState(src || fallbackSrc || '');\n\n useEffect(() => {\n setActiveSrc(src || fallbackSrc || '');\n }, [src, fallbackSrc]);\n\n const usingFallback = Boolean(\n fallbackSrc && activeSrc === fallbackSrc && activeSrc !== src,\n );\n const activeAdaptive = usingFallback ? fallbackAdaptive ?? adaptive : adaptive;\n const activeHasAlpha = usingFallback ? fallbackHasAlpha : hasAlpha;\n const activeIsLight = usingFallback ? fallbackIsLight : isLight;\n const treatment = activeAdaptive\n ? resolveBrandMarkTreatment(activeHasAlpha, activeIsLight, surface)\n : 'bare';\n const frameClass = [\n 'inline-flex shrink-0 items-center justify-center overflow-hidden',\n size == null && width == null && height == null ? SLOT_CLASS[slot] : '',\n surface === 'dark' ? 'text-white' : 'text-gray-700',\n TREATMENT_CLASS[treatment],\n className,\n ].filter(Boolean).join(' ');\n const frameStyle: CSSProperties = {\n ...treatmentStyle(treatment, treatmentRadius, treatmentPadding),\n ...(size == null ? { width, height } : { width: size, height: size }),\n ...style,\n };\n\n if (!activeSrc) {\n if (fallbackMode === 'none') return null;\n return (\n <span\n data-brand-fallback\n data-brand-slot={slot}\n aria-label={decorative ? undefined : alt}\n aria-hidden={decorative || undefined}\n role={decorative ? undefined : 'img'}\n className={`${frameClass} rounded-md bg-gray-100 font-semibold`}\n style={frameStyle}\n >\n {decorative ? null : monogram(alt)}\n </span>\n );\n }\n\n return (\n <span className={frameClass} data-brand-treatment={treatment} style={frameStyle}>\n <img\n src={activeSrc}\n alt={decorative ? '' : alt}\n data-brand-slot={slot}\n className=\"block max-h-full max-w-full\"\n style={{ objectFit: 'contain' }}\n onError={() => setActiveSrc(current => (\n fallbackSrc && current !== fallbackSrc ? fallbackSrc : ''\n ))}\n />\n </span>\n );\n}\n"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/apps/_weatherData.ts
|
|
2
|
+
var WMO = {
|
|
3
|
+
0: ["Clear Sky", "\u2600\uFE0F", "\u{1F319}", "from-sky-400 to-blue-500", "from-indigo-900 to-slate-950"],
|
|
4
|
+
1: ["Mainly Clear", "\u{1F324}\uFE0F", "\u{1F319}", "from-sky-400 to-blue-500", "from-indigo-900 to-slate-950"],
|
|
5
|
+
2: ["Partly Cloudy", "\u26C5", "\u2601\uFE0F", "from-sky-400 to-blue-400", "from-indigo-800 to-slate-900"],
|
|
6
|
+
3: ["Overcast", "\u2601\uFE0F", "\u2601\uFE0F", "from-gray-400 to-gray-500", "from-gray-700 to-slate-900"],
|
|
7
|
+
45: ["Foggy", "\u{1F32B}\uFE0F", "\u{1F32B}\uFE0F", "from-gray-400 to-gray-500", "from-gray-700 to-slate-800"],
|
|
8
|
+
48: ["Foggy", "\u{1F32B}\uFE0F", "\u{1F32B}\uFE0F", "from-gray-400 to-gray-500", "from-gray-700 to-slate-800"],
|
|
9
|
+
51: ["Light Drizzle", "\u{1F326}\uFE0F", "\u{1F327}\uFE0F", "from-gray-400 to-blue-500", "from-gray-700 to-indigo-900"],
|
|
10
|
+
53: ["Drizzle", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-500 to-blue-600", "from-gray-700 to-indigo-900"],
|
|
11
|
+
55: ["Heavy Drizzle", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-500 to-blue-600", "from-gray-700 to-indigo-900"],
|
|
12
|
+
61: ["Light Rain", "\u{1F326}\uFE0F", "\u{1F327}\uFE0F", "from-gray-400 to-blue-500", "from-gray-700 to-indigo-900"],
|
|
13
|
+
63: ["Rain", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-500 to-blue-600", "from-gray-700 to-indigo-900"],
|
|
14
|
+
65: ["Heavy Rain", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-600 to-blue-700", "from-gray-700 to-indigo-950"],
|
|
15
|
+
71: ["Light Snow", "\u{1F328}\uFE0F", "\u{1F328}\uFE0F", "from-blue-200 to-blue-400", "from-blue-800 to-slate-900"],
|
|
16
|
+
73: ["Snow", "\u2744\uFE0F", "\u2744\uFE0F", "from-blue-300 to-blue-500", "from-blue-800 to-slate-900"],
|
|
17
|
+
75: ["Heavy Snow", "\u2744\uFE0F", "\u2744\uFE0F", "from-blue-400 to-blue-600", "from-blue-800 to-slate-900"],
|
|
18
|
+
80: ["Rain Showers", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-500 to-blue-600", "from-gray-700 to-indigo-900"],
|
|
19
|
+
82: ["Heavy Showers", "\u{1F327}\uFE0F", "\u{1F327}\uFE0F", "from-gray-600 to-blue-700", "from-gray-700 to-indigo-950"],
|
|
20
|
+
95: ["Thunderstorm", "\u26C8\uFE0F", "\u26C8\uFE0F", "from-gray-700 to-indigo-800", "from-gray-800 to-indigo-950"],
|
|
21
|
+
96: ["Thunderstorm", "\u26C8\uFE0F", "\u26C8\uFE0F", "from-gray-700 to-indigo-800", "from-gray-800 to-indigo-950"],
|
|
22
|
+
99: ["Thunderstorm", "\u26C8\uFE0F", "\u26C8\uFE0F", "from-gray-700 to-indigo-900", "from-gray-800 to-indigo-950"]
|
|
23
|
+
};
|
|
24
|
+
var getCondition = (code, isDay = true) => {
|
|
25
|
+
const entry = WMO[code] || ["Unknown", "\u2753", "\u2753", "from-gray-400 to-gray-500", "from-gray-700 to-slate-800"];
|
|
26
|
+
return [entry[0], isDay ? entry[1] : entry[2], isDay ? entry[3] : entry[4]];
|
|
27
|
+
};
|
|
28
|
+
var AVAILABLE_CITIES = {
|
|
29
|
+
"Sydney": { lat: -33.8688, lon: 151.2093, tz: "Australia/Sydney" },
|
|
30
|
+
"Melbourne": { lat: -37.8136, lon: 144.9631, tz: "Australia/Melbourne" },
|
|
31
|
+
"Auckland": { lat: -36.8485, lon: 174.7633, tz: "Pacific/Auckland" },
|
|
32
|
+
"Tokyo": { lat: 35.6762, lon: 139.6503, tz: "Asia/Tokyo" },
|
|
33
|
+
"Shanghai": { lat: 31.2304, lon: 121.4737, tz: "Asia/Shanghai" },
|
|
34
|
+
"Hong Kong": { lat: 22.3193, lon: 114.1694, tz: "Asia/Hong_Kong" },
|
|
35
|
+
"Singapore": { lat: 1.3521, lon: 103.8198, tz: "Asia/Singapore" },
|
|
36
|
+
"Bangkok": { lat: 13.7563, lon: 100.5018, tz: "Asia/Bangkok" },
|
|
37
|
+
"Mumbai": { lat: 19.076, lon: 72.8777, tz: "Asia/Kolkata" },
|
|
38
|
+
"Dubai": { lat: 25.2048, lon: 55.2708, tz: "Asia/Dubai" },
|
|
39
|
+
"Berlin": { lat: 52.52, lon: 13.405, tz: "Europe/Berlin" },
|
|
40
|
+
"Paris": { lat: 48.8566, lon: 2.3522, tz: "Europe/Paris" },
|
|
41
|
+
"London": { lat: 51.5074, lon: -0.1278, tz: "Europe/London" },
|
|
42
|
+
"New York": { lat: 40.7128, lon: -74.006, tz: "America/New_York" },
|
|
43
|
+
"Miami": { lat: 25.7617, lon: -80.1918, tz: "America/New_York" },
|
|
44
|
+
"Toronto": { lat: 43.6532, lon: -79.3832, tz: "America/Toronto" },
|
|
45
|
+
"Chicago": { lat: 41.8781, lon: -87.6298, tz: "America/Chicago" },
|
|
46
|
+
"Los Angeles": { lat: 34.0522, lon: -118.2437, tz: "America/Los_Angeles" }
|
|
47
|
+
};
|
|
48
|
+
var WEATHER_CACHE_KEY = "weather_local_cache";
|
|
49
|
+
var WEATHER_CACHE_TTL = 30 * 60 * 1e3;
|
|
50
|
+
function readCache() {
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(localStorage.getItem(WEATHER_CACHE_KEY) || "{}");
|
|
53
|
+
} catch {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function writeCache(c) {
|
|
58
|
+
try {
|
|
59
|
+
localStorage.setItem(WEATHER_CACHE_KEY, JSON.stringify(c));
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function fetchCityWeather(cityName, force = false) {
|
|
64
|
+
const coords = AVAILABLE_CITIES[cityName];
|
|
65
|
+
if (!coords) return null;
|
|
66
|
+
if (!force) {
|
|
67
|
+
const cached = readCache()[cityName];
|
|
68
|
+
if (cached && Date.now() - cached.ts < WEATHER_CACHE_TTL) return cached.data;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const res = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${coords.lat}&longitude=${coords.lon}¤t=temperature_2m,weather_code,is_day&daily=temperature_2m_max,temperature_2m_min&forecast_days=1&timezone=auto`);
|
|
72
|
+
const w = await res.json();
|
|
73
|
+
const cw = {
|
|
74
|
+
city: cityName,
|
|
75
|
+
temp: Math.round(w.current.temperature_2m),
|
|
76
|
+
code: w.current.weather_code,
|
|
77
|
+
high: Math.round(w.daily.temperature_2m_max[0]),
|
|
78
|
+
low: Math.round(w.daily.temperature_2m_min[0]),
|
|
79
|
+
isDay: w.current.is_day === 1,
|
|
80
|
+
timezone: w.timezone || "UTC"
|
|
81
|
+
};
|
|
82
|
+
const c = readCache();
|
|
83
|
+
c[cityName] = { data: cw, ts: Date.now() };
|
|
84
|
+
writeCache(c);
|
|
85
|
+
return cw;
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
var toFahrenheit = (c) => Math.round(c * 9 / 5 + 32);
|
|
91
|
+
|
|
92
|
+
export { AVAILABLE_CITIES, fetchCityWeather, getCondition, toFahrenheit };
|
|
93
|
+
//# sourceMappingURL=chunk-7CCHEEYC.js.map
|
|
94
|
+
//# sourceMappingURL=chunk-7CCHEEYC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/apps/_weatherData.ts"],"names":[],"mappings":";AASO,IAAM,GAAA,GAAgE;AAAA,EAC3E,GAAG,CAAC,WAAA,EAAa,cAAA,EAAM,WAAA,EAAM,4BAA4B,8BAA8B,CAAA;AAAA,EACvF,GAAG,CAAC,cAAA,EAAgB,iBAAA,EAAO,WAAA,EAAM,4BAA4B,8BAA8B,CAAA;AAAA,EAC3F,GAAG,CAAC,eAAA,EAAiB,QAAA,EAAK,cAAA,EAAM,4BAA4B,8BAA8B,CAAA;AAAA,EAC1F,GAAG,CAAC,UAAA,EAAY,cAAA,EAAM,cAAA,EAAM,6BAA6B,4BAA4B,CAAA;AAAA,EACrF,IAAI,CAAC,OAAA,EAAS,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,4BAA4B,CAAA;AAAA,EACrF,IAAI,CAAC,OAAA,EAAS,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,4BAA4B,CAAA;AAAA,EACrF,IAAI,CAAC,eAAA,EAAiB,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,6BAA6B,CAAA;AAAA,EAC9F,IAAI,CAAC,SAAA,EAAW,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,6BAA6B,CAAA;AAAA,EACxF,IAAI,CAAC,eAAA,EAAiB,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,6BAA6B,CAAA;AAAA,EAC9F,IAAI,CAAC,YAAA,EAAc,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,6BAA6B,CAAA;AAAA,EAC3F,IAAI,CAAC,MAAA,EAAQ,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,6BAA6B,CAAA;AAAA,EACrF,IAAI,CAAC,YAAA,EAAc,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,6BAA6B,CAAA;AAAA,EAC3F,IAAI,CAAC,YAAA,EAAc,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,4BAA4B,CAAA;AAAA,EAC1F,IAAI,CAAC,MAAA,EAAQ,cAAA,EAAM,cAAA,EAAM,6BAA6B,4BAA4B,CAAA;AAAA,EAClF,IAAI,CAAC,YAAA,EAAc,cAAA,EAAM,cAAA,EAAM,6BAA6B,4BAA4B,CAAA;AAAA,EACxF,IAAI,CAAC,cAAA,EAAgB,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,6BAA6B,CAAA;AAAA,EAC7F,IAAI,CAAC,eAAA,EAAiB,iBAAA,EAAO,iBAAA,EAAO,6BAA6B,6BAA6B,CAAA;AAAA,EAC9F,IAAI,CAAC,cAAA,EAAgB,cAAA,EAAM,cAAA,EAAM,+BAA+B,6BAA6B,CAAA;AAAA,EAC7F,IAAI,CAAC,cAAA,EAAgB,cAAA,EAAM,cAAA,EAAM,+BAA+B,6BAA6B,CAAA;AAAA,EAC7F,IAAI,CAAC,cAAA,EAAgB,cAAA,EAAM,cAAA,EAAM,+BAA+B,6BAA6B;AAC/F,CAAA;AAEO,IAAM,YAAA,GAAe,CAAC,IAAA,EAAc,KAAA,GAAQ,IAAA,KAAmC;AACpF,EAAA,MAAM,KAAA,GAAQ,IAAI,IAAI,CAAA,IAAK,CAAC,SAAA,EAAW,QAAA,EAAK,QAAA,EAAK,2BAAA,EAA6B,4BAA4B,CAAA;AAC1G,EAAA,OAAO,CAAC,KAAA,CAAM,CAAC,CAAA,EAAG,KAAA,GAAQ,MAAM,CAAC,CAAA,GAAI,KAAA,CAAM,CAAC,GAAG,KAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAI,KAAA,CAAM,CAAC,CAAC,CAAA;AAC5E;AAEO,IAAM,gBAAA,GAA6E;AAAA,EACxF,UAAgB,EAAE,GAAA,EAAK,UAAU,GAAA,EAAK,QAAA,EAAU,IAAI,kBAAA,EAAmB;AAAA,EACvE,aAAgB,EAAE,GAAA,EAAK,UAAU,GAAA,EAAK,QAAA,EAAU,IAAI,qBAAA,EAAsB;AAAA,EAC1E,YAAgB,EAAE,GAAA,EAAK,UAAU,GAAA,EAAK,QAAA,EAAU,IAAI,kBAAA,EAAmB;AAAA,EACvE,SAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,QAAA,EAAU,IAAI,YAAA,EAAa;AAAA,EACjE,YAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,QAAA,EAAU,IAAI,eAAA,EAAgB;AAAA,EACpE,aAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,QAAA,EAAU,IAAI,gBAAA,EAAiB;AAAA,EACrE,aAAgB,EAAE,GAAA,EAAK,QAAU,GAAA,EAAK,QAAA,EAAU,IAAI,gBAAA,EAAiB;AAAA,EACrE,WAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,QAAA,EAAU,IAAI,cAAA,EAAe;AAAA,EACnE,UAAgB,EAAE,GAAA,EAAK,QAAU,GAAA,EAAK,OAAA,EAAU,IAAI,cAAA,EAAe;AAAA,EACnE,SAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,OAAA,EAAU,IAAI,YAAA,EAAa;AAAA,EACjE,UAAgB,EAAE,GAAA,EAAK,OAAU,GAAA,EAAK,MAAA,EAAU,IAAI,eAAA,EAAgB;AAAA,EACpE,SAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,MAAA,EAAU,IAAI,cAAA,EAAe;AAAA,EACnE,UAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,OAAA,EAAU,IAAI,eAAA,EAAgB;AAAA,EACpE,YAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,OAAA,EAAU,IAAI,kBAAA,EAAmB;AAAA,EACvE,SAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,QAAA,EAAU,IAAI,kBAAA,EAAmB;AAAA,EACvE,WAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,QAAA,EAAU,IAAI,iBAAA,EAAkB;AAAA,EACtE,WAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,QAAA,EAAU,IAAI,iBAAA,EAAkB;AAAA,EACtE,eAAgB,EAAE,GAAA,EAAK,SAAU,GAAA,EAAK,SAAA,EAAW,IAAI,qBAAA;AACvD;AAEO,IAAM,iBAAA,GAAoB,qBAAA;AAC1B,IAAM,iBAAA,GAAoB,KAAK,EAAA,GAAK,GAAA;AAe3C,SAAS,SAAA,GAA0B;AACjC,EAAA,IAAI;AAAE,IAAA,OAAO,KAAK,KAAA,CAAM,YAAA,CAAa,OAAA,CAAQ,iBAAiB,KAAK,IAAI,CAAA;AAAA,EAAG,CAAA,CAAA,MAAQ;AAAE,IAAA,OAAO,EAAC;AAAA,EAAG;AACjG;AACA,SAAS,WAAW,CAAA,EAAiB;AACnC,EAAA,IAAI;AAAE,IAAA,YAAA,CAAa,OAAA,CAAQ,iBAAA,EAAmB,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,CAAA;AAAA,EAAG,CAAA,CAAA,MAAQ;AAAA,EAAC;AAC7E;AAIA,eAAsB,gBAAA,CAAiB,QAAA,EAAkB,KAAA,GAAQ,KAAA,EAAoC;AACnG,EAAA,MAAM,MAAA,GAAS,iBAAiB,QAAQ,CAAA;AACxC,EAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AACpB,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,MAAA,GAAS,SAAA,EAAU,CAAE,QAAQ,CAAA;AACnC,IAAA,IAAI,MAAA,IAAU,KAAK,GAAA,EAAI,GAAI,OAAO,EAAA,GAAK,iBAAA,SAA0B,MAAA,CAAO,IAAA;AAAA,EAC1E;AACA,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,CAAA,gDAAA,EAAmD,OAAO,GAAG,CAAA,WAAA,EAAc,MAAA,CAAO,GAAG,CAAA,qHAAA,CAAuH,CAAA;AACpO,IAAA,MAAM,CAAA,GAAI,MAAM,GAAA,CAAI,IAAA,EAAK;AACzB,IAAA,MAAM,EAAA,GAAkB;AAAA,MACtB,IAAA,EAAM,QAAA;AAAA,MACN,IAAA,EAAM,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,QAAQ,cAAc,CAAA;AAAA,MACzC,IAAA,EAAM,EAAE,OAAA,CAAQ,YAAA;AAAA,MAChB,MAAM,IAAA,CAAK,KAAA,CAAM,EAAE,KAAA,CAAM,kBAAA,CAAmB,CAAC,CAAC,CAAA;AAAA,MAC9C,KAAK,IAAA,CAAK,KAAA,CAAM,EAAE,KAAA,CAAM,kBAAA,CAAmB,CAAC,CAAC,CAAA;AAAA,MAC7C,KAAA,EAAO,CAAA,CAAE,OAAA,CAAQ,MAAA,KAAW,CAAA;AAAA,MAC5B,QAAA,EAAU,EAAE,QAAA,IAAY;AAAA,KAC1B;AACA,IAAA,MAAM,IAAI,SAAA,EAAU;AACpB,IAAA,CAAA,CAAE,QAAQ,IAAI,EAAE,IAAA,EAAM,IAAI,EAAA,EAAI,IAAA,CAAK,KAAI,EAAE;AACzC,IAAA,UAAA,CAAW,CAAC,CAAA;AACZ,IAAA,OAAO,EAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEO,IAAM,YAAA,GAAe,CAAC,CAAA,KAAc,IAAA,CAAK,MAAM,CAAA,GAAI,CAAA,GAAI,IAAI,EAAE","file":"chunk-7CCHEEYC.js","sourcesContent":["/**\n * Shared weather data — used by Weather (single-city tile with animations)\n * and WorldClock (multi-city list with per-row weather + day/night via the\n * emoji). Single source of truth for the WMO code map, the supported city\n * coordinates, and the localStorage cache so both widgets share fetched\n * data.\n */\n\n// [condition, day emoji, night emoji, day gradient, night gradient]\nexport const WMO: Record<number, [string, string, string, string, string]> = {\n 0: ['Clear Sky', '☀️', '🌙', 'from-sky-400 to-blue-500', 'from-indigo-900 to-slate-950'],\n 1: ['Mainly Clear', '🌤️', '🌙', 'from-sky-400 to-blue-500', 'from-indigo-900 to-slate-950'],\n 2: ['Partly Cloudy', '⛅', '☁️', 'from-sky-400 to-blue-400', 'from-indigo-800 to-slate-900'],\n 3: ['Overcast', '☁️', '☁️', 'from-gray-400 to-gray-500', 'from-gray-700 to-slate-900'],\n 45: ['Foggy', '🌫️', '🌫️', 'from-gray-400 to-gray-500', 'from-gray-700 to-slate-800'],\n 48: ['Foggy', '🌫️', '🌫️', 'from-gray-400 to-gray-500', 'from-gray-700 to-slate-800'],\n 51: ['Light Drizzle', '🌦️', '🌧️', 'from-gray-400 to-blue-500', 'from-gray-700 to-indigo-900'],\n 53: ['Drizzle', '🌧️', '🌧️', 'from-gray-500 to-blue-600', 'from-gray-700 to-indigo-900'],\n 55: ['Heavy Drizzle', '🌧️', '🌧️', 'from-gray-500 to-blue-600', 'from-gray-700 to-indigo-900'],\n 61: ['Light Rain', '🌦️', '🌧️', 'from-gray-400 to-blue-500', 'from-gray-700 to-indigo-900'],\n 63: ['Rain', '🌧️', '🌧️', 'from-gray-500 to-blue-600', 'from-gray-700 to-indigo-900'],\n 65: ['Heavy Rain', '🌧️', '🌧️', 'from-gray-600 to-blue-700', 'from-gray-700 to-indigo-950'],\n 71: ['Light Snow', '🌨️', '🌨️', 'from-blue-200 to-blue-400', 'from-blue-800 to-slate-900'],\n 73: ['Snow', '❄️', '❄️', 'from-blue-300 to-blue-500', 'from-blue-800 to-slate-900'],\n 75: ['Heavy Snow', '❄️', '❄️', 'from-blue-400 to-blue-600', 'from-blue-800 to-slate-900'],\n 80: ['Rain Showers', '🌧️', '🌧️', 'from-gray-500 to-blue-600', 'from-gray-700 to-indigo-900'],\n 82: ['Heavy Showers', '🌧️', '🌧️', 'from-gray-600 to-blue-700', 'from-gray-700 to-indigo-950'],\n 95: ['Thunderstorm', '⛈️', '⛈️', 'from-gray-700 to-indigo-800', 'from-gray-800 to-indigo-950'],\n 96: ['Thunderstorm', '⛈️', '⛈️', 'from-gray-700 to-indigo-800', 'from-gray-800 to-indigo-950'],\n 99: ['Thunderstorm', '⛈️', '⛈️', 'from-gray-700 to-indigo-900', 'from-gray-800 to-indigo-950'],\n};\n\nexport const getCondition = (code: number, isDay = true): [string, string, string] => {\n const entry = WMO[code] || ['Unknown', '❓', '❓', 'from-gray-400 to-gray-500', 'from-gray-700 to-slate-800'];\n return [entry[0], isDay ? entry[1] : entry[2], isDay ? entry[3] : entry[4]];\n};\n\nexport const AVAILABLE_CITIES: Record<string, { lat: number; lon: number; tz: string }> = {\n 'Sydney': { lat: -33.8688, lon: 151.2093, tz: 'Australia/Sydney' },\n 'Melbourne': { lat: -37.8136, lon: 144.9631, tz: 'Australia/Melbourne' },\n 'Auckland': { lat: -36.8485, lon: 174.7633, tz: 'Pacific/Auckland' },\n 'Tokyo': { lat: 35.6762, lon: 139.6503, tz: 'Asia/Tokyo' },\n 'Shanghai': { lat: 31.2304, lon: 121.4737, tz: 'Asia/Shanghai' },\n 'Hong Kong': { lat: 22.3193, lon: 114.1694, tz: 'Asia/Hong_Kong' },\n 'Singapore': { lat: 1.3521, lon: 103.8198, tz: 'Asia/Singapore' },\n 'Bangkok': { lat: 13.7563, lon: 100.5018, tz: 'Asia/Bangkok' },\n 'Mumbai': { lat: 19.0760, lon: 72.8777, tz: 'Asia/Kolkata' },\n 'Dubai': { lat: 25.2048, lon: 55.2708, tz: 'Asia/Dubai' },\n 'Berlin': { lat: 52.5200, lon: 13.4050, tz: 'Europe/Berlin' },\n 'Paris': { lat: 48.8566, lon: 2.3522, tz: 'Europe/Paris' },\n 'London': { lat: 51.5074, lon: -0.1278, tz: 'Europe/London' },\n 'New York': { lat: 40.7128, lon: -74.0060, tz: 'America/New_York' },\n 'Miami': { lat: 25.7617, lon: -80.1918, tz: 'America/New_York' },\n 'Toronto': { lat: 43.6532, lon: -79.3832, tz: 'America/Toronto' },\n 'Chicago': { lat: 41.8781, lon: -87.6298, tz: 'America/Chicago' },\n 'Los Angeles': { lat: 34.0522, lon: -118.2437, tz: 'America/Los_Angeles' },\n};\n\nexport const WEATHER_CACHE_KEY = 'weather_local_cache';\nexport const WEATHER_CACHE_TTL = 30 * 60 * 1000;\n\nexport interface CityWeather {\n city: string;\n temp: number;\n code: number;\n high: number;\n low: number;\n isDay: boolean;\n timezone: string;\n}\n\nexport interface WeatherCacheEntry { data: CityWeather; ts: number }\ntype WeatherCache = Record<string, WeatherCacheEntry>;\n\nfunction readCache(): WeatherCache {\n try { return JSON.parse(localStorage.getItem(WEATHER_CACHE_KEY) || '{}'); } catch { return {}; }\n}\nfunction writeCache(c: WeatherCache) {\n try { localStorage.setItem(WEATHER_CACHE_KEY, JSON.stringify(c)); } catch {}\n}\n\n/** Fetch a city's current weather + today's high/low, with a 30-min\n * localStorage cache shared across widgets. */\nexport async function fetchCityWeather(cityName: string, force = false): Promise<CityWeather | null> {\n const coords = AVAILABLE_CITIES[cityName];\n if (!coords) return null;\n if (!force) {\n const cached = readCache()[cityName];\n if (cached && Date.now() - cached.ts < WEATHER_CACHE_TTL) return cached.data;\n }\n try {\n const res = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${coords.lat}&longitude=${coords.lon}¤t=temperature_2m,weather_code,is_day&daily=temperature_2m_max,temperature_2m_min&forecast_days=1&timezone=auto`);\n const w = await res.json();\n const cw: CityWeather = {\n city: cityName,\n temp: Math.round(w.current.temperature_2m),\n code: w.current.weather_code,\n high: Math.round(w.daily.temperature_2m_max[0]),\n low: Math.round(w.daily.temperature_2m_min[0]),\n isDay: w.current.is_day === 1,\n timezone: w.timezone || 'UTC',\n };\n const c = readCache();\n c[cityName] = { data: cw, ts: Date.now() };\n writeCache(c);\n return cw;\n } catch {\n return null;\n }\n}\n\nexport const toFahrenheit = (c: number) => Math.round(c * 9 / 5 + 32);\n"]}
|