Skip to content

Commit 9d95a2a

Browse files
authored
Merge branch 'main' into main
2 parents 14d6645 + 3559338 commit 9d95a2a

File tree

9 files changed

+422
-387
lines changed

9 files changed

+422
-387
lines changed

src/simulator/src/hotkey_binder/model/addShortcut.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { saveOffline, openOffline } from '../../data/project'
1515
import createSaveAsImgPrompt from '../../data/saveImage'
1616
import { createSubCircuitPrompt } from '../../subcircuit'
1717
import { createCombinationalAnalysisPrompt } from '../../combinationalAnalysis'
18-
import { shortcut } from './shortcuts.plugin.js'
18+
import { shortcut } from './shortcuts.plugin'
1919
import logixFunction from '../../data'
2020

2121
export const addShortcut = (keys, action) => {

src/simulator/src/themer/customThemer.js

-154
This file was deleted.
+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
<template>
2+
<div>
3+
<form ref="propertiesContainer">
4+
<div v-for="(property, key) in customTheme" :key="key" class="property">
5+
<label :for="String(key)">{{ key }} ({{ property.description }})</label>
6+
<input type="color" :name="String(key)" v-model="customTheme[key]?.color" class="customColorInput"
7+
@input="updateTheme" />
8+
</div>
9+
<a id="downloadThemeFile" style="display: none"></a>
10+
</form>
11+
12+
<button @click="applyTheme">Apply Theme</button>
13+
<button @click="importTheme">Import Theme</button>
14+
<button @click="exportTheme">Export Theme</button>
15+
16+
<input type="file" id="importThemeFile" style="display: none" @change="handleFileImport" />
17+
</div>
18+
</template>
19+
20+
<script lang="ts">
21+
import { defineComponent, reactive, ref, onMounted } from "vue";
22+
import { dots } from "../canvasApi";
23+
import themeOptions from "./themes";
24+
import { updateThemeForStyle } from "./themer";
25+
import { CreateAbstraction } from "./customThemeAbstraction";
26+
27+
export default defineComponent({
28+
name: "CustomColorThemes",
29+
setup() {
30+
// Reactive state for custom theme
31+
const customTheme = reactive<{ [key: string]: { color: string; description: string; ref: string[] } }>(CreateAbstraction(themeOptions["Custom Theme"]) as { [key: string]: { color: string; description: string; ref: string[] } });
32+
33+
// Reference to the form container
34+
const propertiesContainer = ref<HTMLFormElement | null>(null);
35+
36+
// Function to update the theme and background
37+
const updateBG = () => dots(true, false, true);
38+
39+
// Update theme when color input changes
40+
const updateTheme = (event: Event) => {
41+
const target = event.target as HTMLInputElement;
42+
const key = target.name;
43+
if (!customTheme[key]) {
44+
console.error(`Theme property ${key} not found`);
45+
return;
46+
}
47+
(customTheme[key] as { color: string; description: string; ref: string[] }).color = target.value;
48+
try {
49+
customTheme[key].ref.forEach((property: string) => {
50+
themeOptions["Custom Theme"][property] = target.value;
51+
});
52+
updateThemeForStyle("Custom Theme");
53+
updateBG();
54+
} catch (error) {
55+
console.error('Failed to update theme:', error);
56+
}
57+
};
58+
59+
// Apply the custom theme
60+
const applyTheme = () => {
61+
try {
62+
if (typeof localStorage === 'undefined') {
63+
throw new Error('localStorage is not available');
64+
}
65+
localStorage.setItem("theme", "Custom Theme");
66+
localStorage.setItem("Custom Theme", JSON.stringify(themeOptions["Custom Theme"]));
67+
updateThemeForStyle("Custom Theme");
68+
updateBG();
69+
} catch (error) {
70+
console.error('Failed to save theme:', error);
71+
// Fallback: Apply theme without saving
72+
updateThemeForStyle("Custom Theme");
73+
updateBG();
74+
}
75+
};
76+
77+
// Import the custom theme from a JSON file
78+
const importTheme = () => {
79+
const importThemeFile = document.getElementById("importThemeFile") as HTMLInputElement;
80+
importThemeFile.click();
81+
};
82+
83+
// Export the custom theme as a JSON file
84+
const exportTheme = () => {
85+
const dlAnchorElem = document.getElementById("downloadThemeFile") as HTMLAnchorElement;
86+
dlAnchorElem.setAttribute(
87+
"href",
88+
`data:text/json;charset=utf-8,${encodeURIComponent(
89+
JSON.stringify(themeOptions["Custom Theme"])
90+
)}`
91+
);
92+
dlAnchorElem.setAttribute("download", "CV_CustomTheme.json");
93+
dlAnchorElem.click();
94+
};
95+
96+
// Handle file import for custom theme
97+
const handleFileImport = (event: Event) => {
98+
const target = event.target as HTMLInputElement;
99+
const file = target.files?.[0];
100+
if (!file) {
101+
alert("No file selected!");
102+
return;
103+
}
104+
105+
if (!file.name.toLowerCase().endsWith('.json')) {
106+
alert("Please select a JSON file!");
107+
target.value = "";
108+
return;
109+
}
110+
111+
if (file.size > 1024 * 1024) { // 1MB limit
112+
alert("File is too large!");
113+
target.value = "";
114+
return;
115+
}
116+
117+
const reader = new FileReader();
118+
119+
reader.onload = (e) => {
120+
try {
121+
if (!e.target?.result) {
122+
throw new Error("Failed to read file");
123+
}
124+
const lines = JSON.parse(e.target.result as string);
125+
if (!lines || typeof lines !== 'object') {
126+
throw new Error("Invalid theme format");
127+
}
128+
Object.assign(customTheme, CreateAbstraction(lines));
129+
themeOptions["Custom Theme"] = lines;
130+
updateThemeForStyle("Custom Theme");
131+
updateBG();
132+
} catch (error) {
133+
console.error('Failed to parse theme:', error);
134+
alert("Invalid theme file!");
135+
}
136+
};
137+
138+
reader.onerror = () => {
139+
console.error('Failed to read file');
140+
alert("Failed to read file!");
141+
};
142+
143+
reader.readAsText(file);
144+
target.value = ""; // Reset file input
145+
};
146+
147+
// Simulate a delay to update the theme on component mount
148+
onMounted(() => {
149+
updateThemeForStyle("Custom Theme");
150+
updateBG();
151+
});
152+
153+
return {
154+
customTheme,
155+
propertiesContainer,
156+
updateTheme,
157+
applyTheme,
158+
exportTheme,
159+
importTheme,
160+
handleFileImport,
161+
};
162+
},
163+
});
164+
</script>
165+
166+
<style scoped>
167+
.property {
168+
margin-bottom: 10px;
169+
}
170+
171+
.customColorInput {
172+
margin-left: 10px;
173+
}
174+
</style>

src/simulator/src/themer/themeCardSvg.js renamed to src/simulator/src/themer/themeCardSvg.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export default `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="202.9" height="106" viewBox="0 0 202.9 106">
1+
const svgString: string = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="202.9" height="106" viewBox="0 0 202.9 106">
22
<defs>
33
<clipPath id="clip">
44
<use xlink:href="#fill"/>
@@ -98,5 +98,6 @@ export default `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.
9898
<path class='svgChev' id="chevron-down-8" data-name="chevron-down" d="M6,9l1.881,1.881L9.762,9" transform="translate(-6 -9)" fill="none" stroke="#bbb" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
9999
</g>
100100
</g>
101-
</svg>
102-
`
101+
</svg>`;
102+
103+
export default svgString;

0 commit comments

Comments
 (0)