react-os-shell 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/dist/Calculator-BNBRNV4P.js +184 -0
- package/dist/Calculator-BNBRNV4P.js.map +1 -0
- package/dist/Calendar-5EYUVGUU.js +423 -0
- package/dist/Calendar-5EYUVGUU.js.map +1 -0
- package/dist/Checkers-MIAHIKJH.js +214 -0
- package/dist/Checkers-MIAHIKJH.js.map +1 -0
- package/dist/Chess-C5BY45NA.js +190 -0
- package/dist/Chess-C5BY45NA.js.map +1 -0
- package/dist/ConfirmDialog-ZP4AHVUD.js +3 -0
- package/dist/ConfirmDialog-ZP4AHVUD.js.map +1 -0
- package/dist/CurrencyConverter-TYPU2IRF.js +223 -0
- package/dist/CurrencyConverter-TYPU2IRF.js.map +1 -0
- package/dist/Email-JEYYJ3YV.js +1835 -0
- package/dist/Email-JEYYJ3YV.js.map +1 -0
- package/dist/Game2048-3RH3ELRD.js +191 -0
- package/dist/Game2048-3RH3ELRD.js.map +1 -0
- package/dist/GeminiChat-BXLBJFT4.js +184 -0
- package/dist/GeminiChat-BXLBJFT4.js.map +1 -0
- package/dist/Minesweeper-VQGLAZON.js +270 -0
- package/dist/Minesweeper-VQGLAZON.js.map +1 -0
- package/dist/Notepad-YTZRCAXX.js +389 -0
- package/dist/Notepad-YTZRCAXX.js.map +1 -0
- package/dist/PomodoroTimer-HARIJN4S.js +196 -0
- package/dist/PomodoroTimer-HARIJN4S.js.map +1 -0
- package/dist/Spreadsheet-IOKEDNS6.js +446 -0
- package/dist/Spreadsheet-IOKEDNS6.js.map +1 -0
- package/dist/Sudoku-XHLYCEVT.js +197 -0
- package/dist/Sudoku-XHLYCEVT.js.map +1 -0
- package/dist/Tetris-ZHCZYL24.js +243 -0
- package/dist/Tetris-ZHCZYL24.js.map +1 -0
- package/dist/Weather-ROZ7TRNW.js +310 -0
- package/dist/Weather-ROZ7TRNW.js.map +1 -0
- package/dist/apps/index.d.ts +55 -0
- package/dist/apps/index.js +48 -0
- package/dist/apps/index.js.map +1 -0
- package/dist/chunk-5O2KEISQ.js +155 -0
- package/dist/chunk-5O2KEISQ.js.map +1 -0
- package/dist/chunk-D7PYW2QS.js +265 -0
- package/dist/chunk-D7PYW2QS.js.map +1 -0
- package/dist/chunk-GP4Y3VCB.js +806 -0
- package/dist/chunk-GP4Y3VCB.js.map +1 -0
- package/dist/chunk-NSU7OHPC.js +39 -0
- package/dist/chunk-NSU7OHPC.js.map +1 -0
- package/dist/chunk-PDFQNHW7.js +24 -0
- package/dist/chunk-PDFQNHW7.js.map +1 -0
- package/dist/chunk-RFTLYCSF.js +144 -0
- package/dist/chunk-RFTLYCSF.js.map +1 -0
- package/dist/chunk-SVBID2P6.js +142 -0
- package/dist/chunk-SVBID2P6.js.map +1 -0
- package/dist/chunk-TFGOLXGD.js +41 -0
- package/dist/chunk-TFGOLXGD.js.map +1 -0
- package/dist/chunk-WIJ45SYD.js +120 -0
- package/dist/chunk-WIJ45SYD.js.map +1 -0
- package/dist/chunk-WQIS72NL.js +1470 -0
- package/dist/chunk-WQIS72NL.js.map +1 -0
- package/dist/index.d.ts +642 -0
- package/dist/index.js +3443 -0
- package/dist/index.js.map +1 -0
- package/dist/sounds-NT4DEZGD.js +3 -0
- package/dist/sounds-NT4DEZGD.js.map +1 -0
- package/dist/styles.css +174 -0
- package/dist/types-CFIZ1_xt.d.ts +67 -0
- package/package.json +76 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { loadAppearance, WidgetSettingsModal } from './chunk-SVBID2P6.js';
|
|
2
|
+
import { useWidgetSettings } from './chunk-WQIS72NL.js';
|
|
3
|
+
import './chunk-RFTLYCSF.js';
|
|
4
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
5
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var ALL_CURRENCIES = ["USD", "CNY", "AUD", "GBP", "EUR", "JPY", "CAD", "THB", "NZD", "SGD", "HKD", "CHF", "KRW", "INR", "MXN", "BRL"];
|
|
8
|
+
var DEFAULT_PAIRS = [
|
|
9
|
+
["USD", "CNY"],
|
|
10
|
+
["USD", "AUD"],
|
|
11
|
+
["GBP", "USD"],
|
|
12
|
+
["USD", "JPY"],
|
|
13
|
+
["USD", "CAD"]
|
|
14
|
+
];
|
|
15
|
+
var PAIRS_KEY = "currency_pairs";
|
|
16
|
+
var SETTINGS_KEY = "currency_appearance";
|
|
17
|
+
var CACHE_KEY = "currency_rates_cache";
|
|
18
|
+
var CACHE_TTL = 36e5;
|
|
19
|
+
function getCached(base) {
|
|
20
|
+
try {
|
|
21
|
+
const c = JSON.parse(localStorage.getItem(CACHE_KEY) || "{}");
|
|
22
|
+
const e = c[base];
|
|
23
|
+
if (e && Date.now() - e.timestamp < CACHE_TTL) return e.rates;
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function setCache(base, rates) {
|
|
29
|
+
try {
|
|
30
|
+
const c = JSON.parse(localStorage.getItem(CACHE_KEY) || "{}");
|
|
31
|
+
c[base] = { rates, timestamp: Date.now() };
|
|
32
|
+
localStorage.setItem(CACHE_KEY, JSON.stringify(c));
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function loadPairs() {
|
|
37
|
+
try {
|
|
38
|
+
const s = JSON.parse(localStorage.getItem(PAIRS_KEY) || "");
|
|
39
|
+
if (Array.isArray(s) && s.length) return s;
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
return DEFAULT_PAIRS;
|
|
43
|
+
}
|
|
44
|
+
var FLAG = {
|
|
45
|
+
USD: "\u{1F1FA}\u{1F1F8}",
|
|
46
|
+
CNY: "\u{1F1E8}\u{1F1F3}",
|
|
47
|
+
AUD: "\u{1F1E6}\u{1F1FA}",
|
|
48
|
+
GBP: "\u{1F1EC}\u{1F1E7}",
|
|
49
|
+
JPY: "\u{1F1EF}\u{1F1F5}",
|
|
50
|
+
CAD: "\u{1F1E8}\u{1F1E6}",
|
|
51
|
+
EUR: "\u{1F1EA}\u{1F1FA}",
|
|
52
|
+
THB: "\u{1F1F9}\u{1F1ED}",
|
|
53
|
+
NZD: "\u{1F1F3}\u{1F1FF}",
|
|
54
|
+
SGD: "\u{1F1F8}\u{1F1EC}",
|
|
55
|
+
HKD: "\u{1F1ED}\u{1F1F0}",
|
|
56
|
+
CHF: "\u{1F1E8}\u{1F1ED}",
|
|
57
|
+
KRW: "\u{1F1F0}\u{1F1F7}",
|
|
58
|
+
INR: "\u{1F1EE}\u{1F1F3}",
|
|
59
|
+
MXN: "\u{1F1F2}\u{1F1FD}",
|
|
60
|
+
BRL: "\u{1F1E7}\u{1F1F7}"
|
|
61
|
+
};
|
|
62
|
+
function CurrencyConverter() {
|
|
63
|
+
const [pairs, setPairs] = useState(loadPairs);
|
|
64
|
+
const [appearance, setAppearance] = useState(() => loadAppearance(SETTINGS_KEY));
|
|
65
|
+
const [allRates, setAllRates] = useState({});
|
|
66
|
+
const [updated, setUpdated] = useState(null);
|
|
67
|
+
const [loading, setLoading] = useState(false);
|
|
68
|
+
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
69
|
+
const [configPairs, setConfigPairs] = useState([]);
|
|
70
|
+
const [configAppearance, setConfigAppearance] = useState(appearance);
|
|
71
|
+
const [newFrom, setNewFrom] = useState("USD");
|
|
72
|
+
const [newTo, setNewTo] = useState("CNY");
|
|
73
|
+
useWidgetSettings(useCallback(() => {
|
|
74
|
+
setConfigPairs([...pairs]);
|
|
75
|
+
setConfigAppearance({ ...appearance });
|
|
76
|
+
setSettingsOpen(true);
|
|
77
|
+
}, [pairs, appearance]));
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const bases = [...new Set(pairs.map(([from]) => from))];
|
|
80
|
+
let mounted = true;
|
|
81
|
+
async function fetchAll() {
|
|
82
|
+
setLoading(true);
|
|
83
|
+
const result = {};
|
|
84
|
+
for (const base of bases) {
|
|
85
|
+
const cached = getCached(base);
|
|
86
|
+
if (cached) {
|
|
87
|
+
result[base] = cached;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const res = await fetch(`https://open.er-api.com/v6/latest/${base}`);
|
|
92
|
+
const data = await res.json();
|
|
93
|
+
if (data.rates) {
|
|
94
|
+
result[base] = data.rates;
|
|
95
|
+
setCache(base, data.rates);
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (mounted) {
|
|
101
|
+
setAllRates(result);
|
|
102
|
+
setUpdated(/* @__PURE__ */ new Date());
|
|
103
|
+
setLoading(false);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
fetchAll();
|
|
107
|
+
return () => {
|
|
108
|
+
mounted = false;
|
|
109
|
+
};
|
|
110
|
+
}, [pairs]);
|
|
111
|
+
const saveSettings = () => {
|
|
112
|
+
if (configPairs.length === 0) return;
|
|
113
|
+
setPairs(configPairs);
|
|
114
|
+
setAppearance(configAppearance);
|
|
115
|
+
localStorage.setItem(PAIRS_KEY, JSON.stringify(configPairs));
|
|
116
|
+
localStorage.setItem(SETTINGS_KEY, JSON.stringify(configAppearance));
|
|
117
|
+
setSettingsOpen(false);
|
|
118
|
+
};
|
|
119
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
120
|
+
/* @__PURE__ */ jsxs(
|
|
121
|
+
"div",
|
|
122
|
+
{
|
|
123
|
+
className: "flex flex-col h-full rounded-lg",
|
|
124
|
+
style: { backgroundColor: `rgba(255,255,255,${appearance.activeOpacity / 100})`, backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : void 0 },
|
|
125
|
+
children: [
|
|
126
|
+
/* @__PURE__ */ jsxs("div", { className: "px-3 py-2 space-y-0.5 flex-1", children: [
|
|
127
|
+
loading && /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-400 text-center py-4", children: "Loading rates..." }),
|
|
128
|
+
pairs.map(([from, to], idx) => {
|
|
129
|
+
const rate = allRates[from]?.[to];
|
|
130
|
+
return /* @__PURE__ */ jsxs(
|
|
131
|
+
"button",
|
|
132
|
+
{
|
|
133
|
+
onClick: () => {
|
|
134
|
+
const swapped = pairs.map((p, i) => i === idx ? [p[1], p[0]] : p);
|
|
135
|
+
setPairs(swapped);
|
|
136
|
+
localStorage.setItem(PAIRS_KEY, JSON.stringify(swapped));
|
|
137
|
+
},
|
|
138
|
+
className: "flex items-center justify-between py-1.5 border-b border-gray-200/50 last:border-0 w-full hover:bg-gray-100/50 rounded transition-colors cursor-pointer",
|
|
139
|
+
children: [
|
|
140
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
141
|
+
/* @__PURE__ */ jsx("span", { className: "text-base", children: FLAG[from] || "" }),
|
|
142
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-500", children: from }),
|
|
143
|
+
/* @__PURE__ */ jsx("svg", { className: "h-3 w-3 text-gray-300", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" }) }),
|
|
144
|
+
/* @__PURE__ */ jsx("span", { className: "text-base", children: FLAG[to] || "" }),
|
|
145
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-500", children: to })
|
|
146
|
+
] }),
|
|
147
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-mono font-semibold text-gray-800", children: rate != null ? rate.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 4 }) : "\u2014" })
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
`${from}-${to}-${idx}`
|
|
151
|
+
);
|
|
152
|
+
})
|
|
153
|
+
] }),
|
|
154
|
+
updated && /* @__PURE__ */ jsxs("div", { className: "text-[9px] text-gray-400 text-center py-1", children: [
|
|
155
|
+
"Updated ",
|
|
156
|
+
updated.toLocaleTimeString()
|
|
157
|
+
] })
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
/* @__PURE__ */ jsx(
|
|
162
|
+
WidgetSettingsModal,
|
|
163
|
+
{
|
|
164
|
+
open: settingsOpen,
|
|
165
|
+
onClose: () => setSettingsOpen(false),
|
|
166
|
+
title: "Currency Settings",
|
|
167
|
+
appearance: configAppearance,
|
|
168
|
+
onAppearanceChange: setConfigAppearance,
|
|
169
|
+
onSave: saveSettings,
|
|
170
|
+
children: /* @__PURE__ */ jsxs("div", { children: [
|
|
171
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-2", children: "Currency Pairs" }),
|
|
172
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1 mb-2", children: configPairs.map(([f, t], i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between py-1 px-2 bg-gray-50 rounded", children: [
|
|
173
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm", children: [
|
|
174
|
+
FLAG[f] || "",
|
|
175
|
+
" ",
|
|
176
|
+
f,
|
|
177
|
+
" \u2192 ",
|
|
178
|
+
FLAG[t] || "",
|
|
179
|
+
" ",
|
|
180
|
+
t
|
|
181
|
+
] }),
|
|
182
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setConfigPairs((p) => p.filter((_, idx) => idx !== i)), className: "text-red-400 hover:text-red-600", children: "\xD7" })
|
|
183
|
+
] }, i)) }),
|
|
184
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
185
|
+
/* @__PURE__ */ jsx(
|
|
186
|
+
"select",
|
|
187
|
+
{
|
|
188
|
+
value: newFrom,
|
|
189
|
+
onChange: (e) => setNewFrom(e.target.value),
|
|
190
|
+
className: "bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500",
|
|
191
|
+
children: ALL_CURRENCIES.map((c) => /* @__PURE__ */ jsx("option", { children: c }, c))
|
|
192
|
+
}
|
|
193
|
+
),
|
|
194
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-400", children: "\u2192" }),
|
|
195
|
+
/* @__PURE__ */ jsx(
|
|
196
|
+
"select",
|
|
197
|
+
{
|
|
198
|
+
value: newTo,
|
|
199
|
+
onChange: (e) => setNewTo(e.target.value),
|
|
200
|
+
className: "bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500",
|
|
201
|
+
children: ALL_CURRENCIES.map((c) => /* @__PURE__ */ jsx("option", { children: c }, c))
|
|
202
|
+
}
|
|
203
|
+
),
|
|
204
|
+
/* @__PURE__ */ jsx(
|
|
205
|
+
"button",
|
|
206
|
+
{
|
|
207
|
+
onClick: () => {
|
|
208
|
+
if (newFrom !== newTo) setConfigPairs((p) => [...p, [newFrom, newTo]]);
|
|
209
|
+
},
|
|
210
|
+
className: "text-sm font-medium text-blue-600 hover:text-blue-800 px-2",
|
|
211
|
+
children: "+ Add"
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
] })
|
|
215
|
+
] })
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
] });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export { CurrencyConverter as default };
|
|
222
|
+
//# sourceMappingURL=CurrencyConverter-TYPU2IRF.js.map
|
|
223
|
+
//# sourceMappingURL=CurrencyConverter-TYPU2IRF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/apps/CurrencyConverter.tsx"],"names":[],"mappings":"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","file":"CurrencyConverter-TYPU2IRF.js","sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport { useWidgetSettings } from '../shell/Modal';\nimport WidgetSettingsModal, { loadAppearance, type WidgetAppearance } from '../shell/WidgetSettingsModal';\n\nconst ALL_CURRENCIES = ['USD', 'CNY', 'AUD', 'GBP', 'EUR', 'JPY', 'CAD', 'THB', 'NZD', 'SGD', 'HKD', 'CHF', 'KRW', 'INR', 'MXN', 'BRL'];\n\nconst DEFAULT_PAIRS: [string, string][] = [\n ['USD', 'CNY'], ['USD', 'AUD'], ['GBP', 'USD'], ['USD', 'JPY'], ['USD', 'CAD'],\n];\n\nconst PAIRS_KEY = 'currency_pairs';\nconst SETTINGS_KEY = 'currency_appearance';\nconst CACHE_KEY = 'currency_rates_cache';\nconst CACHE_TTL = 3600000;\n\ntype CacheEntry = { rates: Record<string, number>; timestamp: number };\n\nfunction getCached(base: string): Record<string, number> | null {\n try { const c: Record<string, CacheEntry> = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}'); const e = c[base]; if (e && Date.now() - e.timestamp < CACHE_TTL) return e.rates; } catch {} return null;\n}\nfunction setCache(base: string, rates: Record<string, number>) {\n try { const c = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}'); c[base] = { rates, timestamp: Date.now() }; localStorage.setItem(CACHE_KEY, JSON.stringify(c)); } catch {}\n}\nfunction loadPairs(): [string, string][] {\n try { const s = JSON.parse(localStorage.getItem(PAIRS_KEY) || ''); if (Array.isArray(s) && s.length) return s; } catch {} return DEFAULT_PAIRS;\n}\n\nconst FLAG: Record<string, string> = {\n USD: '\\u{1F1FA}\\u{1F1F8}', CNY: '\\u{1F1E8}\\u{1F1F3}', AUD: '\\u{1F1E6}\\u{1F1FA}',\n GBP: '\\u{1F1EC}\\u{1F1E7}', JPY: '\\u{1F1EF}\\u{1F1F5}', CAD: '\\u{1F1E8}\\u{1F1E6}',\n EUR: '\\u{1F1EA}\\u{1F1FA}', THB: '\\u{1F1F9}\\u{1F1ED}', NZD: '\\u{1F1F3}\\u{1F1FF}',\n SGD: '\\u{1F1F8}\\u{1F1EC}', HKD: '\\u{1F1ED}\\u{1F1F0}', CHF: '\\u{1F1E8}\\u{1F1ED}',\n KRW: '\\u{1F1F0}\\u{1F1F7}', INR: '\\u{1F1EE}\\u{1F1F3}', MXN: '\\u{1F1F2}\\u{1F1FD}',\n BRL: '\\u{1F1E7}\\u{1F1F7}',\n};\n\nexport default function CurrencyConverter() {\n const [pairs, setPairs] = useState(loadPairs);\n const [appearance, setAppearance] = useState(() => loadAppearance(SETTINGS_KEY));\n const [allRates, setAllRates] = useState<Record<string, Record<string, number>>>({});\n const [updated, setUpdated] = useState<Date | null>(null);\n const [loading, setLoading] = useState(false);\n const [settingsOpen, setSettingsOpen] = useState(false);\n const [configPairs, setConfigPairs] = useState<[string, string][]>([]);\n const [configAppearance, setConfigAppearance] = useState<WidgetAppearance>(appearance);\n const [newFrom, setNewFrom] = useState('USD');\n const [newTo, setNewTo] = useState('CNY');\n\n useWidgetSettings(useCallback(() => {\n setConfigPairs([...pairs]);\n setConfigAppearance({ ...appearance });\n setSettingsOpen(true);\n }, [pairs, appearance]));\n\n useEffect(() => {\n const bases = [...new Set(pairs.map(([from]) => from))];\n let mounted = true;\n async function fetchAll() {\n setLoading(true);\n const result: Record<string, Record<string, number>> = {};\n for (const base of bases) {\n const cached = getCached(base);\n if (cached) { result[base] = cached; continue; }\n try { const res = await fetch(`https://open.er-api.com/v6/latest/${base}`); const data = await res.json(); if (data.rates) { result[base] = data.rates; setCache(base, data.rates); } } catch {}\n }\n if (mounted) { setAllRates(result); setUpdated(new Date()); setLoading(false); }\n }\n fetchAll();\n return () => { mounted = false; };\n }, [pairs]);\n\n const saveSettings = () => {\n if (configPairs.length === 0) return;\n setPairs(configPairs); setAppearance(configAppearance);\n localStorage.setItem(PAIRS_KEY, JSON.stringify(configPairs));\n localStorage.setItem(SETTINGS_KEY, JSON.stringify(configAppearance));\n setSettingsOpen(false);\n };\n\n return (\n <>\n <div className=\"flex flex-col h-full rounded-lg\"\n style={{ backgroundColor: `rgba(255,255,255,${appearance.activeOpacity / 100})`, backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : undefined }}>\n <div className=\"px-3 py-2 space-y-0.5 flex-1\">\n {loading && <div className=\"text-xs text-gray-400 text-center py-4\">Loading rates...</div>}\n {pairs.map(([from, to], idx) => {\n const rate = allRates[from]?.[to];\n return (\n <button key={`${from}-${to}-${idx}`} onClick={() => {\n const swapped: [string, string][] = pairs.map((p, i) => i === idx ? [p[1], p[0]] : p);\n setPairs(swapped);\n localStorage.setItem(PAIRS_KEY, JSON.stringify(swapped));\n }}\n className=\"flex items-center justify-between py-1.5 border-b border-gray-200/50 last:border-0 w-full hover:bg-gray-100/50 rounded transition-colors cursor-pointer\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-base\">{FLAG[from] || ''}</span>\n <span className=\"text-xs font-medium text-gray-500\">{from}</span>\n <svg className=\"h-3 w-3 text-gray-300\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4\" /></svg>\n <span className=\"text-base\">{FLAG[to] || ''}</span>\n <span className=\"text-xs font-medium text-gray-500\">{to}</span>\n </div>\n <div className=\"text-sm font-mono font-semibold text-gray-800\">\n {rate != null ? rate.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 4 }) : '—'}\n </div>\n </button>\n );\n })}\n </div>\n {updated && <div className=\"text-[9px] text-gray-400 text-center py-1\">Updated {updated.toLocaleTimeString()}</div>}\n </div>\n\n <WidgetSettingsModal open={settingsOpen} onClose={() => setSettingsOpen(false)} title=\"Currency Settings\"\n appearance={configAppearance} onAppearanceChange={setConfigAppearance} onSave={saveSettings}>\n <div>\n <h3 className=\"text-sm font-semibold text-gray-700 mb-2\">Currency Pairs</h3>\n <div className=\"space-y-1 mb-2\">\n {configPairs.map(([f, t], i) => (\n <div key={i} className=\"flex items-center justify-between py-1 px-2 bg-gray-50 rounded\">\n <span className=\"text-sm\">{FLAG[f] || ''} {f} → {FLAG[t] || ''} {t}</span>\n <button onClick={() => setConfigPairs(p => p.filter((_, idx) => idx !== i))} className=\"text-red-400 hover:text-red-600\">×</button>\n </div>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <select value={newFrom} onChange={e => setNewFrom(e.target.value)}\n className=\"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500\">\n {ALL_CURRENCIES.map(c => <option key={c}>{c}</option>)}\n </select>\n <span className=\"text-gray-400\">→</span>\n <select value={newTo} onChange={e => setNewTo(e.target.value)}\n className=\"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500\">\n {ALL_CURRENCIES.map(c => <option key={c}>{c}</option>)}\n </select>\n <button onClick={() => { if (newFrom !== newTo) setConfigPairs(p => [...p, [newFrom, newTo]]); }}\n className=\"text-sm font-medium text-blue-600 hover:text-blue-800 px-2\">+ Add</button>\n </div>\n </div>\n </WidgetSettingsModal>\n </>\n );\n}\n"]}
|