@papu1337/builder 0.0.5 → 1.0.1
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/dist/builder/BuilderView.svelte +158 -0
- package/dist/builder/BuilderView.svelte.d.ts +8 -0
- package/dist/builder/builder.vanilla.es.js +6514 -0
- package/dist/builder/builder.vanilla.umd.js +89 -0
- package/dist/builder/canvas/canvas.svelte +92 -0
- package/dist/builder/canvas/canvas.svelte.d.ts +17 -0
- package/dist/builder/canvas/styles.css +63 -0
- package/dist/builder/createBuilder.svelte.d.ts +9 -0
- package/dist/builder/createBuilder.svelte.js +17 -0
- package/dist/builder/index.d.ts +8 -0
- package/dist/builder/index.js +7 -0
- package/dist/builder/leftbar/leftBar.svelte +740 -0
- package/dist/builder/leftbar/leftBar.svelte.d.ts +8 -0
- package/dist/builder/leftbar/styles.css +152 -0
- package/dist/builder/pageMeta.svelte.d.ts +13 -0
- package/dist/builder/pageMeta.svelte.js +25 -0
- package/dist/builder/rightbar/rightBar.svelte +100 -0
- package/dist/builder/rightbar/rightBar.svelte.d.ts +10 -0
- package/dist/builder/rightbar/styles.css +167 -0
- package/dist/builder/topbar/TopBar.svelte +337 -0
- package/dist/builder/topbar/TopBar.svelte.d.ts +12 -0
- package/dist/builder/topbar/styles.css +123 -0
- package/dist/builder/viewport.svelte.d.ts +9 -0
- package/dist/builder/viewport.svelte.js +17 -0
- package/dist/elements/_shared/Arrow.svelte +58 -0
- package/dist/elements/_shared/Arrow.svelte.d.ts +11 -0
- package/dist/elements/_shared/GradientBorder.svelte +55 -0
- package/dist/elements/_shared/GradientBorder.svelte.d.ts +10 -0
- package/dist/elements/banner/bannerElement.svelte +101 -33
- package/dist/elements/banner/settings.d.ts +13 -3
- package/dist/elements/banner/settings.js +88 -8
- package/dist/elements/button/buttonElement.svelte +27 -21
- package/dist/elements/button/settings.d.ts +11 -9
- package/dist/elements/button/settings.js +18 -39
- package/dist/elements/globalSettings.js +5 -4
- package/dist/elements/howItWorks/howItWorksElement.svelte +221 -0
- package/dist/elements/howItWorks/howItWorksElement.svelte.d.ts +7 -0
- package/dist/elements/howItWorks/settings.d.ts +16 -0
- package/dist/elements/howItWorks/settings.js +70 -0
- package/dist/elements/steps/settings.d.ts +17 -0
- package/dist/elements/steps/settings.js +69 -0
- package/dist/elements/steps/stepsElement.svelte +220 -0
- package/dist/elements/steps/stepsElement.svelte.d.ts +7 -0
- package/dist/elements/terms/settings.d.ts +14 -0
- package/dist/elements/terms/settings.js +32 -0
- package/dist/elements/terms/termsElement.svelte +209 -0
- package/dist/elements/terms/termsElement.svelte.d.ts +7 -0
- package/dist/elements/text/settings.d.ts +8 -11
- package/dist/elements/text/settings.js +21 -51
- package/dist/elements/text/textElement.svelte +23 -23
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useTranslation.svelte.d.ts +9 -0
- package/dist/hooks/useTranslation.svelte.js +10 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/renderer/BuilderRenderer.svelte +30 -2
- package/dist/renderer/registry.js +6 -6
- package/dist/renderer/renderer.vanilla.es.js +1547 -1188
- package/dist/renderer/renderer.vanilla.umd.js +64 -39
- package/dist/renderer/resolve.d.ts +1 -1
- package/dist/renderer/resolve.js +28 -14
- package/dist/renderer/types.d.ts +2 -0
- package/dist/service/element.action.svelte.d.ts +21 -0
- package/dist/service/element.action.svelte.js +125 -0
- package/dist/service/element.history.svelte.d.ts +8 -0
- package/dist/service/element.history.svelte.js +36 -0
- package/dist/service/element.io.svelte.d.ts +4 -0
- package/dist/service/element.io.svelte.js +232 -0
- package/dist/service/element.reader.svelte.d.ts +4 -0
- package/dist/service/element.reader.svelte.js +51 -0
- package/dist/service/element.translate.svelte.d.ts +12 -0
- package/dist/service/element.translate.svelte.js +81 -0
- package/dist/service/index.d.ts +5 -0
- package/dist/service/index.js +5 -0
- package/dist/service/types.d.ts +13 -0
- package/dist/service/types.js +1 -0
- package/dist/settings/base.svelte.d.ts +6 -1
- package/dist/settings/base.svelte.js +61 -17
- package/dist/settings/components/ColorSettings.svelte +169 -40
- package/dist/settings/components/ColorSettings.svelte.d.ts +3 -2
- package/dist/settings/components/ListSettings.svelte +4 -5
- package/dist/settings/components/NumberSettings.svelte +117 -20
- package/dist/settings/components/RepeaterSettings.svelte +145 -0
- package/dist/settings/components/RepeaterSettings.svelte.d.ts +14 -0
- package/dist/settings/components/SegmentSettings.svelte +85 -0
- package/dist/settings/components/SegmentSettings.svelte.d.ts +5 -0
- package/dist/settings/components/SelectSettings.svelte +2 -3
- package/dist/settings/components/SettingsGroup.svelte +14 -69
- package/dist/settings/components/SettingsRenderer.svelte +76 -0
- package/dist/settings/components/SettingsRenderer.svelte.d.ts +8 -0
- package/dist/settings/components/TextSettings.svelte +2 -3
- package/dist/settings/components/TranslatableSettings.svelte +3 -4
- package/dist/settings/components/UploadSettings.svelte +2 -3
- package/dist/settings/groups.d.ts +23 -7
- package/dist/settings/groups.js +48 -24
- package/dist/settings/implementation.svelte.js +4 -0
- package/dist/settings/index.d.ts +2 -0
- package/dist/settings/index.js +2 -0
- package/dist/settings/mode.svelte.d.ts +4 -0
- package/dist/settings/mode.svelte.js +4 -0
- package/dist/settings/repeater.svelte.d.ts +26 -0
- package/dist/settings/repeater.svelte.js +70 -0
- package/dist/settings/types.d.ts +28 -2
- package/package.json +8 -2
- package/dist/elements/accordion/accordionElement.svelte +0 -101
- package/dist/elements/accordion/accordionElement.svelte.d.ts +0 -7
- package/dist/elements/accordion/settings.d.ts +0 -17
- package/dist/elements/accordion/settings.js +0 -54
- package/dist/elements/badge/badgeElement.svelte +0 -49
- package/dist/elements/badge/badgeElement.svelte.d.ts +0 -7
- package/dist/elements/badge/settings.d.ts +0 -14
- package/dist/elements/badge/settings.js +0 -47
- package/dist/elements/ctaCard/ctaCardElement.svelte +0 -132
- package/dist/elements/ctaCard/ctaCardElement.svelte.d.ts +0 -7
- package/dist/elements/ctaCard/settings.d.ts +0 -22
- package/dist/elements/ctaCard/settings.js +0 -64
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { Setting, Settings } from "../settings/base.svelte";
|
|
2
|
+
import { RepeaterSetting } from "../settings/repeater.svelte";
|
|
3
|
+
import { saveHistory } from "./element.history.svelte";
|
|
4
|
+
import { elements } from "./element.reader.svelte";
|
|
5
|
+
import { ComponentsInstance, GLOBAL_KEY, cloneEntry, trackSettings, } from "./element.action.svelte";
|
|
6
|
+
import { languages } from "../hooks/useTranslation.svelte";
|
|
7
|
+
function collectValues(settings) {
|
|
8
|
+
const result = {};
|
|
9
|
+
for (const key in settings.entries) {
|
|
10
|
+
const item = settings.entries[key];
|
|
11
|
+
if (item instanceof Settings) {
|
|
12
|
+
result[key] = collectValues(item);
|
|
13
|
+
}
|
|
14
|
+
else if (item instanceof RepeaterSetting) {
|
|
15
|
+
result[key] = item.items.map((child) => collectValues(child));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
result[key] = item.value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
function applyValues(settings, values) {
|
|
24
|
+
for (const key in settings.entries) {
|
|
25
|
+
const item = settings.entries[key];
|
|
26
|
+
if (item instanceof Settings) {
|
|
27
|
+
const nested = values[key];
|
|
28
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
29
|
+
applyValues(item, nested);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (item instanceof RepeaterSetting) {
|
|
33
|
+
const list = values[key];
|
|
34
|
+
if (Array.isArray(list)) {
|
|
35
|
+
item.items = list.map((entryValues, index) => {
|
|
36
|
+
const created = item.factory(index);
|
|
37
|
+
if (entryValues && typeof entryValues === "object") {
|
|
38
|
+
applyValues(created, entryValues);
|
|
39
|
+
}
|
|
40
|
+
return created;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (key in values) {
|
|
45
|
+
const setting = item;
|
|
46
|
+
if (setting.isTranslatable && typeof values[key] === "string") {
|
|
47
|
+
setting.value = { en: values[key] };
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
setting.value = values[key];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function collectMobile(settings) {
|
|
56
|
+
const result = {};
|
|
57
|
+
for (const key in settings.entries) {
|
|
58
|
+
const item = settings.entries[key];
|
|
59
|
+
if (item instanceof Settings) {
|
|
60
|
+
const nested = collectMobile(item);
|
|
61
|
+
if (Object.keys(nested).length > 0)
|
|
62
|
+
result[key] = nested;
|
|
63
|
+
}
|
|
64
|
+
else if (item instanceof RepeaterSetting) {
|
|
65
|
+
const arr = item.items.map((child) => collectMobile(child));
|
|
66
|
+
if (arr.some((entry) => Object.keys(entry).length > 0))
|
|
67
|
+
result[key] = arr;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const setting = item;
|
|
71
|
+
if (setting.mobileValue !== undefined)
|
|
72
|
+
result[key] = setting.mobileValue;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
function applyMobile(settings, values) {
|
|
78
|
+
for (const key in settings.entries) {
|
|
79
|
+
const item = settings.entries[key];
|
|
80
|
+
if (item instanceof Settings) {
|
|
81
|
+
const nested = values[key];
|
|
82
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
83
|
+
applyMobile(item, nested);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (item instanceof RepeaterSetting) {
|
|
87
|
+
const arr = values[key];
|
|
88
|
+
if (Array.isArray(arr)) {
|
|
89
|
+
item.items.forEach((child, index) => {
|
|
90
|
+
const nested = arr[index];
|
|
91
|
+
if (nested && typeof nested === "object") {
|
|
92
|
+
applyMobile(child, nested);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (key in values) {
|
|
98
|
+
item.mobileValue = values[key];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function exportState() {
|
|
103
|
+
const globalEntry = ComponentsInstance.get(GLOBAL_KEY);
|
|
104
|
+
const globalSettings = globalEntry ? collectValues(globalEntry.settings) : {};
|
|
105
|
+
const exported = [];
|
|
106
|
+
for (const [id, entry] of ComponentsInstance) {
|
|
107
|
+
if (id === GLOBAL_KEY)
|
|
108
|
+
continue;
|
|
109
|
+
const mobileSettings = collectMobile(entry.settings);
|
|
110
|
+
exported.push({
|
|
111
|
+
name: `${entry.name}`,
|
|
112
|
+
settings: collectValues(entry.settings),
|
|
113
|
+
...(Object.keys(mobileSettings).length > 0 ? { mobileSettings } : {}),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const state = { global: globalSettings, components: exported };
|
|
117
|
+
return JSON.stringify(state, null, 2);
|
|
118
|
+
}
|
|
119
|
+
export function importState(json) {
|
|
120
|
+
const state = JSON.parse(json);
|
|
121
|
+
saveHistory();
|
|
122
|
+
const globalEntry = ComponentsInstance.get(GLOBAL_KEY);
|
|
123
|
+
if (globalEntry && state.global) {
|
|
124
|
+
applyValues(globalEntry.settings, state.global);
|
|
125
|
+
trackSettings(globalEntry);
|
|
126
|
+
}
|
|
127
|
+
for (const id of [...ComponentsInstance.keys()]) {
|
|
128
|
+
if (id !== GLOBAL_KEY)
|
|
129
|
+
ComponentsInstance.delete(id);
|
|
130
|
+
}
|
|
131
|
+
for (const item of state.components) {
|
|
132
|
+
const template = elements.find((e) => e.name === item.name);
|
|
133
|
+
if (!template)
|
|
134
|
+
continue;
|
|
135
|
+
const instance = cloneEntry(template);
|
|
136
|
+
instance.id = `${template.name}-${crypto.randomUUID()}`;
|
|
137
|
+
applyValues(instance.settings, item.settings);
|
|
138
|
+
if (item.mobileSettings)
|
|
139
|
+
applyMobile(instance.settings, item.mobileSettings);
|
|
140
|
+
ComponentsInstance.set(instance.id, instance);
|
|
141
|
+
trackSettings(instance);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export function exportTranslations() {
|
|
145
|
+
const dictionary = {};
|
|
146
|
+
const collectCompTrans = (settings) => {
|
|
147
|
+
const result = {};
|
|
148
|
+
const walk = (s) => {
|
|
149
|
+
for (const key in s.entries) {
|
|
150
|
+
const item = s.entries[key];
|
|
151
|
+
if (item instanceof Settings) {
|
|
152
|
+
walk(item);
|
|
153
|
+
}
|
|
154
|
+
else if (item instanceof RepeaterSetting) {
|
|
155
|
+
item.items.forEach(walk);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
const setting = item;
|
|
159
|
+
if (setting.isTranslatable) {
|
|
160
|
+
const val = setting.value;
|
|
161
|
+
const langs = {};
|
|
162
|
+
for (const lang of languages) {
|
|
163
|
+
langs[lang.code] =
|
|
164
|
+
val[lang.code] || (lang.code === "en" ? val["en"] : "");
|
|
165
|
+
}
|
|
166
|
+
result[key] = langs;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
walk(settings);
|
|
172
|
+
return result;
|
|
173
|
+
};
|
|
174
|
+
const globalEntry = ComponentsInstance.get(GLOBAL_KEY);
|
|
175
|
+
if (globalEntry) {
|
|
176
|
+
const tr = collectCompTrans(globalEntry.settings);
|
|
177
|
+
if (Object.keys(tr).length > 0)
|
|
178
|
+
dictionary[`Global-1`] = tr;
|
|
179
|
+
}
|
|
180
|
+
const counts = {};
|
|
181
|
+
for (const [id, entry] of ComponentsInstance) {
|
|
182
|
+
if (id === GLOBAL_KEY)
|
|
183
|
+
continue;
|
|
184
|
+
counts[entry.name] = (counts[entry.name] || 0) + 1;
|
|
185
|
+
const compId = `${entry.name}-${counts[entry.name]}`;
|
|
186
|
+
const tr = collectCompTrans(entry.settings);
|
|
187
|
+
if (Object.keys(tr).length > 0)
|
|
188
|
+
dictionary[compId] = tr;
|
|
189
|
+
}
|
|
190
|
+
return JSON.stringify(dictionary, null, 2);
|
|
191
|
+
}
|
|
192
|
+
export function importTranslations(json) {
|
|
193
|
+
const dictionary = JSON.parse(json);
|
|
194
|
+
saveHistory();
|
|
195
|
+
const applyCompTrans = (settings, compTrans) => {
|
|
196
|
+
const walk = (s) => {
|
|
197
|
+
for (const key in s.entries) {
|
|
198
|
+
const item = s.entries[key];
|
|
199
|
+
if (item instanceof Settings) {
|
|
200
|
+
walk(item);
|
|
201
|
+
}
|
|
202
|
+
else if (item instanceof RepeaterSetting) {
|
|
203
|
+
item.items.forEach(walk);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
const setting = item;
|
|
207
|
+
if (setting.isTranslatable && compTrans[key]) {
|
|
208
|
+
const val = setting.value;
|
|
209
|
+
setting.value = { ...val, ...compTrans[key] };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
walk(settings);
|
|
215
|
+
};
|
|
216
|
+
const globalEntry = ComponentsInstance.get(GLOBAL_KEY);
|
|
217
|
+
if (globalEntry && dictionary[`Global-1`]) {
|
|
218
|
+
applyCompTrans(globalEntry.settings, dictionary[`Global-1`]);
|
|
219
|
+
trackSettings(globalEntry);
|
|
220
|
+
}
|
|
221
|
+
const counts = {};
|
|
222
|
+
for (const [id, entry] of ComponentsInstance) {
|
|
223
|
+
if (id !== GLOBAL_KEY) {
|
|
224
|
+
counts[entry.name] = (counts[entry.name] || 0) + 1;
|
|
225
|
+
const compId = `${entry.name}-${counts[entry.name]}`;
|
|
226
|
+
if (dictionary[compId]) {
|
|
227
|
+
applyCompTrans(entry.settings, dictionary[compId]);
|
|
228
|
+
trackSettings(entry);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Settings } from '../settings';
|
|
2
|
+
const settingsModules = import.meta.glob('../elements/**/settings.ts', {
|
|
3
|
+
eager: true
|
|
4
|
+
});
|
|
5
|
+
const GlobalSettingModule = import.meta.glob('../elements/globalSettings.ts', {
|
|
6
|
+
eager: true
|
|
7
|
+
});
|
|
8
|
+
const componentModules = import.meta.glob('../elements/*/*.svelte', {
|
|
9
|
+
eager: true
|
|
10
|
+
});
|
|
11
|
+
function findSettings(mod) {
|
|
12
|
+
for (const value of Object.values(mod)) {
|
|
13
|
+
if (value instanceof Settings)
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function extractFolderName(path) {
|
|
18
|
+
return path.split('/').at(-2);
|
|
19
|
+
}
|
|
20
|
+
function findComponentModule(folderName) {
|
|
21
|
+
const path = Object.keys(componentModules).find((p) => p.includes(`/elements/${folderName}/`));
|
|
22
|
+
return path ? componentModules[path] : undefined;
|
|
23
|
+
}
|
|
24
|
+
function discoverElements() {
|
|
25
|
+
const entries = [];
|
|
26
|
+
for (const [path, mod] of Object.entries(settingsModules)) {
|
|
27
|
+
const folderName = extractFolderName(path);
|
|
28
|
+
const settings = findSettings(mod);
|
|
29
|
+
const componentModule = findComponentModule(folderName);
|
|
30
|
+
if (settings && componentModule) {
|
|
31
|
+
entries.push({
|
|
32
|
+
id: crypto.randomUUID(),
|
|
33
|
+
name: folderName,
|
|
34
|
+
settings,
|
|
35
|
+
component: componentModule.default
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return entries;
|
|
40
|
+
}
|
|
41
|
+
export function discoverGlobalSettings() {
|
|
42
|
+
const allEntries = {};
|
|
43
|
+
for (const mod of Object.values(GlobalSettingModule)) {
|
|
44
|
+
const settings = findSettings(mod);
|
|
45
|
+
if (settings) {
|
|
46
|
+
Object.assign(allEntries, settings.entries);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return new Settings('Global', allEntries);
|
|
50
|
+
}
|
|
51
|
+
export const elements = $state(discoverElements());
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const translating: {
|
|
2
|
+
active: boolean;
|
|
3
|
+
};
|
|
4
|
+
type Translator = (text: string, targetLang: string) => Promise<string>;
|
|
5
|
+
export declare function setTranslator(fn: Translator | null): void;
|
|
6
|
+
export interface TranslateResult {
|
|
7
|
+
hasSource: boolean;
|
|
8
|
+
translated: number;
|
|
9
|
+
failed: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function translateAll(): Promise<TranslateResult>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Setting, Settings } from '../settings/base.svelte';
|
|
2
|
+
import { RepeaterSetting } from '../settings/repeater.svelte';
|
|
3
|
+
import { languages } from '../hooks/useTranslation.svelte';
|
|
4
|
+
import { ComponentsInstance } from './element.action.svelte';
|
|
5
|
+
import { saveHistory } from './element.history.svelte';
|
|
6
|
+
const SOURCE_LOCALE = 'en';
|
|
7
|
+
const REQUEST_TIMEOUT_MS = 10000;
|
|
8
|
+
export const translating = $state({ active: false });
|
|
9
|
+
let translator = null;
|
|
10
|
+
export function setTranslator(fn) {
|
|
11
|
+
translator = fn;
|
|
12
|
+
}
|
|
13
|
+
function collectTranslatable(settings, out) {
|
|
14
|
+
for (const key in settings.entries) {
|
|
15
|
+
const item = settings.entries[key];
|
|
16
|
+
if (item instanceof Settings) {
|
|
17
|
+
collectTranslatable(item, out);
|
|
18
|
+
}
|
|
19
|
+
else if (item instanceof RepeaterSetting) {
|
|
20
|
+
item.items.forEach((child) => collectTranslatable(child, out));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const setting = item;
|
|
24
|
+
if (setting.isTranslatable)
|
|
25
|
+
out.push(setting);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function translateText(text, target) {
|
|
30
|
+
if (translator)
|
|
31
|
+
return translator(text, target);
|
|
32
|
+
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${SOURCE_LOCALE}&tl=${encodeURIComponent(target)}&dt=t&q=${encodeURIComponent(text)}`;
|
|
33
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });
|
|
34
|
+
if (!response.ok)
|
|
35
|
+
throw new Error(`Translation request failed (${response.status})`);
|
|
36
|
+
const data = await response.json();
|
|
37
|
+
if (!Array.isArray(data) || !Array.isArray(data[0])) {
|
|
38
|
+
throw new Error('Unexpected translation response');
|
|
39
|
+
}
|
|
40
|
+
return data[0]
|
|
41
|
+
.map((segment) => (Array.isArray(segment) ? String(segment[0] ?? '') : ''))
|
|
42
|
+
.join('');
|
|
43
|
+
}
|
|
44
|
+
export async function translateAll() {
|
|
45
|
+
const targets = languages.filter((lang) => lang.code !== SOURCE_LOCALE).map((lang) => lang.code);
|
|
46
|
+
const leaves = [];
|
|
47
|
+
for (const entry of ComponentsInstance.values())
|
|
48
|
+
collectTranslatable(entry.settings, leaves);
|
|
49
|
+
const jobs = [];
|
|
50
|
+
let hasSource = false;
|
|
51
|
+
for (const setting of leaves) {
|
|
52
|
+
const source = setting.value?.[SOURCE_LOCALE];
|
|
53
|
+
if (typeof source !== 'string' || !source.trim())
|
|
54
|
+
continue;
|
|
55
|
+
hasSource = true;
|
|
56
|
+
for (const target of targets)
|
|
57
|
+
jobs.push({ setting, target, text: source });
|
|
58
|
+
}
|
|
59
|
+
if (!hasSource)
|
|
60
|
+
return { hasSource: false, translated: 0, failed: 0 };
|
|
61
|
+
saveHistory();
|
|
62
|
+
translating.active = true;
|
|
63
|
+
let translated = 0;
|
|
64
|
+
let failed = 0;
|
|
65
|
+
try {
|
|
66
|
+
for (const job of jobs) {
|
|
67
|
+
try {
|
|
68
|
+
const result = await translateText(job.text, job.target);
|
|
69
|
+
job.setting.value = { ...job.setting.value, [job.target]: result };
|
|
70
|
+
translated++;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
failed++;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
translating.active = false;
|
|
79
|
+
}
|
|
80
|
+
return { hasSource: true, translated, failed };
|
|
81
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Settings, type SettingMap } from '../settings';
|
|
2
|
+
import type { Component } from 'svelte';
|
|
3
|
+
export type { ExportedEntry, ExportedState } from '../renderer/types';
|
|
4
|
+
export interface ElementEntry {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
settings: Settings<SettingMap>;
|
|
8
|
+
component: Component<Record<string, unknown>> | null;
|
|
9
|
+
}
|
|
10
|
+
export type SettingsModule = Record<string, unknown>;
|
|
11
|
+
export type ComponentModule = {
|
|
12
|
+
default: Component<Record<string, unknown>>;
|
|
13
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { Settings } from '../settings';
|
|
@@ -2,9 +2,11 @@ import type { Component } from 'svelte';
|
|
|
2
2
|
import type { BaseSettingProps, SettingMap, SettingComponent } from './types.js';
|
|
3
3
|
export declare abstract class Setting<V, E extends Record<string, unknown> = Record<string, unknown>> {
|
|
4
4
|
value: V;
|
|
5
|
+
mobileValue: V | undefined;
|
|
5
6
|
readonly title: string;
|
|
6
7
|
readonly extra: E;
|
|
7
8
|
readonly isTranslatable: boolean;
|
|
9
|
+
lastChangedMobile: boolean;
|
|
8
10
|
onValueChange: ((oldValue: V, newValue: V) => void) | null;
|
|
9
11
|
abstract readonly component: Component<BaseSettingProps<V> & E>;
|
|
10
12
|
constructor(options: {
|
|
@@ -12,11 +14,14 @@ export declare abstract class Setting<V, E extends Record<string, unknown> = Rec
|
|
|
12
14
|
defaultValue: V;
|
|
13
15
|
extra?: E;
|
|
14
16
|
});
|
|
17
|
+
get effectiveValue(): V;
|
|
18
|
+
private previewing;
|
|
15
19
|
private previewOrigin;
|
|
20
|
+
private resetMobile;
|
|
16
21
|
getProps(): BaseSettingProps<V> & E;
|
|
17
22
|
}
|
|
18
23
|
type SettingsValues<T extends SettingMap> = {
|
|
19
|
-
[K in keyof T]: T[K] extends SettingComponent ? T[K]['value'] : never;
|
|
24
|
+
[K in keyof T]: T[K] extends Settings<infer U> ? SettingsValues<U> : T[K] extends SettingComponent ? T[K]['value'] : never;
|
|
20
25
|
};
|
|
21
26
|
export type SettingsEntry = Setting<unknown, Record<string, unknown>> | Settings<SettingMap>;
|
|
22
27
|
export declare function isSettingsGroup(entry: SettingsEntry): entry is Settings<SettingMap>;
|
|
@@ -1,31 +1,64 @@
|
|
|
1
|
-
import { locale } from '
|
|
1
|
+
import { locale } from '../hooks/useTranslation.svelte.js';
|
|
2
|
+
import { settingsMode } from './mode.svelte.js';
|
|
2
3
|
export class Setting {
|
|
3
4
|
value = $state();
|
|
5
|
+
mobileValue = $state(undefined);
|
|
4
6
|
title;
|
|
5
7
|
extra;
|
|
6
8
|
isTranslatable = false;
|
|
9
|
+
lastChangedMobile = false;
|
|
7
10
|
onValueChange = null;
|
|
8
11
|
constructor(options) {
|
|
9
12
|
this.title = options.title;
|
|
10
13
|
this.value = options.defaultValue;
|
|
11
14
|
this.extra = (options.extra ?? {});
|
|
12
15
|
}
|
|
13
|
-
|
|
16
|
+
get effectiveValue() {
|
|
17
|
+
return settingsMode.device === 'mobile' && this.mobileValue !== undefined
|
|
18
|
+
? this.mobileValue
|
|
19
|
+
: this.value;
|
|
20
|
+
}
|
|
21
|
+
previewing = false;
|
|
22
|
+
previewOrigin = undefined;
|
|
23
|
+
resetMobile() {
|
|
24
|
+
const oldValue = this.mobileValue;
|
|
25
|
+
this.lastChangedMobile = true;
|
|
26
|
+
this.mobileValue = undefined;
|
|
27
|
+
this.onValueChange?.(oldValue, this.value);
|
|
28
|
+
}
|
|
14
29
|
getProps() {
|
|
30
|
+
const mobile = settingsMode.device === 'mobile';
|
|
31
|
+
const current = mobile ? (this.mobileValue ?? this.value) : this.value;
|
|
15
32
|
return {
|
|
16
|
-
value:
|
|
33
|
+
value: current,
|
|
17
34
|
title: this.title,
|
|
35
|
+
isMobile: mobile,
|
|
36
|
+
overridden: mobile && this.mobileValue !== undefined,
|
|
37
|
+
onreset: mobile ? () => this.resetMobile() : undefined,
|
|
18
38
|
onchange: (v) => {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
39
|
+
const rawOld = this.previewing
|
|
40
|
+
? this.previewOrigin
|
|
41
|
+
: mobile
|
|
42
|
+
? this.mobileValue
|
|
43
|
+
: this.value;
|
|
44
|
+
this.previewing = false;
|
|
45
|
+
this.previewOrigin = undefined;
|
|
46
|
+
this.lastChangedMobile = mobile;
|
|
47
|
+
if (mobile)
|
|
48
|
+
this.mobileValue = v;
|
|
49
|
+
else
|
|
50
|
+
this.value = v;
|
|
51
|
+
this.onValueChange?.(rawOld, v);
|
|
23
52
|
},
|
|
24
53
|
onpreview: (v) => {
|
|
25
|
-
if (this.
|
|
26
|
-
this.
|
|
54
|
+
if (!this.previewing) {
|
|
55
|
+
this.previewing = true;
|
|
56
|
+
this.previewOrigin = mobile ? this.mobileValue : this.value;
|
|
27
57
|
}
|
|
28
|
-
|
|
58
|
+
if (mobile)
|
|
59
|
+
this.mobileValue = v;
|
|
60
|
+
else
|
|
61
|
+
this.value = v;
|
|
29
62
|
},
|
|
30
63
|
...this.extra
|
|
31
64
|
};
|
|
@@ -48,17 +81,27 @@ export class Settings {
|
|
|
48
81
|
if (entry instanceof Settings) {
|
|
49
82
|
clonedEntries[key] = entry.clone();
|
|
50
83
|
}
|
|
84
|
+
else if (typeof entry.clone === 'function') {
|
|
85
|
+
clonedEntries[key] = entry.clone();
|
|
86
|
+
}
|
|
51
87
|
else {
|
|
52
88
|
const setting = entry;
|
|
53
89
|
const defaultValue = typeof setting.value === 'object' && setting.value !== null
|
|
54
90
|
? JSON.parse(JSON.stringify(setting.value))
|
|
55
91
|
: setting.value;
|
|
56
92
|
const Ctor = setting.constructor;
|
|
57
|
-
|
|
93
|
+
const clonedSetting = new Ctor({
|
|
58
94
|
title: setting.title,
|
|
59
95
|
defaultValue,
|
|
60
96
|
extra: setting.extra
|
|
61
97
|
});
|
|
98
|
+
if (setting.mobileValue !== undefined) {
|
|
99
|
+
clonedSetting.mobileValue =
|
|
100
|
+
typeof setting.mobileValue === 'object' && setting.mobileValue !== null
|
|
101
|
+
? JSON.parse(JSON.stringify(setting.mobileValue))
|
|
102
|
+
: setting.mobileValue;
|
|
103
|
+
}
|
|
104
|
+
clonedEntries[key] = clonedSetting;
|
|
62
105
|
}
|
|
63
106
|
}
|
|
64
107
|
return new Settings(this.title, clonedEntries);
|
|
@@ -68,19 +111,20 @@ export class Settings {
|
|
|
68
111
|
for (const key in this.entries) {
|
|
69
112
|
const entry = this.entries[key];
|
|
70
113
|
if (entry instanceof Settings) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
114
|
+
result[key] = entry.values;
|
|
115
|
+
}
|
|
116
|
+
else if (typeof entry.resolvedValues === 'function') {
|
|
117
|
+
result[key] = entry.resolvedValues();
|
|
75
118
|
}
|
|
76
119
|
else {
|
|
77
120
|
const setting = entry;
|
|
121
|
+
const effective = setting.effectiveValue;
|
|
78
122
|
if (setting.isTranslatable) {
|
|
79
|
-
const map =
|
|
123
|
+
const map = effective;
|
|
80
124
|
result[key] = map[locale.current] ?? map['en'] ?? '';
|
|
81
125
|
}
|
|
82
126
|
else {
|
|
83
|
-
result[key] =
|
|
127
|
+
result[key] = effective;
|
|
84
128
|
}
|
|
85
129
|
}
|
|
86
130
|
}
|