@pushwoosh/brand-book 0.0.0-stage → 0.1.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/DeriveThemeSection/DeriveThemeSection.d.ts +3 -0
- package/DeriveThemeSection/DeriveThemeSection.js +459 -0
- package/DeriveThemeSection/index.d.ts +1 -0
- package/DeriveThemeSection/index.js +1 -0
- package/DeriveThemeSection/polyglot/index.d.ts +2 -0
- package/DeriveThemeSection/polyglot/index.js +33 -0
- package/DeriveThemeSection/polyglot/messages_ar.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_ar.js +29 -0
- package/DeriveThemeSection/polyglot/messages_bn.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_bn.js +29 -0
- package/DeriveThemeSection/polyglot/messages_de.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_de.js +29 -0
- package/DeriveThemeSection/polyglot/messages_en.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_en.js +29 -0
- package/DeriveThemeSection/polyglot/messages_es.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_es.js +29 -0
- package/DeriveThemeSection/polyglot/messages_fr.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_fr.js +29 -0
- package/DeriveThemeSection/polyglot/messages_he.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_he.js +29 -0
- package/DeriveThemeSection/polyglot/messages_hi.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_hi.js +29 -0
- package/DeriveThemeSection/polyglot/messages_ja.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_ja.js +29 -0
- package/DeriveThemeSection/polyglot/messages_ko.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_ko.js +29 -0
- package/DeriveThemeSection/polyglot/messages_pt.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_pt.js +29 -0
- package/DeriveThemeSection/polyglot/messages_ru.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_ru.js +29 -0
- package/DeriveThemeSection/polyglot/messages_th.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_th.js +29 -0
- package/DeriveThemeSection/polyglot/messages_zh.d.ts +3 -0
- package/DeriveThemeSection/polyglot/messages_zh.js +29 -0
- package/DeriveThemeSection/polyglot/types.d.ts +44 -0
- package/DeriveThemeSection/polyglot/types.js +1 -0
- package/DeriveThemeSection/useExtractTheme.d.ts +19 -0
- package/DeriveThemeSection/useExtractTheme.js +44 -0
- package/brandModel.d.ts +23 -0
- package/brandModel.js +125 -0
- package/brandStore.d.ts +5 -0
- package/brandStore.js +56 -0
- package/defaults.d.ts +14 -0
- package/defaults.js +189 -0
- package/fonts.d.ts +29 -0
- package/fonts.js +140 -0
- package/index.d.ts +7 -0
- package/index.js +7 -0
- package/package.json +34 -3
- package/sources.d.ts +21 -0
- package/sources.js +110 -0
- package/types.d.ts +102 -0
- package/types.js +1 -0
- package/README.md +0 -3
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { DeriveThemeSectionProps } from '../types';
|
|
2
|
+
/** Derives a brand from a site URL, or from attached materials plus the written voice; the caller applies it. */
|
|
3
|
+
export declare function DeriveThemeSection({ application, extractTheme, logoSrc, disabled, onTheme, voice, onVoice, onLogo, }: DeriveThemeSectionProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { Button } from '@pushwoosh/dumb-components';
|
|
4
|
+
import { Color } from '@pushwoosh/kit-constants';
|
|
5
|
+
import { useLang } from './polyglot';
|
|
6
|
+
import { useExtractTheme } from './useExtractTheme';
|
|
7
|
+
import { ACCEPTED_IMAGE_TYPES, ACCEPTED_SOURCE_ACCEPT, fileToReferenceImage, fileToSource } from '../sources';
|
|
8
|
+
const HINT = {
|
|
9
|
+
fontSize: 12,
|
|
10
|
+
color: Color.LOCKED,
|
|
11
|
+
marginBottom: 8
|
|
12
|
+
};
|
|
13
|
+
const LABEL = {
|
|
14
|
+
fontSize: 12,
|
|
15
|
+
fontWeight: 600,
|
|
16
|
+
color: Color.MAIN,
|
|
17
|
+
marginBottom: 6
|
|
18
|
+
};
|
|
19
|
+
const INPUT = {
|
|
20
|
+
width: '100%',
|
|
21
|
+
fontSize: 13,
|
|
22
|
+
padding: '8px 10px',
|
|
23
|
+
borderRadius: 6,
|
|
24
|
+
border: `1px solid ${Color.FORM}`,
|
|
25
|
+
color: Color.MAIN,
|
|
26
|
+
boxSizing: 'border-box',
|
|
27
|
+
fontFamily: 'inherit'
|
|
28
|
+
};
|
|
29
|
+
const TEXTAREA = {
|
|
30
|
+
...INPUT,
|
|
31
|
+
resize: 'vertical',
|
|
32
|
+
minHeight: 72
|
|
33
|
+
};
|
|
34
|
+
const SITE_BLOCK = {
|
|
35
|
+
border: `1px solid ${Color.FORM}`,
|
|
36
|
+
background: Color.BRIGHT_LIGHT,
|
|
37
|
+
borderRadius: 8,
|
|
38
|
+
padding: 12
|
|
39
|
+
};
|
|
40
|
+
const FOUND = {
|
|
41
|
+
marginTop: 10,
|
|
42
|
+
border: `1px solid ${Color.FORM}`,
|
|
43
|
+
background: Color.CLEAR,
|
|
44
|
+
borderRadius: 8,
|
|
45
|
+
padding: '8px 10px',
|
|
46
|
+
fontSize: 12,
|
|
47
|
+
color: Color.LOCKED
|
|
48
|
+
};
|
|
49
|
+
const FOUND_HEAD = {
|
|
50
|
+
color: Color.SUCCESS,
|
|
51
|
+
marginBottom: 4
|
|
52
|
+
};
|
|
53
|
+
const SWATCHES = {
|
|
54
|
+
display: 'flex',
|
|
55
|
+
gap: 4,
|
|
56
|
+
margin: '6px 0',
|
|
57
|
+
flexWrap: 'wrap'
|
|
58
|
+
};
|
|
59
|
+
const SWATCH = {
|
|
60
|
+
width: 22,
|
|
61
|
+
height: 22,
|
|
62
|
+
borderRadius: 5,
|
|
63
|
+
border: '1px solid rgba(16,24,40,.15)'
|
|
64
|
+
};
|
|
65
|
+
const CHIP = {
|
|
66
|
+
display: 'inline-flex',
|
|
67
|
+
alignItems: 'center',
|
|
68
|
+
gap: 6,
|
|
69
|
+
maxWidth: '100%',
|
|
70
|
+
fontSize: 12,
|
|
71
|
+
color: Color.MAIN,
|
|
72
|
+
background: Color.FROZEN,
|
|
73
|
+
border: `1px solid ${Color.FORM}`,
|
|
74
|
+
borderRadius: 6,
|
|
75
|
+
padding: '3px 8px'
|
|
76
|
+
};
|
|
77
|
+
const CHIP_NAME = {
|
|
78
|
+
overflow: 'hidden',
|
|
79
|
+
textOverflow: 'ellipsis',
|
|
80
|
+
whiteSpace: 'nowrap'
|
|
81
|
+
};
|
|
82
|
+
const CHIP_X = {
|
|
83
|
+
border: 'none',
|
|
84
|
+
background: 'none',
|
|
85
|
+
cursor: 'pointer',
|
|
86
|
+
color: Color.LOCKED,
|
|
87
|
+
fontSize: 14,
|
|
88
|
+
lineHeight: 1,
|
|
89
|
+
padding: 0
|
|
90
|
+
};
|
|
91
|
+
const OR = {
|
|
92
|
+
fontSize: 11,
|
|
93
|
+
letterSpacing: '.08em',
|
|
94
|
+
textTransform: 'uppercase',
|
|
95
|
+
color: Color.LOCKED
|
|
96
|
+
};
|
|
97
|
+
const NOTE = {
|
|
98
|
+
fontSize: 12,
|
|
99
|
+
color: Color.WARNING
|
|
100
|
+
};
|
|
101
|
+
const ERROR = {
|
|
102
|
+
fontSize: 12,
|
|
103
|
+
color: Color.DANGER
|
|
104
|
+
};
|
|
105
|
+
function hostOf(rawUrl) {
|
|
106
|
+
return rawUrl.trim().replace(/^https?:\/\//i, '').replace(/\/.*$/, '');
|
|
107
|
+
}
|
|
108
|
+
function summarize(host, brand) {
|
|
109
|
+
const fonts = [brand.theme.fontFamily, brand.theme.headingFontFamily].filter(Boolean).map(stack => stack.split(',')[0].replace(/["']/g, '').trim());
|
|
110
|
+
return {
|
|
111
|
+
host,
|
|
112
|
+
colors: Object.values(brand.theme.colors ?? {}).filter(Boolean),
|
|
113
|
+
fonts: Array.from(new Set(fonts)),
|
|
114
|
+
logo: brand.logoUrl !== '',
|
|
115
|
+
voice: brand.brandVoice.trim() !== ''
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** Derives a brand from a site URL, or from attached materials plus the written voice; the caller applies it. */
|
|
119
|
+
export function DeriveThemeSection({
|
|
120
|
+
application,
|
|
121
|
+
extractTheme,
|
|
122
|
+
logoSrc,
|
|
123
|
+
disabled = false,
|
|
124
|
+
onTheme,
|
|
125
|
+
voice,
|
|
126
|
+
onVoice,
|
|
127
|
+
onLogo
|
|
128
|
+
}) {
|
|
129
|
+
const lang = useLang();
|
|
130
|
+
const {
|
|
131
|
+
extract,
|
|
132
|
+
extracting,
|
|
133
|
+
error
|
|
134
|
+
} = useExtractTheme(application, extractTheme);
|
|
135
|
+
const fileInputRef = useRef(null);
|
|
136
|
+
const [siteUrl, setSiteUrl] = useState('');
|
|
137
|
+
const [scan, setScan] = useState(null);
|
|
138
|
+
const [localVoice, setLocalVoice] = useState('');
|
|
139
|
+
const [image, setImage] = useState(null);
|
|
140
|
+
const [sources, setSources] = useState([]);
|
|
141
|
+
const nextSourceId = useRef(1);
|
|
142
|
+
const [note, setNote] = useState(null);
|
|
143
|
+
const logoAtRequest = useRef(logoSrc);
|
|
144
|
+
const liveLogoSrc = useRef(logoSrc);
|
|
145
|
+
liveLogoSrc.current = logoSrc;
|
|
146
|
+
const voiceValue = voice ?? localVoice;
|
|
147
|
+
const setVoice = onVoice ?? setLocalVoice;
|
|
148
|
+
const busy = disabled || extracting;
|
|
149
|
+
const readErrorNote = name => lang({
|
|
150
|
+
message: 'BRAND_BOOK.DERIVE_THEME.SKIP_READ_ERROR',
|
|
151
|
+
name
|
|
152
|
+
});
|
|
153
|
+
const addFiles = async files => {
|
|
154
|
+
const notes = [];
|
|
155
|
+
let pickedImage = image;
|
|
156
|
+
for (const file of files) {
|
|
157
|
+
if (ACCEPTED_IMAGE_TYPES.includes(file.type)) {
|
|
158
|
+
if (pickedImage) {
|
|
159
|
+
notes.push(lang({
|
|
160
|
+
message: 'BRAND_BOOK.DERIVE_THEME.SKIP_ONLY_ONE_IMAGE',
|
|
161
|
+
name: file.name
|
|
162
|
+
}));
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const ref = await fileToReferenceImage(file);
|
|
166
|
+
if (ref) {
|
|
167
|
+
pickedImage = {
|
|
168
|
+
image: ref,
|
|
169
|
+
name: file.name
|
|
170
|
+
};
|
|
171
|
+
setImage(pickedImage);
|
|
172
|
+
} else notes.push(readErrorNote(file.name));
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const result = await fileToSource(file);
|
|
176
|
+
if (result.ok) {
|
|
177
|
+
const id = nextSourceId.current;
|
|
178
|
+
nextSourceId.current += 1;
|
|
179
|
+
setSources(prev => [...prev, {
|
|
180
|
+
id,
|
|
181
|
+
source: result.source
|
|
182
|
+
}]);
|
|
183
|
+
} else if (result.reason === 'too-large') {
|
|
184
|
+
notes.push(lang({
|
|
185
|
+
message: 'BRAND_BOOK.DERIVE_THEME.SKIP_TOO_LARGE',
|
|
186
|
+
name: file.name
|
|
187
|
+
}));
|
|
188
|
+
} else if (result.reason === 'empty') {
|
|
189
|
+
notes.push(lang({
|
|
190
|
+
message: 'BRAND_BOOK.DERIVE_THEME.SKIP_EMPTY',
|
|
191
|
+
name: file.name
|
|
192
|
+
}));
|
|
193
|
+
} else {
|
|
194
|
+
notes.push(readErrorNote(file.name));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
setNote(notes.length > 0 ? notes.join(' ') : null);
|
|
198
|
+
};
|
|
199
|
+
const referenceImage = async () => {
|
|
200
|
+
if (image) return {
|
|
201
|
+
ok: true,
|
|
202
|
+
image: image.image
|
|
203
|
+
};
|
|
204
|
+
if (logoSrc.trim() === '') return {
|
|
205
|
+
ok: true
|
|
206
|
+
};
|
|
207
|
+
try {
|
|
208
|
+
const resp = await fetch(logoSrc);
|
|
209
|
+
const blob = await resp.blob();
|
|
210
|
+
const ref = await fileToReferenceImage(new File([blob], 'brand-logo', {
|
|
211
|
+
type: blob.type
|
|
212
|
+
}));
|
|
213
|
+
return ref ? {
|
|
214
|
+
ok: true,
|
|
215
|
+
image: ref
|
|
216
|
+
} : {
|
|
217
|
+
ok: false
|
|
218
|
+
};
|
|
219
|
+
} catch {
|
|
220
|
+
return {
|
|
221
|
+
ok: false
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const apply = (brand, host) => {
|
|
226
|
+
onTheme(brand.theme);
|
|
227
|
+
if (brand.brandVoice.trim() !== '') setVoice(brand.brandVoice.trim());
|
|
228
|
+
if (brand.logoUrl !== '' && liveLogoSrc.current === logoAtRequest.current) onLogo?.(brand.logoUrl);
|
|
229
|
+
setScan(host === '' ? null : summarize(host, brand));
|
|
230
|
+
};
|
|
231
|
+
const scanSite = async () => {
|
|
232
|
+
setNote(null);
|
|
233
|
+
logoAtRequest.current = logoSrc;
|
|
234
|
+
const brand = await extract({
|
|
235
|
+
siteUrl: siteUrl.trim()
|
|
236
|
+
});
|
|
237
|
+
if (brand) apply(brand, hostOf(siteUrl));
|
|
238
|
+
};
|
|
239
|
+
const deriveFromMaterials = async () => {
|
|
240
|
+
setNote(null);
|
|
241
|
+
logoAtRequest.current = logoSrc;
|
|
242
|
+
const logo = await referenceImage();
|
|
243
|
+
if (!logo.ok && sources.length === 0 && voiceValue.trim() === '') {
|
|
244
|
+
setNote(lang({
|
|
245
|
+
message: 'BRAND_BOOK.DERIVE_THEME.LOGO_LOAD_FAILED_ONLY'
|
|
246
|
+
}));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (!logo.ok) setNote(lang({
|
|
250
|
+
message: 'BRAND_BOOK.DERIVE_THEME.LOGO_LOAD_FAILED_FALLBACK'
|
|
251
|
+
}));
|
|
252
|
+
const brand = await extract({
|
|
253
|
+
image: logo.ok ? logo.image : undefined,
|
|
254
|
+
sources: sources.map(s => s.source),
|
|
255
|
+
description: voiceValue.trim()
|
|
256
|
+
});
|
|
257
|
+
if (brand) apply(brand, '');
|
|
258
|
+
};
|
|
259
|
+
const canScan = !busy && siteUrl.trim() !== '';
|
|
260
|
+
const canDerive = !busy && (!!image || sources.length > 0 || voiceValue.trim() !== '' || logoSrc.trim() !== '');
|
|
261
|
+
return _jsxs("div", {
|
|
262
|
+
style: {
|
|
263
|
+
display: 'flex',
|
|
264
|
+
flexDirection: 'column',
|
|
265
|
+
gap: 8,
|
|
266
|
+
marginBottom: 12
|
|
267
|
+
},
|
|
268
|
+
children: [_jsxs("div", {
|
|
269
|
+
style: SITE_BLOCK,
|
|
270
|
+
children: [_jsx("div", {
|
|
271
|
+
style: LABEL,
|
|
272
|
+
children: lang({
|
|
273
|
+
message: 'BRAND_BOOK.DERIVE_THEME.BUILD_FROM_WEBSITE'
|
|
274
|
+
})
|
|
275
|
+
}), _jsxs("div", {
|
|
276
|
+
style: {
|
|
277
|
+
display: 'flex',
|
|
278
|
+
gap: 8
|
|
279
|
+
},
|
|
280
|
+
children: [_jsx("input", {
|
|
281
|
+
style: INPUT,
|
|
282
|
+
value: siteUrl,
|
|
283
|
+
placeholder: lang({
|
|
284
|
+
message: 'BRAND_BOOK.DERIVE_THEME.SITE_URL_PLACEHOLDER'
|
|
285
|
+
}),
|
|
286
|
+
disabled: busy,
|
|
287
|
+
onChange: e => setSiteUrl(e.target.value),
|
|
288
|
+
onKeyDown: e => {
|
|
289
|
+
if (e.key === 'Enter' && canScan) void scanSite();
|
|
290
|
+
}
|
|
291
|
+
}), _jsx("div", {
|
|
292
|
+
style: {
|
|
293
|
+
flexShrink: 0
|
|
294
|
+
},
|
|
295
|
+
children: _jsx(Button, {
|
|
296
|
+
size: "compact",
|
|
297
|
+
color: "primary",
|
|
298
|
+
isDisabled: !canScan,
|
|
299
|
+
onClick: () => {
|
|
300
|
+
void scanSite();
|
|
301
|
+
},
|
|
302
|
+
children: extracting ? lang({
|
|
303
|
+
message: 'BRAND_BOOK.DERIVE_THEME.SCANNING'
|
|
304
|
+
}) : lang({
|
|
305
|
+
message: 'BRAND_BOOK.DERIVE_THEME.SCAN_SITE'
|
|
306
|
+
})
|
|
307
|
+
})
|
|
308
|
+
})]
|
|
309
|
+
}), _jsx("div", {
|
|
310
|
+
style: {
|
|
311
|
+
...HINT,
|
|
312
|
+
marginTop: 8,
|
|
313
|
+
marginBottom: 0
|
|
314
|
+
},
|
|
315
|
+
children: lang({
|
|
316
|
+
message: 'BRAND_BOOK.DERIVE_THEME.SCAN_EXPLAINER'
|
|
317
|
+
})
|
|
318
|
+
}), scan && _jsxs("div", {
|
|
319
|
+
style: FOUND,
|
|
320
|
+
children: [_jsxs("div", {
|
|
321
|
+
style: FOUND_HEAD,
|
|
322
|
+
children: [lang({
|
|
323
|
+
message: 'BRAND_BOOK.DERIVE_THEME.READ_CHECK'
|
|
324
|
+
}), ' ', scan.host]
|
|
325
|
+
}), _jsx("div", {
|
|
326
|
+
style: SWATCHES,
|
|
327
|
+
children: scan.colors.map(color => _jsx("span", {
|
|
328
|
+
style: {
|
|
329
|
+
...SWATCH,
|
|
330
|
+
background: color
|
|
331
|
+
},
|
|
332
|
+
title: color
|
|
333
|
+
}, color))
|
|
334
|
+
}), _jsxs("div", {
|
|
335
|
+
children: [lang({
|
|
336
|
+
message: 'BRAND_BOOK.DERIVE_THEME.COLOURS_COUNT',
|
|
337
|
+
count: scan.colors.length
|
|
338
|
+
}), scan.fonts.length > 0 && ` · ${scan.fonts.join(' · ')}`, scan.logo && ` · ${lang({
|
|
339
|
+
message: 'BRAND_BOOK.DERIVE_THEME.LOGO_TAG'
|
|
340
|
+
})}`, scan.voice && ` · ${lang({
|
|
341
|
+
message: 'BRAND_BOOK.DERIVE_THEME.VOICE_TAG'
|
|
342
|
+
})}`, ` — ${lang({
|
|
343
|
+
message: 'BRAND_BOOK.DERIVE_THEME.EDITABLE_BELOW'
|
|
344
|
+
})}`]
|
|
345
|
+
})]
|
|
346
|
+
})]
|
|
347
|
+
}), _jsx("div", {
|
|
348
|
+
style: OR,
|
|
349
|
+
children: lang({
|
|
350
|
+
message: 'BRAND_BOOK.DERIVE_THEME.OR'
|
|
351
|
+
})
|
|
352
|
+
}), _jsx("div", {
|
|
353
|
+
style: LABEL,
|
|
354
|
+
children: lang({
|
|
355
|
+
message: 'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_LABEL'
|
|
356
|
+
})
|
|
357
|
+
}), _jsx("div", {
|
|
358
|
+
style: HINT,
|
|
359
|
+
children: lang({
|
|
360
|
+
message: 'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_HINT'
|
|
361
|
+
})
|
|
362
|
+
}), _jsx("textarea", {
|
|
363
|
+
style: TEXTAREA,
|
|
364
|
+
value: voiceValue,
|
|
365
|
+
placeholder: lang({
|
|
366
|
+
message: 'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_PLACEHOLDER'
|
|
367
|
+
}),
|
|
368
|
+
disabled: busy,
|
|
369
|
+
onChange: e => setVoice(e.target.value)
|
|
370
|
+
}), (image || sources.length > 0) && _jsxs("div", {
|
|
371
|
+
style: {
|
|
372
|
+
display: 'flex',
|
|
373
|
+
flexWrap: 'wrap',
|
|
374
|
+
gap: 6
|
|
375
|
+
},
|
|
376
|
+
children: [image && _jsxs("span", {
|
|
377
|
+
style: CHIP,
|
|
378
|
+
children: [_jsxs("span", {
|
|
379
|
+
style: CHIP_NAME,
|
|
380
|
+
children: ["\uD83D\uDDBC", image.name]
|
|
381
|
+
}), _jsx("button", {
|
|
382
|
+
type: "button",
|
|
383
|
+
style: CHIP_X,
|
|
384
|
+
"aria-label": lang({
|
|
385
|
+
message: 'BRAND_BOOK.DERIVE_THEME.REMOVE_IMAGE'
|
|
386
|
+
}),
|
|
387
|
+
onClick: () => setImage(null),
|
|
388
|
+
children: "\u00D7"
|
|
389
|
+
})]
|
|
390
|
+
}), sources.map(({
|
|
391
|
+
id,
|
|
392
|
+
source
|
|
393
|
+
}) => _jsxs("span", {
|
|
394
|
+
style: CHIP,
|
|
395
|
+
children: [_jsx("span", {
|
|
396
|
+
style: CHIP_NAME,
|
|
397
|
+
children: source.name || lang({
|
|
398
|
+
message: 'BRAND_BOOK.DERIVE_THEME.FILE_FALLBACK_NAME'
|
|
399
|
+
})
|
|
400
|
+
}), _jsx("button", {
|
|
401
|
+
type: "button",
|
|
402
|
+
style: CHIP_X,
|
|
403
|
+
"aria-label": lang({
|
|
404
|
+
message: 'BRAND_BOOK.DERIVE_THEME.REMOVE_SOURCE',
|
|
405
|
+
name: source.name
|
|
406
|
+
}),
|
|
407
|
+
onClick: () => setSources(prev => prev.filter(s => s.id !== id)),
|
|
408
|
+
children: "\u00D7"
|
|
409
|
+
})]
|
|
410
|
+
}, id))]
|
|
411
|
+
}), _jsx("input", {
|
|
412
|
+
ref: fileInputRef,
|
|
413
|
+
type: "file",
|
|
414
|
+
multiple: true,
|
|
415
|
+
accept: [...ACCEPTED_IMAGE_TYPES, ACCEPTED_SOURCE_ACCEPT].join(','),
|
|
416
|
+
style: {
|
|
417
|
+
display: 'none'
|
|
418
|
+
},
|
|
419
|
+
onChange: e => {
|
|
420
|
+
const files = Array.from(e.target.files ?? []);
|
|
421
|
+
e.target.value = '';
|
|
422
|
+
void addFiles(files);
|
|
423
|
+
}
|
|
424
|
+
}), _jsxs("div", {
|
|
425
|
+
style: {
|
|
426
|
+
display: 'flex',
|
|
427
|
+
gap: 8,
|
|
428
|
+
flexWrap: 'wrap'
|
|
429
|
+
},
|
|
430
|
+
children: [_jsx(Button, {
|
|
431
|
+
size: "compact",
|
|
432
|
+
color: "secondary",
|
|
433
|
+
isDisabled: busy,
|
|
434
|
+
onClick: () => fileInputRef.current?.click(),
|
|
435
|
+
children: lang({
|
|
436
|
+
message: 'BRAND_BOOK.DERIVE_THEME.ATTACH_FILES'
|
|
437
|
+
})
|
|
438
|
+
}), _jsx(Button, {
|
|
439
|
+
size: "compact",
|
|
440
|
+
color: "primary",
|
|
441
|
+
isDisabled: !canDerive,
|
|
442
|
+
onClick: () => {
|
|
443
|
+
void deriveFromMaterials();
|
|
444
|
+
},
|
|
445
|
+
children: extracting ? lang({
|
|
446
|
+
message: 'BRAND_BOOK.DERIVE_THEME.READING'
|
|
447
|
+
}) : lang({
|
|
448
|
+
message: 'BRAND_BOOK.DERIVE_THEME.DERIVE_BRAND'
|
|
449
|
+
})
|
|
450
|
+
})]
|
|
451
|
+
}), note && _jsx("div", {
|
|
452
|
+
style: NOTE,
|
|
453
|
+
children: note
|
|
454
|
+
}), error && _jsx("div", {
|
|
455
|
+
style: ERROR,
|
|
456
|
+
children: error
|
|
457
|
+
})]
|
|
458
|
+
});
|
|
459
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DeriveThemeSection } from './DeriveThemeSection';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DeriveThemeSection } from './DeriveThemeSection';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createLang } from '@pushwoosh/dumb-components/polyglot';
|
|
2
|
+
import { messages as ar } from './messages_ar';
|
|
3
|
+
import { messages as bn } from './messages_bn';
|
|
4
|
+
import { messages as de } from './messages_de';
|
|
5
|
+
import { messages as en } from './messages_en';
|
|
6
|
+
import { messages as es } from './messages_es';
|
|
7
|
+
import { messages as fr } from './messages_fr';
|
|
8
|
+
import { messages as he } from './messages_he';
|
|
9
|
+
import { messages as hi } from './messages_hi';
|
|
10
|
+
import { messages as ja } from './messages_ja';
|
|
11
|
+
import { messages as ko } from './messages_ko';
|
|
12
|
+
import { messages as pt } from './messages_pt';
|
|
13
|
+
import { messages as ru } from './messages_ru';
|
|
14
|
+
import { messages as th } from './messages_th';
|
|
15
|
+
import { messages as zh } from './messages_zh';
|
|
16
|
+
export const {
|
|
17
|
+
useLang
|
|
18
|
+
} = createLang({
|
|
19
|
+
ar,
|
|
20
|
+
bn,
|
|
21
|
+
de,
|
|
22
|
+
en,
|
|
23
|
+
es,
|
|
24
|
+
fr,
|
|
25
|
+
he,
|
|
26
|
+
hi,
|
|
27
|
+
ja,
|
|
28
|
+
ko,
|
|
29
|
+
pt,
|
|
30
|
+
ru,
|
|
31
|
+
th,
|
|
32
|
+
zh
|
|
33
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const messages = {
|
|
2
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_ONLY_ONE_IMAGE': 'تم تخطي “{{name}}” — يتم استخدام صورة واحدة فقط.',
|
|
3
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_READ_ERROR': 'تعذرت قراءة “{{name}}”.',
|
|
4
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_TOO_LARGE': '“{{name}}” كبير جدًا.',
|
|
5
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_EMPTY': '“{{name}}” فارغ.',
|
|
6
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_LOAD_FAILED_ONLY': 'تعذر تحميل صورة الشعار — أرفقها كملف بدلاً من ذلك.',
|
|
7
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_LOAD_FAILED_FALLBACK': 'تعذر تحميل صورة الشعار؛ سيتم الاشتقاق من المواد الأخرى.',
|
|
8
|
+
'BRAND_BOOK.DERIVE_THEME.BUILD_FROM_WEBSITE': 'أنشئ من موقعك الإلكتروني',
|
|
9
|
+
'BRAND_BOOK.DERIVE_THEME.SITE_URL_PLACEHOLDER': 'acme.com',
|
|
10
|
+
'BRAND_BOOK.DERIVE_THEME.SCANNING': 'جارٍ المسح…',
|
|
11
|
+
'BRAND_BOOK.DERIVE_THEME.SCAN_SITE': 'مسح الموقع',
|
|
12
|
+
'BRAND_BOOK.DERIVE_THEME.SCAN_EXPLAINER': 'نقرأ الصفحة وملفات الأنماط الخاصة بها — الألوان والخطوط والشعار ونبرة النص. لا يتم تخزين أي شيء حتى تحفظ العلامة التجارية.',
|
|
13
|
+
'BRAND_BOOK.DERIVE_THEME.READ_CHECK': '✓ تمت القراءة',
|
|
14
|
+
'BRAND_BOOK.DERIVE_THEME.COLOURS_COUNT': '{{count}} ألوان',
|
|
15
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_TAG': 'شعار',
|
|
16
|
+
'BRAND_BOOK.DERIVE_THEME.VOICE_TAG': 'نبرة',
|
|
17
|
+
'BRAND_BOOK.DERIVE_THEME.EDITABLE_BELOW': 'كل شيء قابل للتعديل أدناه',
|
|
18
|
+
'BRAND_BOOK.DERIVE_THEME.OR': 'أو',
|
|
19
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_LABEL': 'نبرة العلامة التجارية',
|
|
20
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_HINT': 'كيف تتحدث العلامة التجارية. يملأ المسح هذا الحقل من الموقع؛ كما يوجه لوحة الألوان عند الاستخلاص من المواد المرفقة.',
|
|
21
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_PLACEHOLDER': 'مثال: حوارية وساخرة، جمل قصيرة، تخاطب القارئ مباشرة',
|
|
22
|
+
'BRAND_BOOK.DERIVE_THEME.REMOVE_IMAGE': 'إزالة الصورة',
|
|
23
|
+
'BRAND_BOOK.DERIVE_THEME.FILE_FALLBACK_NAME': 'ملف',
|
|
24
|
+
'BRAND_BOOK.DERIVE_THEME.REMOVE_SOURCE': 'إزالة {{name}}',
|
|
25
|
+
'BRAND_BOOK.DERIVE_THEME.ATTACH_FILES': 'إرفاق ملفات',
|
|
26
|
+
'BRAND_BOOK.DERIVE_THEME.READING': 'جارٍ القراءة…',
|
|
27
|
+
'BRAND_BOOK.DERIVE_THEME.DERIVE_BRAND': 'استخلاص العلامة التجارية',
|
|
28
|
+
'BRAND_BOOK.DERIVE_THEME.EXTRACT_FAILED': 'تعذرت قراءة العلامة التجارية: {{error}}'
|
|
29
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const messages = {
|
|
2
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_ONLY_ONE_IMAGE': '“{{name}}” এড়িয়ে যাওয়া হয়েছে — শুধুমাত্র একটি ছবি ব্যবহৃত হয়েছে।',
|
|
3
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_READ_ERROR': '“{{name}}” পড়া যায়নি।',
|
|
4
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_TOO_LARGE': '“{{name}}” খুব বড়।',
|
|
5
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_EMPTY': '“{{name}}” খালি।',
|
|
6
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_LOAD_FAILED_ONLY': 'লোগো ছবিটি লোড করা যায়নি — পরিবর্তে এটি একটি ফাইল হিসাবে সংযুক্ত করুন।',
|
|
7
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_LOAD_FAILED_FALLBACK': 'লোগো ছবিটি লোড করা যায়নি; অন্যান্য উপাদান থেকে তৈরি করা হচ্ছে।',
|
|
8
|
+
'BRAND_BOOK.DERIVE_THEME.BUILD_FROM_WEBSITE': 'আপনার ওয়েবসাইট থেকে তৈরি করুন',
|
|
9
|
+
'BRAND_BOOK.DERIVE_THEME.SITE_URL_PLACEHOLDER': 'acme.com',
|
|
10
|
+
'BRAND_BOOK.DERIVE_THEME.SCANNING': 'স্ক্যান করা হচ্ছে…',
|
|
11
|
+
'BRAND_BOOK.DERIVE_THEME.SCAN_SITE': 'সাইট স্ক্যান করুন',
|
|
12
|
+
'BRAND_BOOK.DERIVE_THEME.SCAN_EXPLAINER': 'আমরা পৃষ্ঠা এবং এর স্টাইলশীটগুলি পড়ি — রঙ, ফন্ট, লোগো এবং এর কপির টোন। আপনি ব্র্যান্ডটি সেভ না করা পর্যন্ত কিছুই সংরক্ষণ করা হয় না।',
|
|
13
|
+
'BRAND_BOOK.DERIVE_THEME.READ_CHECK': '✓ পড়া হয়েছে',
|
|
14
|
+
'BRAND_BOOK.DERIVE_THEME.COLOURS_COUNT': '{{count}}টি রঙ',
|
|
15
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_TAG': 'লোগো',
|
|
16
|
+
'BRAND_BOOK.DERIVE_THEME.VOICE_TAG': 'ভয়েস',
|
|
17
|
+
'BRAND_BOOK.DERIVE_THEME.EDITABLE_BELOW': 'নিচে সবকিছু সম্পাদনাযোগ্য',
|
|
18
|
+
'BRAND_BOOK.DERIVE_THEME.OR': 'অথবা',
|
|
19
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_LABEL': 'ব্র্যান্ড ভয়েস',
|
|
20
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_HINT': 'ব্র্যান্ডটি কীভাবে কথা বলে। একটি স্ক্যান সাইট থেকে এটি পূরণ করে; এটি সংযুক্ত উপকরণ থেকে ডিরাইভ করার সময় প্যালেটকেও চালনা করে।',
|
|
21
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_PLACEHOLDER': 'যেমন কথোপকথনমূলক এবং আত্ম-বিদ্রূপাত্মক, ছোট বাক্য, সরাসরি পাঠকের সাথে কথা বলে',
|
|
22
|
+
'BRAND_BOOK.DERIVE_THEME.REMOVE_IMAGE': 'ছবি সরান',
|
|
23
|
+
'BRAND_BOOK.DERIVE_THEME.FILE_FALLBACK_NAME': 'ফাইল',
|
|
24
|
+
'BRAND_BOOK.DERIVE_THEME.REMOVE_SOURCE': '{{name}} সরান',
|
|
25
|
+
'BRAND_BOOK.DERIVE_THEME.ATTACH_FILES': 'ফাইল সংযুক্ত করুন',
|
|
26
|
+
'BRAND_BOOK.DERIVE_THEME.READING': 'পড়া হচ্ছে…',
|
|
27
|
+
'BRAND_BOOK.DERIVE_THEME.DERIVE_BRAND': 'ব্র্যান্ড ডিরাইভ করুন',
|
|
28
|
+
'BRAND_BOOK.DERIVE_THEME.EXTRACT_FAILED': 'ব্র্যান্ডটি পড়া যায়নি: {{error}}'
|
|
29
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const messages = {
|
|
2
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_ONLY_ONE_IMAGE': '„{{name}}“ übersprungen – es wird nur ein Bild verwendet.',
|
|
3
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_READ_ERROR': '„{{name}}“ konnte nicht gelesen werden.',
|
|
4
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_TOO_LARGE': '„{{name}}“ ist zu groß.',
|
|
5
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_EMPTY': '„{{name}}“ ist leer.',
|
|
6
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_LOAD_FAILED_ONLY': 'Das Logo-Bild konnte nicht geladen werden – hängen Sie es stattdessen als Datei an.',
|
|
7
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_LOAD_FAILED_FALLBACK': 'Logo-Bild konnte nicht geladen werden; wird von anderen Materialien abgeleitet.',
|
|
8
|
+
'BRAND_BOOK.DERIVE_THEME.BUILD_FROM_WEBSITE': 'Von Ihrer Website erstellen',
|
|
9
|
+
'BRAND_BOOK.DERIVE_THEME.SITE_URL_PLACEHOLDER': 'acme.com',
|
|
10
|
+
'BRAND_BOOK.DERIVE_THEME.SCANNING': 'Wird gescannt…',
|
|
11
|
+
'BRAND_BOOK.DERIVE_THEME.SCAN_SITE': 'Website scannen',
|
|
12
|
+
'BRAND_BOOK.DERIVE_THEME.SCAN_EXPLAINER': 'Wir lesen die Seite und ihre Stylesheets – Farben, Schriftarten, Logo und den Tonfall der Texte. Nichts wird gespeichert, bis Sie die Marke speichern.',
|
|
13
|
+
'BRAND_BOOK.DERIVE_THEME.READ_CHECK': '✓ Gelesen',
|
|
14
|
+
'BRAND_BOOK.DERIVE_THEME.COLOURS_COUNT': '{{count}} Farben',
|
|
15
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_TAG': 'Logo',
|
|
16
|
+
'BRAND_BOOK.DERIVE_THEME.VOICE_TAG': 'Tonfall',
|
|
17
|
+
'BRAND_BOOK.DERIVE_THEME.EDITABLE_BELOW': 'alles unten bearbeitbar',
|
|
18
|
+
'BRAND_BOOK.DERIVE_THEME.OR': 'oder',
|
|
19
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_LABEL': 'Markenstimme',
|
|
20
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_HINT': 'Wie die Marke spricht. Ein Scan füllt dies von der Website aus; es steuert auch die Palette, wenn Sie sie aus angehängten Materialien ableiten.',
|
|
21
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_PLACEHOLDER': 'z. B. gesprächig und selbstironisch, kurze Sätze, spricht den Leser direkt an',
|
|
22
|
+
'BRAND_BOOK.DERIVE_THEME.REMOVE_IMAGE': 'Bild entfernen',
|
|
23
|
+
'BRAND_BOOK.DERIVE_THEME.FILE_FALLBACK_NAME': 'Datei',
|
|
24
|
+
'BRAND_BOOK.DERIVE_THEME.REMOVE_SOURCE': '{{name}} entfernen',
|
|
25
|
+
'BRAND_BOOK.DERIVE_THEME.ATTACH_FILES': 'Dateien anhängen',
|
|
26
|
+
'BRAND_BOOK.DERIVE_THEME.READING': 'Lese…',
|
|
27
|
+
'BRAND_BOOK.DERIVE_THEME.DERIVE_BRAND': 'Marke ableiten',
|
|
28
|
+
'BRAND_BOOK.DERIVE_THEME.EXTRACT_FAILED': 'Marke konnte nicht gelesen werden: {{error}}'
|
|
29
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const messages = {
|
|
2
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_ONLY_ONE_IMAGE': '“{{name}}” skipped — only one image is used.',
|
|
3
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_READ_ERROR': '“{{name}}” could not be read.',
|
|
4
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_TOO_LARGE': '“{{name}}” is too large.',
|
|
5
|
+
'BRAND_BOOK.DERIVE_THEME.SKIP_EMPTY': '“{{name}}” is empty.',
|
|
6
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_LOAD_FAILED_ONLY': 'Could not load the logo image — attach it as a file instead.',
|
|
7
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_LOAD_FAILED_FALLBACK': 'Could not load the logo image; deriving from the other materials.',
|
|
8
|
+
'BRAND_BOOK.DERIVE_THEME.BUILD_FROM_WEBSITE': 'Build from your website',
|
|
9
|
+
'BRAND_BOOK.DERIVE_THEME.SITE_URL_PLACEHOLDER': 'acme.com',
|
|
10
|
+
'BRAND_BOOK.DERIVE_THEME.SCANNING': 'Scanning…',
|
|
11
|
+
'BRAND_BOOK.DERIVE_THEME.SCAN_SITE': 'Scan site',
|
|
12
|
+
'BRAND_BOOK.DERIVE_THEME.SCAN_EXPLAINER': 'We read the page and its stylesheets — colours, fonts, logo and the tone of its copy. Nothing is stored until you save the brand.',
|
|
13
|
+
'BRAND_BOOK.DERIVE_THEME.READ_CHECK': '✓ Read',
|
|
14
|
+
'BRAND_BOOK.DERIVE_THEME.COLOURS_COUNT': '{{count}} colours',
|
|
15
|
+
'BRAND_BOOK.DERIVE_THEME.LOGO_TAG': 'logo',
|
|
16
|
+
'BRAND_BOOK.DERIVE_THEME.VOICE_TAG': 'voice',
|
|
17
|
+
'BRAND_BOOK.DERIVE_THEME.EDITABLE_BELOW': 'all editable below',
|
|
18
|
+
'BRAND_BOOK.DERIVE_THEME.OR': 'or',
|
|
19
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_LABEL': 'Brand voice',
|
|
20
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_HINT': 'How the brand talks. A scan fills this from the site; it also steers the palette when you derive from attached materials.',
|
|
21
|
+
'BRAND_BOOK.DERIVE_THEME.BRAND_VOICE_PLACEHOLDER': 'e.g. conversational and self-ironic, short sentences, speaks to the reader directly',
|
|
22
|
+
'BRAND_BOOK.DERIVE_THEME.REMOVE_IMAGE': 'Remove image',
|
|
23
|
+
'BRAND_BOOK.DERIVE_THEME.FILE_FALLBACK_NAME': 'file',
|
|
24
|
+
'BRAND_BOOK.DERIVE_THEME.REMOVE_SOURCE': 'Remove {{name}}',
|
|
25
|
+
'BRAND_BOOK.DERIVE_THEME.ATTACH_FILES': 'Attach files',
|
|
26
|
+
'BRAND_BOOK.DERIVE_THEME.READING': 'Reading…',
|
|
27
|
+
'BRAND_BOOK.DERIVE_THEME.DERIVE_BRAND': 'Derive brand',
|
|
28
|
+
'BRAND_BOOK.DERIVE_THEME.EXTRACT_FAILED': 'Could not read the brand: {{error}}'
|
|
29
|
+
};
|