Skip to content

Commit bf8c6d2

Browse files
committed
feat: 改用 indexdb 解决储存限制问题 #1
1 parent be9af07 commit bf8c6d2

File tree

9 files changed

+279
-60
lines changed

9 files changed

+279
-60
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "cc-editor",
33
"private": true,
4-
"version": "1.0.8",
4+
"version": "1.1.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",
@@ -25,6 +25,7 @@
2525
"crc-32": "^1.2.2",
2626
"filesize": "^10.1.6",
2727
"handlebars": "^4.7.8",
28+
"idb": "^8.0.3",
2829
"marked": "^15.0.12",
2930
"monaco-editor": "^0.52.2",
3031
"nanoid": "^5.1.5",

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/common/deepClone.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const deepClone: typeof globalThis.structuredClone =
2+
globalThis.structuredClone
3+
? globalThis.structuredClone
4+
: (obj: any) => JSON.parse(JSON.stringify(obj));

src/common/uuid.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const uuid = () =>
2+
"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
3+
var r = (Math.random() * 16) | 0,
4+
v = c == "x" ? r : (r & 0x3) | 0x8;
5+
return v.toString(16);
6+
});

src/components/App.tsx

Lines changed: 61 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import {
4343
Settings24Regular,
4444
} from "@fluentui/react-icons";
4545
import { useStyles } from "./useStyles";
46-
import { GITHUB_REPO_LINK, HISTORY_KEY, MAX_HISTORY_ITEMS } from "./constants";
46+
import { GITHUB_REPO_LINK } from "./constants";
4747
import { GithubIcon } from "./icons";
4848
import { CharacterBookTab } from "./tabs/CharacterBookTab";
4949
import { useGlobalDrop } from "./useGlobalDrop";
@@ -59,6 +59,9 @@ import { ToolTab } from "./tabs/ToolTab";
5959
import { useGlobalPaste } from "./useGlobalPaste";
6060
import { StartPanel } from "./StartPanel/StartPanel";
6161
import pkgJson from "../../package.json";
62+
import { cardDB } from "../db/db";
63+
import { deepClone } from "../common/deepclone";
64+
import type { CardRecord } from "../db/types";
6265

