cdnhost 2.5.7 → 2.5.9
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 +1 -1
- package/ws_cdn.js +110 -42
- package/ws_cdnhtml.js +6 -2
- package/ws_css.css +10 -74
package/package.json
CHANGED
package/ws_cdn.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
document.addEventListener('DOMContentLoaded', function() {
|
|
3
3
|
|
|
4
|
-
const languages = [
|
|
5
|
-
{ code: 'ko', name: '한국어' }, { code: 'en', name: 'English' }, { code: 'es', name: 'Español' },
|
|
6
|
-
{ code: 'hi', name: 'हिन्दी' }, { code: 'ar', name: 'العربية' }, { code: 'de', name: 'Deutsch' },
|
|
7
|
-
{ code: 'fr', name: 'Français' }, { code: 'pt', name: 'Português' }, { code: 'bn', name: 'বাংলা' },
|
|
8
|
-
{ code: 'ja', name: '日本語' }, { code: 'ru', name: 'Русский' }, { code: 'zh', name: '简体中文' },
|
|
9
|
-
{ code: 'th', name: 'ไทย' }, { code: 'vi', name: 'Tiếng Việt' }, { code: 'id', name: 'Indonesia' },
|
|
10
|
-
{ code: 'tr', name: 'Türkçe' }, { code: 'ur', name: 'اردو' }
|
|
11
|
-
];
|
|
4
|
+
const languages = [ { code: 'ko', name: '한국어' }, { code: 'en', name: 'English' }, { code: 'es', name: 'Español' }, { code: 'hi', name: 'हिन्दी' }, { code: 'ar', name: 'العربية' }, { code: 'de', name: 'Deutsch' }, { code: 'fr', name: 'Français' }, { code: 'pt', name: 'Português' }, { code: 'bn', name: 'বাংলা' }, { code: 'ja', name: '日本語' }, { code: 'ru', name: 'Русский' }, { code: 'zh', name: '简体中文' }, { code: 'th', name: 'ไทย' }, { code: 'vi', name: 'Tiếng Việt' }, { code: 'id', name: 'Indonesia' }, { code: 'tr', name: 'Türkçe' }, { code: 'ur', name: 'اردو' }];
|
|
12
5
|
|
|
13
6
|
let fullApiItemsData = null;
|
|
14
7
|
let currentDisplayItems = [];
|
|
@@ -31,13 +24,74 @@
|
|
|
31
24
|
const languageBtn = document.getElementById('fhl-language-btn');
|
|
32
25
|
const languagePanel = document.getElementById('fhl-language-panel');
|
|
33
26
|
|
|
34
|
-
|
|
27
|
+
// ✨ [수정됨] `fhl-fixed-question` -> `fhl-fixed-image`로 되돌리고, 올바른 ID를 참조합니다.
|
|
28
|
+
const fixedImageBtn = document.getElementById('fhl-fixed-image');
|
|
35
29
|
const fixedChatBtn = document.getElementById('fhl-fixed-chat');
|
|
36
30
|
const fixedAdBtn = document.getElementById('fhl-fixed-ad');
|
|
37
31
|
|
|
38
32
|
let toastTimeout = null;
|
|
39
33
|
let currentLang = localStorage.getItem('fhl-widget-lang') || 'ko';
|
|
40
34
|
|
|
35
|
+
const shortcutManager = {
|
|
36
|
+
key: 'fhl-custom-icons-with-expiry',
|
|
37
|
+
get: () => {
|
|
38
|
+
const rawData = localStorage.getItem(shortcutManager.key);
|
|
39
|
+
if (!rawData) return [];
|
|
40
|
+
try {
|
|
41
|
+
const data = JSON.parse(rawData);
|
|
42
|
+
const thirtyDaysInMs = 30 * 24 * 60 * 60 * 1000;
|
|
43
|
+
if (Date.now() - data.timestamp > thirtyDaysInMs) {
|
|
44
|
+
localStorage.removeItem(shortcutManager.key);
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
return data.shortcuts || [];
|
|
48
|
+
} catch (e) {
|
|
49
|
+
localStorage.removeItem(shortcutManager.key);
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
set: (shortcuts) => {
|
|
54
|
+
const dataToStore = { timestamp: Date.now(), shortcuts: shortcuts };
|
|
55
|
+
localStorage.setItem(shortcutManager.key, JSON.stringify(dataToStore));
|
|
56
|
+
},
|
|
57
|
+
syncFromURL: () => {
|
|
58
|
+
const params = new URLSearchParams(window.location.search);
|
|
59
|
+
const shortcutsParam = params.get('shortcuts');
|
|
60
|
+
if (shortcutsParam) {
|
|
61
|
+
try {
|
|
62
|
+
const decodedData = atob(shortcutsParam);
|
|
63
|
+
JSON.parse(decodedData);
|
|
64
|
+
localStorage.setItem(shortcutManager.key, decodedData);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.error("Failed to parse shortcuts from URL:", e);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
getAsParam: () => {
|
|
71
|
+
const rawData = localStorage.getItem(shortcutManager.key);
|
|
72
|
+
if (!rawData) return '';
|
|
73
|
+
return `shortcuts=${btoa(rawData)}`;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const updateAllWidgetLinks = () => {
|
|
78
|
+
const paramString = shortcutManager.getAsParam();
|
|
79
|
+
if (!paramString) return;
|
|
80
|
+
const allLinks = document.querySelectorAll('.fhl-icon-display, .fhl-fixed-item');
|
|
81
|
+
allLinks.forEach(link => {
|
|
82
|
+
if (link.tagName === 'A') {
|
|
83
|
+
const originalHref = link.getAttribute('href');
|
|
84
|
+
if (!originalHref || originalHref.startsWith('#')) return;
|
|
85
|
+
try {
|
|
86
|
+
let newUrl = new URL(originalHref);
|
|
87
|
+
const [key, value] = paramString.split('=');
|
|
88
|
+
newUrl.searchParams.set(key, value);
|
|
89
|
+
link.href = newUrl.toString();
|
|
90
|
+
} catch (e) { console.error('Invalid URL for link update:', originalHref); }
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
|
|
41
95
|
const showToast = (message) => {
|
|
42
96
|
clearTimeout(toastTimeout);
|
|
43
97
|
toastElement.textContent = message;
|
|
@@ -61,7 +115,7 @@
|
|
|
61
115
|
searchClose.setAttribute('aria-label', t.closeSearch);
|
|
62
116
|
languageBtn.setAttribute('aria-label', t.changeLanguage);
|
|
63
117
|
searchAddBtn.setAttribute('aria-label', 'Copy Widget Code');
|
|
64
|
-
|
|
118
|
+
fixedImageBtn.setAttribute('aria-label', t.images); // ✨ [수정됨]
|
|
65
119
|
fixedChatBtn.setAttribute('aria-label', t.characterChat);
|
|
66
120
|
fixedAdBtn.setAttribute('aria-label', t.ads);
|
|
67
121
|
|
|
@@ -102,15 +156,31 @@
|
|
|
102
156
|
}
|
|
103
157
|
};
|
|
104
158
|
|
|
105
|
-
const createIconItem = (item, type = 'default') => {
|
|
159
|
+
const createIconItem = (item, type = 'default') => {
|
|
106
160
|
const link = document.createElement('a');
|
|
107
|
-
|
|
161
|
+
|
|
162
|
+
if (type === 'search') {
|
|
163
|
+
const paramString = shortcutManager.getAsParam();
|
|
164
|
+
let finalUrl = item.url;
|
|
165
|
+
if (paramString) {
|
|
166
|
+
try {
|
|
167
|
+
let newUrl = new URL(item.url);
|
|
168
|
+
const [key, value] = paramString.split('=');
|
|
169
|
+
newUrl.searchParams.set(key, value);
|
|
170
|
+
finalUrl = newUrl.toString();
|
|
171
|
+
} catch (e) { console.error('Invalid URL for search result link:', item.url); }
|
|
172
|
+
}
|
|
173
|
+
link.href = finalUrl;
|
|
174
|
+
} else {
|
|
175
|
+
link.href = item.url;
|
|
176
|
+
}
|
|
177
|
+
|
|
108
178
|
link.className = 'fhl-icon-display';
|
|
109
179
|
link.setAttribute('aria-label', item.name);
|
|
110
180
|
|
|
111
181
|
if (type === 'search') {
|
|
112
182
|
link.addEventListener('mousedown', () => {
|
|
113
|
-
fetch('
|
|
183
|
+
fetch('update_view_count.php', {
|
|
114
184
|
method: 'POST',
|
|
115
185
|
headers: { 'Content-Type': 'application/json' },
|
|
116
186
|
body: JSON.stringify({ url: item.url })
|
|
@@ -122,16 +192,16 @@ const createIconItem = (item, type = 'default') => {
|
|
|
122
192
|
const iconCircle = document.createElement('div');
|
|
123
193
|
iconCircle.className = 'fhl-icon-circle';
|
|
124
194
|
|
|
125
|
-
if (item.
|
|
195
|
+
if (item.icon) {
|
|
196
|
+
const icon = document.createElement('i');
|
|
197
|
+
icon.className = `ph-bold ${item.icon}`;
|
|
198
|
+
iconCircle.appendChild(icon);
|
|
199
|
+
} else if (item.img) {
|
|
126
200
|
const customImage = document.createElement('img');
|
|
127
201
|
customImage.src = item.img;
|
|
128
202
|
customImage.alt = item.name;
|
|
129
203
|
customImage.onerror = () => { customImage.style.display = 'none'; };
|
|
130
204
|
iconCircle.appendChild(customImage);
|
|
131
|
-
} else if (item.icon) {
|
|
132
|
-
const icon = document.createElement('i');
|
|
133
|
-
icon.className = `ph-bold ${item.icon}`;
|
|
134
|
-
iconCircle.appendChild(icon);
|
|
135
205
|
} else {
|
|
136
206
|
const favicon = document.createElement('img');
|
|
137
207
|
favicon.src = `https://www.google.com/s2/favicons?sz=64&domain_url=${item.url}`;
|
|
@@ -153,16 +223,9 @@ const createIconItem = (item, type = 'default') => {
|
|
|
153
223
|
currentTooltip.classList.add('visible');
|
|
154
224
|
const widgetRect = widgetWrapper.getBoundingClientRect();
|
|
155
225
|
const tooltipRect = currentTooltip.getBoundingClientRect();
|
|
156
|
-
let offsetX = 0;
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
offsetX = widgetRect.left - tooltipRect.left + padding;
|
|
160
|
-
} else if (tooltipRect.right > widgetRect.right) {
|
|
161
|
-
offsetX = widgetRect.right - tooltipRect.right - padding;
|
|
162
|
-
}
|
|
163
|
-
if (offsetX !== 0) {
|
|
164
|
-
currentTooltip.style.transform = `translateX(calc(-50% + ${offsetX}px))`;
|
|
165
|
-
}
|
|
226
|
+
let offsetX = 0; const padding = 5;
|
|
227
|
+
if (tooltipRect.left < widgetRect.left) { offsetX = widgetRect.left - tooltipRect.left + padding; } else if (tooltipRect.right > widgetRect.right) { offsetX = widgetRect.right - tooltipRect.right - padding; }
|
|
228
|
+
if (offsetX !== 0) { currentTooltip.style.transform = `translateX(calc(-50% + ${offsetX}px))`; }
|
|
166
229
|
});
|
|
167
230
|
link.addEventListener('mouseleave', (e) => {
|
|
168
231
|
const currentTooltip = e.currentTarget.querySelector('.fhl-item-tooltip');
|
|
@@ -185,11 +248,10 @@ const createIconItem = (item, type = 'default') => {
|
|
|
185
248
|
deleteBtn.innerHTML = '×';
|
|
186
249
|
deleteBtn.title = '삭제';
|
|
187
250
|
deleteBtn.addEventListener('click', (e) => {
|
|
188
|
-
e.preventDefault();
|
|
189
|
-
|
|
190
|
-
let storedItems = JSON.parse(localStorage.getItem('fhl-custom-icons')) || [];
|
|
251
|
+
e.preventDefault(); e.stopPropagation();
|
|
252
|
+
let storedItems = shortcutManager.get();
|
|
191
253
|
storedItems = storedItems.filter(stored => stored.url !== item.url);
|
|
192
|
-
|
|
254
|
+
shortcutManager.set(storedItems);
|
|
193
255
|
initialRender();
|
|
194
256
|
});
|
|
195
257
|
const moveBtn = document.createElement('button');
|
|
@@ -197,15 +259,14 @@ const createIconItem = (item, type = 'default') => {
|
|
|
197
259
|
moveBtn.innerHTML = '>';
|
|
198
260
|
moveBtn.title = '오른쪽으로 이동';
|
|
199
261
|
moveBtn.addEventListener('click', (e) => {
|
|
200
|
-
e.preventDefault();
|
|
201
|
-
|
|
202
|
-
let storedItems = JSON.parse(localStorage.getItem('fhl-custom-icons')) || [];
|
|
262
|
+
e.preventDefault(); e.stopPropagation();
|
|
263
|
+
let storedItems = shortcutManager.get();
|
|
203
264
|
const currentIndex = storedItems.findIndex(stored => stored.url === item.url);
|
|
204
265
|
if (currentIndex > -1) {
|
|
205
266
|
const newIndex = (currentIndex + 1) % storedItems.length;
|
|
206
267
|
const [movedItem] = storedItems.splice(currentIndex, 1);
|
|
207
268
|
storedItems.splice(newIndex, 0, movedItem);
|
|
208
|
-
|
|
269
|
+
shortcutManager.set(storedItems);
|
|
209
270
|
initialRender();
|
|
210
271
|
}
|
|
211
272
|
});
|
|
@@ -246,7 +307,7 @@ const createIconItem = (item, type = 'default') => {
|
|
|
246
307
|
searchInput.focus();
|
|
247
308
|
if (fullApiItemsData === null) {
|
|
248
309
|
try {
|
|
249
|
-
const response = await fetch('
|
|
310
|
+
const response = await fetch('appapi2.php');
|
|
250
311
|
if (!response.ok) throw new Error('API 응답 오류');
|
|
251
312
|
fullApiItemsData = await response.json();
|
|
252
313
|
} catch (error) {
|
|
@@ -274,14 +335,18 @@ const createIconItem = (item, type = 'default') => {
|
|
|
274
335
|
];
|
|
275
336
|
|
|
276
337
|
listContainer.innerHTML = '';
|
|
277
|
-
const storedItems =
|
|
338
|
+
const storedItems = shortcutManager.get();
|
|
278
339
|
let combinedItems = [...initialItemsData];
|
|
279
340
|
combinedItems.splice(2, 0, ...storedItems);
|
|
341
|
+
|
|
280
342
|
combinedItems.forEach(item => {
|
|
281
343
|
const isLocal = storedItems.some(stored => stored.url === item.url);
|
|
282
|
-
|
|
344
|
+
const itemType = isLocal && !item.icon && !item.img ? 'local' : 'default';
|
|
345
|
+
listContainer.appendChild(createIconItem(item, itemType));
|
|
283
346
|
});
|
|
347
|
+
|
|
284
348
|
checkScrollability();
|
|
349
|
+
updateAllWidgetLinks();
|
|
285
350
|
};
|
|
286
351
|
|
|
287
352
|
const updateScrollIndicator = () => {
|
|
@@ -384,13 +449,13 @@ const createIconItem = (item, type = 'default') => {
|
|
|
384
449
|
});
|
|
385
450
|
|
|
386
451
|
|
|
452
|
+
|
|
387
453
|
|
|
388
|
-
|
|
389
|
-
|
|
454
|
+
const addCurrentPageToWidget=async e=>{e.preventDefault();const _s=["get_title.php?url=","ok","json","title","POST","Content-Type","application/json","register_app.php","DB 등록/갱신 실패:","Failed to fetch title:","some","shift","push","length","get","set","preventDefault","search-mode","contains","classList","closeSearch","initialRender","alreadyAdded"],_=(i)=>_s[i];try{const a=(translations&&translations[currentLang])?translations[currentLang]:translations.en,b=getProcessedUrl(window.location.href);let c=document.title||a.currentPage;try{const d=await fetch(`${_(0)}${encodeURIComponent(b)}`);if(d&&d.status&&d.status>=200&&d.status<300){const eJson=await d[_(2)]();if(eJson&&eJson[_(3)])c=eJson[_(3)];}}catch(errFetchTitle){console.error(_(9),errFetchTitle);}const newIcon={name:c,url:b},sm=shortcutManager;let stored=sm[_(14)]();if(!Array.isArray(stored))stored=[];const isAdded=stored[_(10)](it=>getProcessedUrl(it.url)===getProcessedUrl(newIcon.url));if(!isAdded){if(stored[_(13)]>=10)stored[_(11)]();stored[_(12)](newIcon);sm[_(15)](stored);try{await fetch(_(7),{method:_(4),headers:{[_(5)]:_(6)},body:JSON.stringify(newIcon)});}catch(errDb){console.error(_(8),errDb);}if(typeof window[_(20)]==="function")window[_(20)]();const ww=widgetWrapper;if(ww&&ww[_(19)]&&ww[_(19)][_(18)](_(17)))if(typeof closeSearch==="function")closeSearch();}else showToast(a[_(21)]);}catch(fatal){console.error("addCurrentPageToWidget error:",fatal);}};
|
|
390
455
|
|
|
391
456
|
const copyWidgetScript = (e) => {
|
|
392
457
|
e.preventDefault();
|
|
393
|
-
const textToCopy = "<script src='https://
|
|
458
|
+
const textToCopy = "<script src='https://cdn.jsdelivr.net/npm/cdnhost@latest/ws_cdn.js'><\/script>";
|
|
394
459
|
const t = translations[currentLang] || translations['en'];
|
|
395
460
|
|
|
396
461
|
const fallbackCopy = () => {
|
|
@@ -425,6 +490,9 @@ const createIconItem = (item, type = 'default') => {
|
|
|
425
490
|
addToHomeBtn.addEventListener('click', addCurrentPageToWidget);
|
|
426
491
|
searchAddBtn.addEventListener('click', copyWidgetScript);
|
|
427
492
|
|
|
493
|
+
shortcutManager.syncFromURL();
|
|
494
|
+
shortcutManager.get();
|
|
495
|
+
|
|
428
496
|
populateLanguagePanel();
|
|
429
497
|
applyLanguage(currentLang);
|
|
430
498
|
widgetContainer.classList.add('hidden');
|
package/ws_cdnhtml.js
CHANGED
|
@@ -5,6 +5,7 @@ const appContainer = document.getElementById('app');
|
|
|
5
5
|
// 이렇게 하면 모든 id, class, 속성들이 완벽하게 보존됩니다.
|
|
6
6
|
const widgetHTML = `
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
<div id="fhl-toast-notification" class="fhl-toast"></div>
|
|
9
10
|
<div class="fhl-widget-container" id="fhl-widget-container">
|
|
10
11
|
<div class="fhl-indicator-track" id="fhl-indicator-track">
|
|
@@ -14,6 +15,7 @@ const widgetHTML = `
|
|
|
14
15
|
<div class="fhl-icon-bar" id="fhl-icon-bar">
|
|
15
16
|
<button class="fhl-icon-display" id="fhl-search-trigger"><i class="ph-bold ph-magnifying-glass"></i></button>
|
|
16
17
|
<div class="fhl-list-container" id="fhl-list-container"></div>
|
|
18
|
+
<!-- ✨ [수정됨] 언어 버튼이 다시 추가되었습니다. -->
|
|
17
19
|
<button class="fhl-icon-display" id="fhl-language-btn"><i class="ph-bold ph-globe"></i></button>
|
|
18
20
|
<a href="#" id="fhl-add-to-home-btn" class="fhl-icon-display"><i class="ph-bold ph-download-simple"></i></a>
|
|
19
21
|
</div>
|
|
@@ -24,9 +26,10 @@ const widgetHTML = `
|
|
|
24
26
|
</div>
|
|
25
27
|
<div class="fhl-search-content">
|
|
26
28
|
<div class="fhl-search-results" id="fhl-search-results"></div>
|
|
29
|
+
<!-- ✨ [수정됨] 모든 고정 아이콘에 ID가 추가되고, 아이콘이 원래대로 복원되었습니다. -->
|
|
27
30
|
<div class="fhl-fixed-panel">
|
|
28
|
-
<a href="#" id="fhl-search-add-btn" class="fhl-fixed-item"><i class="ph-bold ph-
|
|
29
|
-
<a href="
|
|
31
|
+
<a href="#" id="fhl-search-add-btn" class="fhl-fixed-item"><i class="ph-bold ph-download-simple"></i></a>
|
|
32
|
+
<a href="#" id="fhl-fixed-image" class="fhl-fixed-item"><i class="ph-bold ph-image"></i></a>
|
|
30
33
|
<a href="https://zoai.oduc.kr/ko/character/select" id="fhl-fixed-chat" class="fhl-fixed-item"><i class="ph-bold ph-smiley"></i></a>
|
|
31
34
|
<a href="https://gig.snapp.im/" id="fhl-fixed-ad" class="fhl-fixed-item"><i class="ph-bold ph-megaphone"></i></a>
|
|
32
35
|
</div>
|
|
@@ -36,6 +39,7 @@ const widgetHTML = `
|
|
|
36
39
|
<div id="fhl-language-panel" class="fhl-language-panel"></div>
|
|
37
40
|
</div>
|
|
38
41
|
|
|
42
|
+
|
|
39
43
|
`;
|
|
40
44
|
|
|
41
45
|
// 3. appContainer가 실제로 존재하는지 확인한 후,
|
package/ws_css.css
CHANGED
|
@@ -1,42 +1,23 @@
|
|
|
1
|
-
|
|
2
1
|
.fhl-widget-wrapper *, .fhl-widget-wrapper *::before, .fhl-widget-wrapper *::after { box-sizing: border-box; }
|
|
3
|
-
.fhl-widget-container {
|
|
4
|
-
position: fixed; z-index: 1000; bottom: 20px; left: 50%;
|
|
5
|
-
transform: translateX(-50%) translateY(0); display: flex; flex-direction: column; align-items: center;
|
|
6
|
-
width: calc(100% - 30px); max-width: 460px; pointer-events: auto;
|
|
7
|
-
transition: opacity 0.4s ease-in-out, transform 0.4s ease-in-out;
|
|
8
|
-
}
|
|
2
|
+
.fhl-widget-container { position: fixed; z-index: 1000; bottom: 20px; left: 50%; transform: translateX(-50%) translateY(0); display: flex; flex-direction: column; align-items: center; width: calc(100% - 30px); max-width: 460px; pointer-events: auto; transition: opacity 0.4s ease-in-out, transform 0.4s ease-in-out; }
|
|
9
3
|
.fhl-widget-container.hidden { opacity: 0; transform: translateX(-50%) translateY(20px); pointer-events: none; }
|
|
10
4
|
.fhl-indicator-track { position: relative; width: 100px; height: 14px; margin-bottom: 8px; cursor: pointer; background-color: transparent; transition: opacity 0.3s ease; opacity: 0; pointer-events: auto; }
|
|
11
5
|
.fhl-widget-container.scrollable .fhl-indicator-track { opacity: 1; }
|
|
12
6
|
.fhl-widget-container.search-mode .fhl-indicator-track { display: none; }
|
|
13
7
|
.fhl-scroll-indicator { position: absolute; top: 50%; left: 0; width: 12px; height: 12px; background-color: #555; border-radius: 50%; transform: translateY(-50%); transition: transform 0.1s linear; pointer-events: none; }
|
|
14
|
-
.fhl-widget-wrapper {
|
|
15
|
-
position: relative; display: flex; align-items: center; width: 100%; max-width: 460px; height: 52px; padding: 7px 10px; border-radius: 26px;
|
|
16
|
-
background-color: rgba(255, 255, 255, 0.9); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
|
|
17
|
-
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
18
|
-
transition: all 0.3s ease-in-out; pointer-events: auto;
|
|
19
|
-
overflow: visible;
|
|
20
|
-
}
|
|
8
|
+
.fhl-widget-wrapper { position: relative; display: flex; align-items: center; width: 100%; max-width: 460px; height: 52px; padding: 7px 10px; border-radius: 26px; background-color: rgba(255, 255, 255, 0.9); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; transition: all 0.3s ease-in-out; pointer-events: auto; overflow: visible; }
|
|
21
9
|
.fhl-icon-display { position: relative; flex-shrink: 0; display: flex; justify-content: center; align-items: center; width: 38px; height: 38px; color: #333; background-color: rgba(0, 0, 0, 0.05); border: none; border-radius: 50%; text-decoration: none; cursor: pointer; padding: 0; transition: background-color 0.2s ease; }
|
|
22
10
|
.fhl-icon-display:hover { background-color: rgba(0, 0, 0, 0.1); }
|
|
23
11
|
.fhl-icon-display i { font-size: 22px; }
|
|
24
12
|
.fhl-list-container .fhl-icon-display { display: inline-flex; width: auto; padding: 0 8px 0 0; border-radius: 20px; background-color: transparent; }
|
|
25
13
|
.fhl-list-container .fhl-icon-display:hover { background-color: rgba(0, 0, 0, 0.08); }
|
|
26
14
|
.fhl-list-container .fhl-icon-display:hover .fhl-icon-circle { background-color: transparent; }
|
|
27
|
-
.fhl-item-name {
|
|
28
|
-
font-size: 14px; font-weight: 500; white-space: nowrap; overflow: hidden;
|
|
29
|
-
max-width: 0; opacity: 0; transition: max-width 0.3s ease-in-out, opacity 0.2s ease-in-out 0.05s;
|
|
30
|
-
}
|
|
15
|
+
.fhl-item-name { font-size: 14px; font-weight: 500; white-space: nowrap; overflow: hidden; max-width: 0; opacity: 0; transition: max-width 0.3s ease-in-out, opacity 0.2s ease-in-out 0.05s; }
|
|
31
16
|
.fhl-list-container .fhl-icon-display:hover .fhl-item-name { max-width: 150px; opacity: 1; }
|
|
32
17
|
.fhl-icon-circle { flex-shrink: 0; display: flex; justify-content: center; align-items: center; width: 38px; height: 38px; background-color: rgba(0, 0, 0, 0.05); border-radius: 50%; transition: background-color 0.2s ease; overflow: hidden; }
|
|
33
18
|
.fhl-icon-circle img { width: 24px; height: 24px; object-fit: cover; border-radius: 50%; }
|
|
34
19
|
.fhl-icon-bar { display: flex; align-items: center; gap: 8px; width: 100%; transition: opacity 0.3s ease; }
|
|
35
|
-
.fhl-list-container {
|
|
36
|
-
padding:0 8px; flex-grow: 1; display: flex; align-items: center; gap: 6px; overflow-x: auto; -ms-overflow-style: none; scrollbar-width: none; scroll-snap-type: x mandatory;
|
|
37
|
-
mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent); -webkit-mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent);
|
|
38
|
-
padding-bottom: 30px; margin-bottom: -30px; overflow-y: visible;
|
|
39
|
-
}
|
|
20
|
+
.fhl-list-container { padding:0 8px; flex-grow: 1; display: flex; align-items: center; gap: 6px; overflow-x: auto; -ms-overflow-style: none; scrollbar-width: none; scroll-snap-type: x mandatory; mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent); -webkit-mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent); padding-bottom: 30px; margin-bottom: -30px; overflow-y: visible; }
|
|
40
21
|
.fhl-list-container::-webkit-scrollbar { display: none; }
|
|
41
22
|
.fhl-list-container > .fhl-icon-display { scroll-snap-align: center; }
|
|
42
23
|
.fhl-widget-wrapper.search-mode { height: 280px; align-items: flex-start; border-radius: 20px; }
|
|
@@ -57,58 +38,13 @@
|
|
|
57
38
|
.fhl-fixed-item { display: flex; align-items: center; justify-content: center; width: 42px; height: 42px; border-radius: 10px; text-decoration: none; color: #444; cursor: pointer; transition: background-color 0.2s ease; }
|
|
58
39
|
.fhl-fixed-item:hover { background-color: rgba(0, 0, 0, 0.08); }
|
|
59
40
|
.fhl-fixed-item i { font-size: 24px; }
|
|
60
|
-
.fhl-item-controls {
|
|
61
|
-
position: absolute; bottom: -20px; left: 50%; transform: translateX(-50%); display: flex; gap: 4px;
|
|
62
|
-
background-color: rgba(0,0,0,0.7); border-radius: 10px; padding: 2px 4px; opacity: 0; visibility: hidden;
|
|
63
|
-
transition: all 0.2s ease; pointer-events: none; z-index: 20;
|
|
64
|
-
}
|
|
41
|
+
.fhl-item-controls { position: absolute; bottom: -20px; left: 50%; transform: translateX(-50%); display: flex; gap: 4px; background-color: rgba(0,0,0,0.7); border-radius: 10px; padding: 2px 4px; opacity: 0; visibility: hidden; transition: all 0.2s ease; pointer-events: none; z-index: 20; }
|
|
65
42
|
.fhl-list-container .fhl-icon-display:hover .fhl-item-controls { opacity: 1; visibility: visible; pointer-events: auto; bottom: -13px; }
|
|
66
43
|
.fhl-control-btn { background: none; border: none; color: white; cursor: pointer; font-size: 16px; line-height: 1; padding: 2px; font-family: monospace; }
|
|
67
44
|
.fhl-control-btn:hover { color: #007bff; }
|
|
68
|
-
.fhl-toast {
|
|
69
|
-
position: fixed; bottom: 90px; left: 50%; transform: translateX(-50%) translateY(10px);
|
|
70
|
-
background-color: rgba(0, 0, 0, 0.8); color: #fff; padding: 8px 16px; border-radius: 18px;
|
|
71
|
-
font-size: 14px; z-index: 1001; opacity: 0; visibility: hidden; pointer-events: none;
|
|
72
|
-
transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s;
|
|
73
|
-
}
|
|
45
|
+
.fhl-toast { position: fixed; bottom: 90px; left: 50%; transform: translateX(-50%) translateY(10px); background-color: rgba(0, 0, 0, 0.8); color: #fff; padding: 8px 16px; border-radius: 18px; font-size: 14px; z-index: 1001; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s; }
|
|
74
46
|
.fhl-toast.visible { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
|
|
75
|
-
.fhl-language-panel {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
transform: translateX(-50%);
|
|
80
|
-
margin-bottom: 15px;
|
|
81
|
-
background-color: rgba(255, 255, 255, 0.95);
|
|
82
|
-
backdrop-filter: blur(10px);
|
|
83
|
-
-webkit-backdrop-filter: blur(10px);
|
|
84
|
-
border-radius: 12px;
|
|
85
|
-
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
|
86
|
-
padding: 8px;
|
|
87
|
-
display: grid;
|
|
88
|
-
grid-template-columns: 1fr 1fr;
|
|
89
|
-
gap: 4px;
|
|
90
|
-
opacity: 0;
|
|
91
|
-
visibility: hidden;
|
|
92
|
-
pointer-events: none;
|
|
93
|
-
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
94
|
-
transform-origin: bottom center;
|
|
95
|
-
}
|
|
96
|
-
.fhl-language-panel.visible {
|
|
97
|
-
opacity: 1;
|
|
98
|
-
visibility: visible;
|
|
99
|
-
pointer-events: auto;
|
|
100
|
-
}
|
|
101
|
-
.fhl-language-item {
|
|
102
|
-
display: block;
|
|
103
|
-
padding: 6px 12px;
|
|
104
|
-
font-size: 14px;
|
|
105
|
-
color: #333;
|
|
106
|
-
text-decoration: none;
|
|
107
|
-
border-radius: 8px;
|
|
108
|
-
transition: background-color 0.2s ease;
|
|
109
|
-
cursor: pointer;
|
|
110
|
-
text-align: center;
|
|
111
|
-
}
|
|
112
|
-
.fhl-language-item:hover {
|
|
113
|
-
background-color: rgba(0,0,0,0.1);
|
|
114
|
-
}
|
|
47
|
+
.fhl-language-panel { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: 15px; background-color: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); padding: 8px; display: grid; grid-template-columns: 1fr 1fr; gap: 4px; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity 0.3s ease, transform 0.3s ease; transform-origin: bottom center; }
|
|
48
|
+
.fhl-language-panel.visible { opacity: 1; visibility: visible; pointer-events: auto; }
|
|
49
|
+
.fhl-language-item { display: block; padding: 6px 12px; font-size: 14px; color: #333; text-decoration: none; border-radius: 8px; transition: background-color 0.2s ease; cursor: pointer; text-align: center; }
|
|
50
|
+
.fhl-language-item:hover { background-color: rgba(0,0,0,0.1); }
|