wineater-bot 4.24.1 → 4.24.2
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/package.json +5 -1
- package/.claude/settings.local.json +0 -10
- package/.prettierignore +0 -9
- package/.prettierrc +0 -16
- package/CLAUDE.md +0 -115
- package/docs/CUSTOMIZATION.md +0 -133
- package/docs/SVELTE.md +0 -146
- package/e2e/demo.test.ts +0 -6
- package/eslint.config.js +0 -41
- package/index.html +0 -23
- package/playwright.config.ts +0 -9
- package/pnpm-workspace.yaml +0 -2
- package/src/App.svelte +0 -784
- package/src/App.svelte.test.ts +0 -13
- package/src/assets/biobio.png +0 -0
- package/src/components/CookieConsent.svelte +0 -73
- package/src/components/Footer.svelte +0 -122
- package/src/components/Header.svelte +0 -99
- package/src/components/Search.svelte +0 -177
- package/src/components/SkeletonTiles.svelte +0 -94
- package/src/components/WineBottle.svelte +0 -56
- package/src/components/WinePopup.svelte +0 -192
- package/src/components/WineTile.svelte +0 -458
- package/src/components/icons/Budget.svelte +0 -35
- package/src/components/icons/Diamond.svelte +0 -35
- package/src/components/icons/Fish.svelte +0 -56
- package/src/components/icons/Heart.svelte +0 -28
- package/src/components/icons/Meat.svelte +0 -32
- package/src/components/ui/Action.svelte +0 -83
- package/src/demo.spec.ts +0 -13
- package/src/index.js +0 -7
- package/src/services/analytics/analytics.ts +0 -142
- package/src/services/analytics/constants.ts +0 -18
- package/src/services/api/api.ts +0 -298
- package/src/services/api/deserialize.ts +0 -120
- package/src/services/bottle/bottle.spec.ts +0 -32
- package/src/services/bottle/bottle.ts +0 -78
- package/src/services/cache.ts +0 -28
- package/src/services/i18n/i18n.ts +0 -146
- package/src/services/i18n/translations.json +0 -658
- package/src/services/profile/consent.svelte.ts +0 -115
- package/src/services/profile/events.ts +0 -73
- package/src/services/profile/uid.ts +0 -87
- package/src/services/shopify/cart.ts +0 -105
- package/src/services/shopify/shopify.spec.ts +0 -66
- package/src/services/shopify/shopify.ts +0 -150
- package/src/services/store/app-store.svelte.ts +0 -172
- package/src/services/store/deserialize.ts +0 -25
- package/src/services/store/types.ts +0 -12
- package/src/services/ui-config/get-config.ts +0 -25
- package/src/services/ui-config/projects-config.ts +0 -156
- package/src/services/ui-config/types.ts +0 -15
- package/src/services/ui-config/utils.ts +0 -40
- package/tsconfig.json +0 -23
- package/vite.config.js +0 -84
- package/vite.config.js.timestamp-1772121492941-e00587396dc48.mjs +0 -71
- package/vitest-setup-client.ts +0 -2
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { wineaterApi, cacheScope } from "../api/api";
|
|
2
|
-
import { readCache, writeCache } from "../cache";
|
|
3
|
-
import type { CurrencyCode, LanguageCode } from "../i18n/i18n";
|
|
4
|
-
import type { StoreConfig } from "./types";
|
|
5
|
-
import { deserializeStoreConfig } from "./deserialize";
|
|
6
|
-
import type { ProjectUiConfig } from "../ui-config/types";
|
|
7
|
-
import { getProjectUiConfig } from "../ui-config/get-config";
|
|
8
|
-
import { setConsentGate, setStoreConsentPolicy } from "../profile/consent.svelte";
|
|
9
|
-
import translationsJson from "../i18n/translations.json";
|
|
10
|
-
import { loadShopifyPrivacyApi, pickLanguage, shopifyAnalyticsAllowed, shopifyContext, type ShopifyContext } from "../shopify/shopify";
|
|
11
|
-
|
|
12
|
-
export type AppState = {
|
|
13
|
-
storeConfig: StoreConfig | null;
|
|
14
|
-
currentLanguage: LanguageCode;
|
|
15
|
-
isConfigLoaded: boolean;
|
|
16
|
-
uiConfig: ProjectUiConfig;
|
|
17
|
-
/** Shopify mode: false once the shop has used up its monthly searches */
|
|
18
|
-
searchAvailable: boolean;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// Shopify mode: the theme's own font and the accent color from the theme editor, no web fonts
|
|
22
|
-
function shopifyUiConfig(sf: ShopifyContext): ProjectUiConfig {
|
|
23
|
-
const color = sf.color || '#7B2BEB';
|
|
24
|
-
return { ...getProjectUiConfig(''), brandColor: color, wineaterColor: color, customCss: '' };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const STORE_CONFIG_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
28
|
-
|
|
29
|
-
let state = $state<AppState>({
|
|
30
|
-
storeConfig: null,
|
|
31
|
-
currentLanguage: 'en',
|
|
32
|
-
isConfigLoaded: false,
|
|
33
|
-
uiConfig: null,
|
|
34
|
-
searchAvailable: true,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
export const appStore = {
|
|
38
|
-
getLanguageFromURL(): LanguageCode | null {
|
|
39
|
-
// a Shopify storefront owns its URL and its locale
|
|
40
|
-
if (shopifyContext()) return null;
|
|
41
|
-
const params = new URLSearchParams(window.location.search);
|
|
42
|
-
const lang = params.get('lang');
|
|
43
|
-
return lang as LanguageCode | null;
|
|
44
|
-
},
|
|
45
|
-
// app language -- the one used in the UI
|
|
46
|
-
getAppLanguage(): LanguageCode {
|
|
47
|
-
return state?.currentLanguage ?? 'en';
|
|
48
|
-
},
|
|
49
|
-
// store default language from API
|
|
50
|
-
getStoreLanguage(): LanguageCode | null {
|
|
51
|
-
return state.storeConfig?.lang ?? null;
|
|
52
|
-
},
|
|
53
|
-
getApiKey(): string | null {
|
|
54
|
-
return state.storeConfig?.open_ai_key ?? null;
|
|
55
|
-
},
|
|
56
|
-
getStoreId(): string | null {
|
|
57
|
-
return state.storeConfig?.id ?? null;
|
|
58
|
-
},
|
|
59
|
-
setStoreLanguage(lang: LanguageCode) {
|
|
60
|
-
if (state.storeConfig && state.storeConfig.available_languages.includes(lang)) {
|
|
61
|
-
state.currentLanguage = lang;
|
|
62
|
-
if (shopifyContext()) {
|
|
63
|
-
wineaterApi.setLanguage(lang);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const params = new URLSearchParams(window.location.search);
|
|
67
|
-
params.set('lang', lang);
|
|
68
|
-
const newUrl = `${window.location.pathname}?${params.toString()}`;
|
|
69
|
-
window.history.replaceState({}, '', newUrl);
|
|
70
|
-
} else {
|
|
71
|
-
throw new Error(`Language ${lang} is not available in store configuration.`);
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
getSupportedLanguages(): LanguageCode[] {
|
|
75
|
-
return state.storeConfig?.available_languages ?? ['en'];
|
|
76
|
-
},
|
|
77
|
-
getConsentRequired(): boolean {
|
|
78
|
-
return state.storeConfig?.requires_cookie_consent === true;
|
|
79
|
-
},
|
|
80
|
-
getCurrency(): CurrencyCode {
|
|
81
|
-
return state.storeConfig?.currency ?? 'USD';
|
|
82
|
-
},
|
|
83
|
-
applyStoreConfig(config: unknown) {
|
|
84
|
-
state.storeConfig = deserializeStoreConfig(config as Parameters<typeof deserializeStoreConfig>[0]);
|
|
85
|
-
state.currentLanguage = state.storeConfig.lang as LanguageCode;
|
|
86
|
-
const urlLang = this.getLanguageFromURL();
|
|
87
|
-
if (urlLang && state.storeConfig.available_languages.includes(urlLang)) {
|
|
88
|
-
this.setStoreLanguage(urlLang);
|
|
89
|
-
} else {
|
|
90
|
-
// fallback to store default language
|
|
91
|
-
this.setStoreLanguage(state.storeConfig.lang as LanguageCode);
|
|
92
|
-
}
|
|
93
|
-
const sf = shopifyContext();
|
|
94
|
-
this.setUiConfig(
|
|
95
|
-
sf
|
|
96
|
-
? { ...shopifyUiConfig(sf), titleText: state.storeConfig.title || null }
|
|
97
|
-
: getProjectUiConfig(state.storeConfig.id)
|
|
98
|
-
);
|
|
99
|
-
setStoreConsentPolicy(state.storeConfig.requires_cookie_consent);
|
|
100
|
-
state.isConfigLoaded = true;
|
|
101
|
-
},
|
|
102
|
-
// Stale-while-revalidate: a cached config is applied synchronously (first
|
|
103
|
-
// paint without waiting for the network) and refreshed in the background
|
|
104
|
-
// for the next visit. Without a cache we wait for the network as before.
|
|
105
|
-
async initializeStore() {
|
|
106
|
-
const sf = shopifyContext();
|
|
107
|
-
if (sf) return this.initializeShopify(sf);
|
|
108
|
-
// :v2 — конфиг получил requires_cookie_consent; старый кэш (24 ч) его не знает
|
|
109
|
-
const cacheKey = `${cacheScope}:store-config:v2`;
|
|
110
|
-
const fetchAndCache = async () => {
|
|
111
|
-
const config = await wineaterApi.getStoreConfig();
|
|
112
|
-
writeCache(cacheKey, config);
|
|
113
|
-
return config;
|
|
114
|
-
};
|
|
115
|
-
try {
|
|
116
|
-
const cached = readCache<unknown>(cacheKey, STORE_CONFIG_MAX_AGE_MS);
|
|
117
|
-
if (cached) {
|
|
118
|
-
try {
|
|
119
|
-
this.applyStoreConfig(cached);
|
|
120
|
-
fetchAndCache().catch(() => {});
|
|
121
|
-
return;
|
|
122
|
-
} catch (error) {
|
|
123
|
-
console.warn('Ignoring invalid cached store config:', error);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
this.applyStoreConfig(await fetchAndCache());
|
|
127
|
-
} catch (error) {
|
|
128
|
-
console.error('Failed to initialize store:', error);
|
|
129
|
-
state.isConfigLoaded = false;
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
// Shopify mode: the embed passes the public config (app-data metafield), so the first paint
|
|
133
|
-
// needs no request; without it we ask /shopify/storefront/config once.
|
|
134
|
-
async initializeShopify(sf: ShopifyContext) {
|
|
135
|
-
setConsentGate(shopifyAnalyticsAllowed);
|
|
136
|
-
loadShopifyPrivacyApi();
|
|
137
|
-
try {
|
|
138
|
-
const cfg = sf.config?.lang ? sf.config : await wineaterApi.getShopifyConfig();
|
|
139
|
-
const supported = Object.keys(translationsJson);
|
|
140
|
-
const lang = pickLanguage(sf.locale, cfg?.lang, supported);
|
|
141
|
-
this.applyStoreConfig({
|
|
142
|
-
id: `shopify:${sf.shop}`,
|
|
143
|
-
type: cfg?.widget_type ?? 'store',
|
|
144
|
-
lang,
|
|
145
|
-
available_languages: [lang],
|
|
146
|
-
open_ai_key: null,
|
|
147
|
-
currency: sf.currency || cfg?.currency || 'USD',
|
|
148
|
-
title: cfg?.title ?? '',
|
|
149
|
-
requires_cookie_consent: true,
|
|
150
|
-
});
|
|
151
|
-
state.searchAvailable = cfg?.search_available !== false;
|
|
152
|
-
} catch (error) {
|
|
153
|
-
console.error('Failed to initialize store:', error);
|
|
154
|
-
state.isConfigLoaded = false;
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
isSearchAvailable(): boolean {
|
|
158
|
-
return state.searchAvailable;
|
|
159
|
-
},
|
|
160
|
-
setSearchAvailable(available: boolean) {
|
|
161
|
-
state.searchAvailable = available;
|
|
162
|
-
},
|
|
163
|
-
isInitialized(): boolean {
|
|
164
|
-
return state.isConfigLoaded;
|
|
165
|
-
},
|
|
166
|
-
getUiConfig(): ProjectUiConfig {
|
|
167
|
-
return state.uiConfig;
|
|
168
|
-
},
|
|
169
|
-
setUiConfig(uiConfig: ProjectUiConfig) {
|
|
170
|
-
state.uiConfig = uiConfig;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { StoreConfig } from "./types";
|
|
2
|
-
import type { LanguageCode, CurrencyCode } from "../i18n/i18n";
|
|
3
|
-
|
|
4
|
-
type RawStoreConfig = {
|
|
5
|
-
id: string;
|
|
6
|
-
type: string | null;
|
|
7
|
-
lang: LanguageCode;
|
|
8
|
-
available_languages: LanguageCode[] | null;
|
|
9
|
-
open_ai_key: string | null;
|
|
10
|
-
currency: CurrencyCode;
|
|
11
|
-
title: string;
|
|
12
|
-
requires_cookie_consent?: boolean | null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function deserializeStoreConfig(data: RawStoreConfig): StoreConfig {
|
|
16
|
-
return {
|
|
17
|
-
id: String(data.id),
|
|
18
|
-
lang: data.lang,
|
|
19
|
-
available_languages: data.available_languages ? data.available_languages : [data.lang],
|
|
20
|
-
open_ai_key: data.open_ai_key ?? null,
|
|
21
|
-
currency: data.currency,
|
|
22
|
-
title: data.title,
|
|
23
|
-
requires_cookie_consent: data.requires_cookie_consent === true,
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { CurrencyCode, LanguageCode } from "../i18n/i18n";
|
|
2
|
-
|
|
3
|
-
export type StoreConfig = {
|
|
4
|
-
id: string;
|
|
5
|
-
lang: LanguageCode;
|
|
6
|
-
available_languages: LanguageCode[];
|
|
7
|
-
open_ai_key: string | null;
|
|
8
|
-
currency: CurrencyCode;
|
|
9
|
-
title: string;
|
|
10
|
-
/** EU shop: no tracking/identifiers until the visitor accepts cookies. */
|
|
11
|
-
requires_cookie_consent: boolean;
|
|
12
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { projectConfigs } from "./projects-config";
|
|
2
|
-
import { UiConfig } from "./types";
|
|
3
|
-
|
|
4
|
-
export function getProjectUiConfig(projectId: string): UiConfig {
|
|
5
|
-
const defaultConfig = {
|
|
6
|
-
logoUrl: null,
|
|
7
|
-
// Brand color is used for shop branding
|
|
8
|
-
brandColor: "#7B2BEB",
|
|
9
|
-
// Wineater color is brand color of Wineater
|
|
10
|
-
// It should be changed if client wants to remove Wineater branding
|
|
11
|
-
wineaterColor: "#7B2BEB",
|
|
12
|
-
customCss: `
|
|
13
|
-
@import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&display=swap');
|
|
14
|
-
:root { --wineater-font-primary: 'Bricolage Grotesque', serif; }
|
|
15
|
-
`
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const projectConfig = projectConfigs[projectId] || {};
|
|
19
|
-
|
|
20
|
-
// Merge default config with project-specific config if available
|
|
21
|
-
return {
|
|
22
|
-
...defaultConfig,
|
|
23
|
-
...projectConfig,
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import type { ProjectUiConfig } from "./types";
|
|
2
|
-
|
|
3
|
-
export const projectConfigs: Record<string, ProjectUiConfig> = {
|
|
4
|
-
"0": {
|
|
5
|
-
logoUrl: null,
|
|
6
|
-
brandColor: "#7B2BEB",
|
|
7
|
-
},
|
|
8
|
-
"29": {
|
|
9
|
-
logoUrl: "https://czvgkhagwvmknscoerfy.supabase.co/storage/v1/object/public/static-media/plou_logo.jpg",
|
|
10
|
-
brandColor: "#e6c6bb",
|
|
11
|
-
},
|
|
12
|
-
"25": {
|
|
13
|
-
logoUrl: null,
|
|
14
|
-
brandColor: "#000000",
|
|
15
|
-
wineaterColor: "#000000",
|
|
16
|
-
buttonUppercase: true,
|
|
17
|
-
customCss: `
|
|
18
|
-
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;600;700&display=swap');
|
|
19
|
-
:root { --wineater-font-primary: 'Roboto', sans-serif; }
|
|
20
|
-
#wr-Custom {
|
|
21
|
-
.wr-Header-title {
|
|
22
|
-
font-family: 'Roboto', sans-serif;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.wr-Search-input {
|
|
26
|
-
border-color: #7c7c7c;
|
|
27
|
-
color: #7c7c7c;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.wr-Search-icon {
|
|
31
|
-
filter: brightness(0) saturate(100%);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.wr-Search-button:hover {
|
|
35
|
-
background: #E3BE8D;
|
|
36
|
-
opacity: 1;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.wr-WineTile-params {
|
|
40
|
-
color: #7c7c7c;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.wr-WineTilePopup-reason {
|
|
44
|
-
color: #000000;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.wr-WineTilePopup-tag {
|
|
48
|
-
color: #000000;
|
|
49
|
-
background: rgba(239,239,239,0.7);
|
|
50
|
-
border-color: transparent;
|
|
51
|
-
text-transform: uppercase;
|
|
52
|
-
font-weight: 600;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.wr-WineTile-action {
|
|
56
|
-
background: transparent;
|
|
57
|
-
font-weight: 700;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.wr-WineTile-action:hover {
|
|
61
|
-
background: #000000;
|
|
62
|
-
color: #ffffff;
|
|
63
|
-
box-shadow: none;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
`,
|
|
67
|
-
translations: {
|
|
68
|
-
fr: {
|
|
69
|
-
"wineDetails.buy": "voir plus"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
"21": {
|
|
74
|
-
logoUrl: "https://www.intermarche.com/images/logoIntermarche.svg",
|
|
75
|
-
brandColor: "#B81242",
|
|
76
|
-
wineaterColor: "#B81242",
|
|
77
|
-
buttonUppercase: true,
|
|
78
|
-
customCss: `
|
|
79
|
-
#wr-Custom {
|
|
80
|
-
.wr-Search-icon {
|
|
81
|
-
background-image: none;
|
|
82
|
-
-webkit-mask-image: url('https://czvgkhagwvmknscoerfy.supabase.co/storage/v1/object/public/static-media/search.svg?x=1');
|
|
83
|
-
mask-image: url('https://czvgkhagwvmknscoerfy.supabase.co/storage/v1/object/public/static-media/search.svg?x=1');
|
|
84
|
-
-webkit-mask-repeat: no-repeat;
|
|
85
|
-
mask-repeat: no-repeat;
|
|
86
|
-
-webkit-mask-size: contain;
|
|
87
|
-
mask-size: contain;
|
|
88
|
-
background-color: #B81242;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
`,
|
|
92
|
-
translations: {}
|
|
93
|
-
},
|
|
94
|
-
"26": {
|
|
95
|
-
logoUrl: "https://paladarlondon.com/wp-content/uploads/2021/05/cropped-cropped-Paladar-Logo-1473x464-min-768x219.png",
|
|
96
|
-
brandColor: "#B81242",
|
|
97
|
-
wineaterColor: "#7B2BEB",
|
|
98
|
-
buttonUppercase: true,
|
|
99
|
-
customCss: "",
|
|
100
|
-
translations: {}
|
|
101
|
-
},
|
|
102
|
-
"28": {
|
|
103
|
-
logoUrl: "https://czvgkhagwvmknscoerfy.supabase.co/storage/v1/object/public/static-media/ryba_moya.png",
|
|
104
|
-
brandColor: "#de4e2a",
|
|
105
|
-
wineaterColor: "#de4e2a",
|
|
106
|
-
buttonUppercase: true,
|
|
107
|
-
customCss: `
|
|
108
|
-
#wr-Custom {
|
|
109
|
-
.wr-Search-icon {
|
|
110
|
-
background-image: none;
|
|
111
|
-
-webkit-mask-image: url('https://czvgkhagwvmknscoerfy.supabase.co/storage/v1/object/public/static-media/search.svg?x=1');
|
|
112
|
-
mask-image: url('https://czvgkhagwvmknscoerfy.supabase.co/storage/v1/object/public/static-media/search.svg?x=1');
|
|
113
|
-
-webkit-mask-repeat: no-repeat;
|
|
114
|
-
mask-repeat: no-repeat;
|
|
115
|
-
-webkit-mask-size: contain;
|
|
116
|
-
mask-size: contain;
|
|
117
|
-
background-color: #de4e2a;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
`,
|
|
121
|
-
translations: {}
|
|
122
|
-
},
|
|
123
|
-
"30": {
|
|
124
|
-
logoUrl: null,
|
|
125
|
-
brandColor: "#8bae98",
|
|
126
|
-
wineaterColor: "#8bae98",
|
|
127
|
-
titleText: "Looking for the perfect bottle? Ask our AI Wine Geek for personalised recommendations, whatever the meal, mood or occasion.",
|
|
128
|
-
customCss: `
|
|
129
|
-
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500&display=swap');
|
|
130
|
-
:root { --wineater-font-primary: 'Montserrat', Verdana, Helvetica, Arial, sans-serif; }
|
|
131
|
-
#wr-Custom {
|
|
132
|
-
font-weight: 400;
|
|
133
|
-
|
|
134
|
-
.wr-Header-title {
|
|
135
|
-
font-family: 'Montserrat', Verdana, Helvetica, Arial, sans-serif;
|
|
136
|
-
font-weight: 500;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.wr-Footer {
|
|
140
|
-
display: none;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
.wr-Search-icon {
|
|
144
|
-
background-image: none;
|
|
145
|
-
-webkit-mask-image: url('https://czvgkhagwvmknscoerfy.supabase.co/storage/v1/object/public/static-media/search.svg?x=1');
|
|
146
|
-
mask-image: url('https://czvgkhagwvmknscoerfy.supabase.co/storage/v1/object/public/static-media/search.svg?x=1');
|
|
147
|
-
-webkit-mask-repeat: no-repeat;
|
|
148
|
-
mask-repeat: no-repeat;
|
|
149
|
-
-webkit-mask-size: contain;
|
|
150
|
-
mask-size: contain;
|
|
151
|
-
background-color: #8bae98;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
`
|
|
155
|
-
}
|
|
156
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { LanguageCode } from "../i18n/i18n";
|
|
2
|
-
|
|
3
|
-
export type ProjectId = string;
|
|
4
|
-
export type ProjectUiConfig = {
|
|
5
|
-
logoUrl: string | null;
|
|
6
|
-
brandColor: string;
|
|
7
|
-
wineaterColor?: string;
|
|
8
|
-
customCss?: string | null;
|
|
9
|
-
buttonUppercase?: boolean;
|
|
10
|
-
titleText?: string | null;
|
|
11
|
-
translations?: Partial<Record<LanguageCode, Record<string, string>>>;
|
|
12
|
-
}
|
|
13
|
-
export type UiConfig = ProjectUiConfig & {
|
|
14
|
-
wineaterColor: string;
|
|
15
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const IMPORT_RE = /@import\s+url\(\s*['"]?([^'")]+)['"]?\s*\)\s*;?/g;
|
|
2
|
-
|
|
3
|
-
function addLink(rel: string, href: string, crossOrigin = false) {
|
|
4
|
-
const link = document.createElement('link');
|
|
5
|
-
link.rel = rel;
|
|
6
|
-
link.href = href;
|
|
7
|
-
if (crossOrigin) link.crossOrigin = 'anonymous';
|
|
8
|
-
document.head.appendChild(link);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Opens the connections to the font hosts before the config tells us which
|
|
12
|
-
// font the project uses, so the stylesheet and font file start downloading
|
|
13
|
-
// without a fresh DNS/TLS handshake.
|
|
14
|
-
export function preconnectFontHosts() {
|
|
15
|
-
if (document.querySelector('link[rel="preconnect"][href="https://fonts.googleapis.com"]')) return;
|
|
16
|
-
addLink('preconnect', 'https://fonts.googleapis.com');
|
|
17
|
-
addLink('preconnect', 'https://fonts.gstatic.com', true);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function generateInlineStyle(cssString: string | null): string {
|
|
21
|
-
if (!cssString) {
|
|
22
|
-
return '';
|
|
23
|
-
}
|
|
24
|
-
const styleId = "custom-ui-config-style";
|
|
25
|
-
if (document.getElementById(styleId)) return;
|
|
26
|
-
|
|
27
|
-
// `@import` inside <style> is only discovered after that stylesheet is
|
|
28
|
-
// parsed, which chains the font download behind it. A <link> is fetched in
|
|
29
|
-
// parallel with the rest of the page.
|
|
30
|
-
const css = cssString.replace(IMPORT_RE, (_match, href: string) => {
|
|
31
|
-
addLink('stylesheet', href);
|
|
32
|
-
return '';
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const style = document.createElement('style');
|
|
36
|
-
style.id = styleId;
|
|
37
|
-
style.innerHTML = css;
|
|
38
|
-
document.head.appendChild(style);
|
|
39
|
-
return styleId;
|
|
40
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"module": "ESNext",
|
|
4
|
-
"target": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"allowImportingTsExtensions": true,
|
|
7
|
-
"allowArbitraryExtensions": true,
|
|
8
|
-
"resolveJsonModule": true,
|
|
9
|
-
"allowSyntheticDefaultImports": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"strict": false,
|
|
13
|
-
"forceConsistentCasingInFileNames": true,
|
|
14
|
-
"isolatedModules": true,
|
|
15
|
-
"noEmit": true
|
|
16
|
-
},
|
|
17
|
-
"include": [
|
|
18
|
-
"src/**/*"
|
|
19
|
-
],
|
|
20
|
-
"exclude": [
|
|
21
|
-
"node_modules"
|
|
22
|
-
]
|
|
23
|
-
}
|
package/vite.config.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import devtoolsJson from 'vite-plugin-devtools-json';
|
|
2
|
-
import { defineConfig } from 'vitest/config';
|
|
3
|
-
import { transformWithEsbuild } from 'vite';
|
|
4
|
-
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
5
|
-
|
|
6
|
-
// Vite skips whitespace minification for `es` library output (to keep tree-shaking
|
|
7
|
-
// annotations for downstream bundlers). The widget is a final artifact loaded by a
|
|
8
|
-
// <script> tag, so minify it ourselves: the unminified ESM bundle was ~40% larger.
|
|
9
|
-
const minifyEsmBundle = {
|
|
10
|
-
name: 'minify-esm-bundle',
|
|
11
|
-
apply: 'build',
|
|
12
|
-
// generateBundle runs after every renderChunk, including Vite's own esbuild
|
|
13
|
-
// step that re-prints the code with indentation for `es` library builds
|
|
14
|
-
async generateBundle(options, bundle) {
|
|
15
|
-
if (options.format !== 'es') return;
|
|
16
|
-
for (const chunk of Object.values(bundle)) {
|
|
17
|
-
if (chunk.type !== 'chunk') continue;
|
|
18
|
-
const result = await transformWithEsbuild(chunk.code, chunk.fileName, { minify: true, target: 'es2020' });
|
|
19
|
-
chunk.code = result.code;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export default defineConfig({
|
|
25
|
-
server: {
|
|
26
|
-
port: 3001 // Customize the port if necessary
|
|
27
|
-
},
|
|
28
|
-
build: {
|
|
29
|
-
lib: {
|
|
30
|
-
entry: 'src/index.js',
|
|
31
|
-
name: 'WineaterChatbot',
|
|
32
|
-
fileName: () => 'wineater-chatbot' // base name only
|
|
33
|
-
},
|
|
34
|
-
rollupOptions: {
|
|
35
|
-
output: [
|
|
36
|
-
{
|
|
37
|
-
format: 'es',
|
|
38
|
-
entryFileNames: 'wineater-chatbot.mjs'
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
format: 'umd',
|
|
42
|
-
name: 'WineaterChatbot',
|
|
43
|
-
entryFileNames: 'wineater-chatbot.umd.js'
|
|
44
|
-
}
|
|
45
|
-
],
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
test: {
|
|
49
|
-
expect: { requireAssertions: true },
|
|
50
|
-
projects: [
|
|
51
|
-
{
|
|
52
|
-
extends: './vite.config.js',
|
|
53
|
-
test: {
|
|
54
|
-
name: 'client',
|
|
55
|
-
environment: 'browser',
|
|
56
|
-
browser: {
|
|
57
|
-
enabled: true,
|
|
58
|
-
provider: 'playwright',
|
|
59
|
-
instances: [{ browser: 'chromium' }]
|
|
60
|
-
},
|
|
61
|
-
include: ['src/**/*.svelte.{test,spec}.{js,ts}'],
|
|
62
|
-
exclude: ['src/lib/server/**'],
|
|
63
|
-
setupFiles: ['./vitest-setup-client.ts']
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
extends: './vite.config.js',
|
|
68
|
-
test: {
|
|
69
|
-
name: 'server',
|
|
70
|
-
environment: 'node',
|
|
71
|
-
include: ['src/**/*.{test,spec}.{js,ts}'],
|
|
72
|
-
exclude: ['src/**/*.svelte.{test,spec}.{js,ts}']
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
]
|
|
76
|
-
},
|
|
77
|
-
plugins: [
|
|
78
|
-
devtoolsJson(),
|
|
79
|
-
minifyEsmBundle,
|
|
80
|
-
svelte({
|
|
81
|
-
emitCss: false
|
|
82
|
-
})
|
|
83
|
-
]
|
|
84
|
-
});
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
// vite.config.js
|
|
2
|
-
import devtoolsJson from "file:///Users/alekseiolkhovoi/Desktop/dev/Wineater/wineater-bot/node_modules/vite-plugin-devtools-json/dist/index.mjs";
|
|
3
|
-
import { defineConfig } from "file:///Users/alekseiolkhovoi/Desktop/dev/Wineater/wineater-bot/node_modules/vitest/dist/config.js";
|
|
4
|
-
import { svelte } from "file:///Users/alekseiolkhovoi/Desktop/dev/Wineater/wineater-bot/node_modules/@sveltejs/vite-plugin-svelte/src/index.js";
|
|
5
|
-
var vite_config_default = defineConfig({
|
|
6
|
-
server: {
|
|
7
|
-
port: 3001
|
|
8
|
-
// Customize the port if necessary
|
|
9
|
-
},
|
|
10
|
-
build: {
|
|
11
|
-
lib: {
|
|
12
|
-
entry: "src/index.js",
|
|
13
|
-
name: "WineaterChatbot",
|
|
14
|
-
fileName: () => "wineater-chatbot"
|
|
15
|
-
// base name only
|
|
16
|
-
},
|
|
17
|
-
rollupOptions: {
|
|
18
|
-
output: [
|
|
19
|
-
{
|
|
20
|
-
format: "es",
|
|
21
|
-
entryFileNames: "wineater-chatbot.mjs"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
format: "umd",
|
|
25
|
-
name: "WineaterChatbot",
|
|
26
|
-
entryFileNames: "wineater-chatbot.umd.js"
|
|
27
|
-
}
|
|
28
|
-
]
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
test: {
|
|
32
|
-
expect: { requireAssertions: true },
|
|
33
|
-
projects: [
|
|
34
|
-
{
|
|
35
|
-
extends: "./vite.config.js",
|
|
36
|
-
test: {
|
|
37
|
-
name: "client",
|
|
38
|
-
environment: "browser",
|
|
39
|
-
browser: {
|
|
40
|
-
enabled: true,
|
|
41
|
-
provider: "playwright",
|
|
42
|
-
instances: [{ browser: "chromium" }]
|
|
43
|
-
},
|
|
44
|
-
include: ["src/**/*.svelte.{test,spec}.{js,ts}"],
|
|
45
|
-
exclude: ["src/lib/server/**"],
|
|
46
|
-
setupFiles: ["./vitest-setup-client.ts"]
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
extends: "./vite.config.js",
|
|
51
|
-
test: {
|
|
52
|
-
name: "server",
|
|
53
|
-
environment: "node",
|
|
54
|
-
include: ["src/**/*.{test,spec}.{js,ts}"],
|
|
55
|
-
exclude: ["src/**/*.svelte.{test,spec}.{js,ts}"]
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
]
|
|
59
|
-
},
|
|
60
|
-
plugins: [
|
|
61
|
-
devtoolsJson(),
|
|
62
|
-
svelte({
|
|
63
|
-
compilerOptions: { hydratable: true },
|
|
64
|
-
emitCss: false
|
|
65
|
-
})
|
|
66
|
-
]
|
|
67
|
-
});
|
|
68
|
-
export {
|
|
69
|
-
vite_config_default as default
|
|
70
|
-
};
|
|
71
|
-
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvYWxla3NlaW9sa2hvdm9pL0Rlc2t0b3AvZGV2L1dpbmVhdGVyL3dpbmVhdGVyLWJvdFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL2FsZWtzZWlvbGtob3ZvaS9EZXNrdG9wL2Rldi9XaW5lYXRlci93aW5lYXRlci1ib3Qvdml0ZS5jb25maWcuanNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL1VzZXJzL2FsZWtzZWlvbGtob3ZvaS9EZXNrdG9wL2Rldi9XaW5lYXRlci93aW5lYXRlci1ib3Qvdml0ZS5jb25maWcuanNcIjtpbXBvcnQgZGV2dG9vbHNKc29uIGZyb20gJ3ZpdGUtcGx1Z2luLWRldnRvb2xzLWpzb24nO1xuaW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZXN0L2NvbmZpZyc7XG5pbXBvcnQgeyBzdmVsdGUgfSBmcm9tICdAc3ZlbHRlanMvdml0ZS1wbHVnaW4tc3ZlbHRlJztcblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcblx0c2VydmVyOiB7XG5cdFx0cG9ydDogMzAwMSAvLyBDdXN0b21pemUgdGhlIHBvcnQgaWYgbmVjZXNzYXJ5XG5cdH0sXG5cdGJ1aWxkOiB7XG5cdFx0bGliOiB7XG5cdFx0XHRlbnRyeTogJ3NyYy9pbmRleC5qcycsXG5cdFx0XHRuYW1lOiAnV2luZWF0ZXJDaGF0Ym90Jyxcblx0XHRcdGZpbGVOYW1lOiAoKSA9PiAnd2luZWF0ZXItY2hhdGJvdCcgIC8vIGJhc2UgbmFtZSBvbmx5XG5cdFx0fSxcblx0XHRyb2xsdXBPcHRpb25zOiB7XG5cdFx0XHRvdXRwdXQ6IFtcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGZvcm1hdDogJ2VzJyxcblx0XHRcdFx0XHRlbnRyeUZpbGVOYW1lczogJ3dpbmVhdGVyLWNoYXRib3QubWpzJ1xuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0Zm9ybWF0OiAndW1kJyxcblx0XHRcdFx0XHRuYW1lOiAnV2luZWF0ZXJDaGF0Ym90Jyxcblx0XHRcdFx0XHRlbnRyeUZpbGVOYW1lczogJ3dpbmVhdGVyLWNoYXRib3QudW1kLmpzJ1xuXHRcdFx0XHR9XG5cdFx0XHRdLFxuXHRcdH0sXG5cdH0sXG5cdHRlc3Q6IHtcblx0XHRleHBlY3Q6IHsgcmVxdWlyZUFzc2VydGlvbnM6IHRydWUgfSxcblx0XHRwcm9qZWN0czogW1xuXHRcdFx0e1xuXHRcdFx0XHRleHRlbmRzOiAnLi92aXRlLmNvbmZpZy5qcycsXG5cdFx0XHRcdHRlc3Q6IHtcblx0XHRcdFx0XHRuYW1lOiAnY2xpZW50Jyxcblx0XHRcdFx0XHRlbnZpcm9ubWVudDogJ2Jyb3dzZXInLFxuXHRcdFx0XHRcdGJyb3dzZXI6IHtcblx0XHRcdFx0XHRcdGVuYWJsZWQ6IHRydWUsXG5cdFx0XHRcdFx0XHRwcm92aWRlcjogJ3BsYXl3cmlnaHQnLFxuXHRcdFx0XHRcdFx0aW5zdGFuY2VzOiBbeyBicm93c2VyOiAnY2hyb21pdW0nIH1dXG5cdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRpbmNsdWRlOiBbJ3NyYy8qKi8qLnN2ZWx0ZS57dGVzdCxzcGVjfS57anMsdHN9J10sXG5cdFx0XHRcdFx0ZXhjbHVkZTogWydzcmMvbGliL3NlcnZlci8qKiddLFxuXHRcdFx0XHRcdHNldHVwRmlsZXM6IFsnLi92aXRlc3Qtc2V0dXAtY2xpZW50LnRzJ11cblx0XHRcdFx0fVxuXHRcdFx0fSxcblx0XHRcdHtcblx0XHRcdFx0ZXh0ZW5kczogJy4vdml0ZS5jb25maWcuanMnLFxuXHRcdFx0XHR0ZXN0OiB7XG5cdFx0XHRcdFx0bmFtZTogJ3NlcnZlcicsXG5cdFx0XHRcdFx0ZW52aXJvbm1lbnQ6ICdub2RlJyxcblx0XHRcdFx0XHRpbmNsdWRlOiBbJ3NyYy8qKi8qLnt0ZXN0LHNwZWN9Lntqcyx0c30nXSxcblx0XHRcdFx0XHRleGNsdWRlOiBbJ3NyYy8qKi8qLnN2ZWx0ZS57dGVzdCxzcGVjfS57anMsdHN9J11cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdF1cblx0fSxcblx0cGx1Z2luczogW1xuXHRcdGRldnRvb2xzSnNvbigpLFxuXHRcdHN2ZWx0ZSh7XG5cdFx0XHRjb21waWxlck9wdGlvbnM6IHsgaHlkcmF0YWJsZTogdHJ1ZSB9LFxuXHRcdFx0ZW1pdENzczogZmFsc2Vcblx0XHR9KVxuXHRdXG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBMFYsT0FBTyxrQkFBa0I7QUFDblgsU0FBUyxvQkFBb0I7QUFDN0IsU0FBUyxjQUFjO0FBRXZCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzNCLFFBQVE7QUFBQSxJQUNQLE1BQU07QUFBQTtBQUFBLEVBQ1A7QUFBQSxFQUNBLE9BQU87QUFBQSxJQUNOLEtBQUs7QUFBQSxNQUNKLE9BQU87QUFBQSxNQUNQLE1BQU07QUFBQSxNQUNOLFVBQVUsTUFBTTtBQUFBO0FBQUEsSUFDakI7QUFBQSxJQUNBLGVBQWU7QUFBQSxNQUNkLFFBQVE7QUFBQSxRQUNQO0FBQUEsVUFDQyxRQUFRO0FBQUEsVUFDUixnQkFBZ0I7QUFBQSxRQUNqQjtBQUFBLFFBQ0E7QUFBQSxVQUNDLFFBQVE7QUFBQSxVQUNSLE1BQU07QUFBQSxVQUNOLGdCQUFnQjtBQUFBLFFBQ2pCO0FBQUEsTUFDRDtBQUFBLElBQ0Q7QUFBQSxFQUNEO0FBQUEsRUFDQSxNQUFNO0FBQUEsSUFDTCxRQUFRLEVBQUUsbUJBQW1CLEtBQUs7QUFBQSxJQUNsQyxVQUFVO0FBQUEsTUFDVDtBQUFBLFFBQ0MsU0FBUztBQUFBLFFBQ1QsTUFBTTtBQUFBLFVBQ0wsTUFBTTtBQUFBLFVBQ04sYUFBYTtBQUFBLFVBQ2IsU0FBUztBQUFBLFlBQ1IsU0FBUztBQUFBLFlBQ1QsVUFBVTtBQUFBLFlBQ1YsV0FBVyxDQUFDLEVBQUUsU0FBUyxXQUFXLENBQUM7QUFBQSxVQUNwQztBQUFBLFVBQ0EsU0FBUyxDQUFDLHFDQUFxQztBQUFBLFVBQy9DLFNBQVMsQ0FBQyxtQkFBbUI7QUFBQSxVQUM3QixZQUFZLENBQUMsMEJBQTBCO0FBQUEsUUFDeEM7QUFBQSxNQUNEO0FBQUEsTUFDQTtBQUFBLFFBQ0MsU0FBUztBQUFBLFFBQ1QsTUFBTTtBQUFBLFVBQ0wsTUFBTTtBQUFBLFVBQ04sYUFBYTtBQUFBLFVBQ2IsU0FBUyxDQUFDLDhCQUE4QjtBQUFBLFVBQ3hDLFNBQVMsQ0FBQyxxQ0FBcUM7QUFBQSxRQUNoRDtBQUFBLE1BQ0Q7QUFBQSxJQUNEO0FBQUEsRUFDRDtBQUFBLEVBQ0EsU0FBUztBQUFBLElBQ1IsYUFBYTtBQUFBLElBQ2IsT0FBTztBQUFBLE1BQ04saUJBQWlCLEVBQUUsWUFBWSxLQUFLO0FBQUEsTUFDcEMsU0FBUztBQUFBLElBQ1YsQ0FBQztBQUFBLEVBQ0Y7QUFDRCxDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=
|
package/vitest-setup-client.ts
DELETED