6366
function getDefaultFormData() {
6467
return {
@@ -96,7 +99,7 @@ export function App() {
9699
const [avatarPreview, setAvatarPreview] = useState<null | string>(null);
97100
const [originalFile, setOriginalFile] = useState<null | ArrayBuffer>(null);
98101
const [originalFileName, setOriginalFileName] = useState("");
99-
const [history, setHistory] = useState<any[]>([]);
102+
const [history, setHistory] = useState<CardRecord[]>([]);
100103
const [isHistoryDrawerOpen, setIsHistoryDrawerOpen] = useState(false);
101104
const [selectedTab, setSelectedTab] = useState("basic");
102105
const [isDirty, setIsDirty] = useState(false);
@@ -109,8 +112,10 @@ export function App() {
109112
});
110113

111114
useEffect(() => {
112-
const storedHistory = localStorage.getItem(HISTORY_KEY);
113-
if (storedHistory) setHistory(JSON.parse(storedHistory));
115+
(async () => {
116+
const cards = await cardDB.getAll();
117+
setHistory(cards);
118+
})();
114119
}, []);
115120

116121
useEffect(() => {
@@ -226,45 +231,37 @@ export function App() {
226231
);
227232
};
228233

229-
const addToHistory = (
234+
const addToHistory = async (
230235
cardData: SpecV3.CharacterCardV3["data"],
231236
avatarUrl: string | null,
232237
spec: string,
233238
spec_version: string
234239
) => {
235240
if (!cardData || !cardData.name) return;
241+
const newCard = CharacterCard.from_json(
242+
{
243+
data: cardData,
244+
spec,
245+
spec_version,
246+
},
247+
avatarUrl ?? undefined
248+
);
249+
const record = cardDB.create({ card: newCard.toSpecV3(), avatarUrl });
250+
try {
251+
await cardDB.addRecord(record);
252+
} catch (error) {
253+
console.error("Error adding record to history:", error);
254+
return;
255+
}
236256
setHistory((prevHistory) => {
237257
const newHistory = prevHistory.filter(
238258
(item) =>
239259
!(
240-
item.data.name === cardData.name &&
241-
item.data.first_mes === cardData.first_mes
260+
item.card.data.name === cardData.name &&
261+
item.card.data.first_mes === cardData.first_mes
242262
)
243263
);
244-
const newEntry = {
245-
data: JSON.parse(JSON.stringify(cardData)), // Deep copy
246-
avatar: avatarUrl,
247-
timestamp: new Date().toISOString(),
248-
spec: spec || "chara_card_v3", // Store spec info
249-
spec_version: spec_version || "3.0",
250-
};
251-
const updatedHistory = [newEntry, ...newHistory].slice(
252-
0,
253-
MAX_HISTORY_ITEMS
254-
);
255-
try {
256-
localStorage.setItem(HISTORY_KEY, JSON.stringify(updatedHistory));
257-
} catch (error) {
258-
if (error instanceof Error && error.name === "QuotaExceededError") {
259-
alert(
260-
"Local storage quota exceeded. Some history items may be lost."
261-
);
262-
return prevHistory;
263-
} else {
264-
throw error;
265-
}
266-
}
267-
return updatedHistory;
264+
return [record, ...newHistory];
268265
});
269266
};
270267

@@ -276,14 +273,17 @@ export function App() {
276273
const confirmLoadFromHistory = (index: number) => {
277274
const item = history[index];
278275
if (item) {
279-
const mockCard = new CharacterCard(item); // For spec/version info
276+
const mockCard = new CharacterCard(
277+
item.card,
278+
item.avatarUrl ?? undefined
279+
); // For spec/version info
280280
setCharacterCard(mockCard);
281281

282-
setFormDataFromCardData(item.data);
283-
setAvatarPreview(item.avatar || null);
282+
setFormDataFromCardData(item.card.data);
283+
setAvatarPreview(mockCard.avatar || null);
284284
setOriginalFile(null);
285285
setOriginalFileName(
286-
`Loaded from history: ${item.data.name || "Unnamed"}`
286+
`Loaded from history: ${item.card.data.name || "Unnamed"}`
287287
);
288288
setIsDirty(false);
289289
setSelectedTab("basic"); // Reset to basic tab on load
@@ -292,10 +292,10 @@ export function App() {
292292
setShowHistoryLoadConfirm({ open: false, index: -1 });
293293
};
294294

295-
const handleClearHistory = () => {
295+
const handleClearHistory = async () => {
296296
if (confirm("Are you sure you want to clear all history?")) {
297297
setHistory([]);
298-
localStorage.removeItem(HISTORY_KEY);
298+
await cardDB.clear();
299299
}
300300
};
301301

@@ -407,26 +407,30 @@ export function App() {
407407
<DrawerBody className={styles.historyDrawerBody}>
408408
{history.length > 0 ? (
409409
<>
410-
{history.map((item, index) => (
411-
<div
412-
key={index}
413-
className={styles.historyItem}
414-
onClick={() => handleLoadFromHistory(index)}
415-
tabIndex={0}
416-
onKeyDown={(e) => {
417-
if (e.key === "Enter" || e.key === " ")
418-
handleLoadFromHistory(index);
419-
}}
420-
>
421-
<Text className={styles.historyItemName}>
422-
{item.data.name || t("Unnamed Card")}
423-
</Text>
424-
<Text className={styles.historyItemDate}>
425-
{new Date(item.timestamp).toLocaleDateString()}
426-
</Text>
427-
</div>
428-
))}
429-
<Divider style={{ margin: `${tokens.spacingVerticalM} 0` }} />
410+
<div style={{ flex: 1 }}>
411+
{history.map((item, index) => (
412+
<div
413+
key={index}
414+
className={styles.historyItem}
415+
onClick={() => handleLoadFromHistory(index)}
416+
tabIndex={0}
417+
onKeyDown={(e) => {
418+
if (e.key === "Enter" || e.key === " ")
419+
handleLoadFromHistory(index);
420+
}}
421+
>
422+
<Text className={styles.historyItemName}>
423+
{item.card.data.name || t("Unnamed Card")}
424+
</Text>
425+
<Text className={styles.historyItemDate}>
426+
{new Date(item.updatedAt).toLocaleDateString()}
427+
</Text>
428+
</div>
429+
))}
430+
</div>
431+
<Divider
432+
style={{ margin: `${tokens.spacingVerticalM} 0`, flex: 0 }}
433+
/>
430434
<Button
431435
appearance="outline"
432436
onClick={handleClearHistory}

src/components/constants.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
export const HISTORY_KEY = "CCEditorHistory";
2-
export const MAX_HISTORY_ITEMS = 15;
31
export const GITHUB_REPO_LINK = "https://github.com/lenML/CCEditor";

src/components/useStyles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export const useStyles = makeStyles({
134134
padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`,
135135
...shorthands.borderRadius(tokens.borderRadiusMedium),
136136
backgroundColor: tokens.colorNeutralBackground2,
137+
userSelect: "none",
137138
cursor: "pointer",
138139
":hover": {
139140
backgroundColor: tokens.colorNeutralBackground2Hover,

0 commit comments

Comments
 (0)