maplibre-preview 1.4.3 → 1.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff0508a77186b4b42b987de36d9f307556f182cd184d62a206963d211719963f
4
- data.tar.gz: d547c84b4b1ca39ffe52b065a493dbec416d6aa5c5829c401bcafebd0dd9ad6f
3
+ metadata.gz: ab6c314ff00f531f6088b82f2d11b44206fe947f407d512f725ea5efd4df10d1
4
+ data.tar.gz: 2698dbd6a6c458f4c475d71b5dc549bbfde888c96357c6d18c74060b666ffca7
5
5
  SHA512:
6
- metadata.gz: 61b409020f21b43cf1b7cdb75ff6ea28980927d73cd60aa9e3acb7105eda17b9843c2789ef6eaa03437c8068b028441c01f78487b5e8df294fe0aecae478d4c1
7
- data.tar.gz: b066ef7e588b70d8ececa8eb3d90c66dae571f2ec15082dc2859b27ac3834d605b35f6e5722f4684fd0ba21ab21d45b21423de82794f3ae9d7f03a5459b849c4
6
+ metadata.gz: deefd8864adfd9cdd6189f10834bab986c11cfd50e1413eca36f57238ff3d6d3a98eda71bb370e0c14699542abcc9bb8bdde4b271f2693bd802777184539dc9f
7
+ data.tar.gz: 599cdcbc90b314f3bdc21a765fe6817094cec77073843896fca53b77a7e9be90d5a8ccbfe331dc969dd50fd7dcccb3e3e5721095acead56ee958833d13867a6a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.7.2] - 2026-05-14
4
+
5
+ ### Changed
6
+ - **Map settings styling** - rounded all control panel corners and styled range sliders to match the dark UI
7
+
8
+ ## [1.7.0] - 2026-05-14
9
+
10
+ ### Added
11
+ - **Style source parameters** - detect `query_params` / `queryParams` from style sources and source metadata
12
+ - **Style parameters panel** - add a bottom-center collapsible panel for passing detected parameters into style, source, tile, data, and metadata URLs
13
+ - **Date/time parameter inputs** - render date/time-like parameters as `datetime-local` fields and send them as epoch seconds
14
+
15
+ ### Changed
16
+ - **Overlay layout** - stack bottom overlays so Style Parameters, Loading, and Elevation Profile panels do not cover each other
17
+ - **Style parameter state** - persist parameter values per style URL and keep applied values in the page query string
18
+
19
+ ## [1.6.0] - 2026-05-13
20
+
21
+ ### Added
22
+ - **Basemap opacity control** - added a Map Settings slider for changing preview basemap transparency without changing the tested style
23
+ - **Terrain exaggeration control** - added a terrain-only slider that updates `map.setTerrain({ source, exaggeration })`
24
+ - **MapLibre debug controls** - added Collision Boxes, Overdraw, and Raster Fade toggles for label placement, dense style, and raster/DEM diagnostics
25
+
26
+ ### Changed
27
+ - **Tile Boundaries naming** - renamed the Tile Grid UI to Tile Boundaries to clarify that it uses MapLibre `showTileBoundaries`
28
+ - **Map Settings layout** - added compact setting rows and range styling for view-mode map controls
29
+
30
+ ## [1.5.1] - 2026-05-13
31
+
32
+ ### Changed
33
+ - **Map control layout** - split map settings and style controls into independent panels with separate collapse controls
34
+ - **Panel sizing** - adjusted map settings, filters, and layers panels to size from their visible content
35
+
36
+ ### Fixed
37
+ - **Filter and layer scrolling** - restored internal scrolling for large filter and layer lists after the panel layout split
38
+
39
+ ## [1.5.0] - 2026-05-13
40
+
41
+ ### Added
42
+ - **Map request cache toggle** - added a development mode that disables browser HTTP cache for style and MapLibre resource requests
43
+
44
+ ## [1.4.4] - 2026-05-13
45
+
46
+ ### Fixed
47
+ - **Filter labels** - normalize object-based localized labels to display `title`, `name`, or `label` instead of `[object Object]`
48
+
3
49
  ## [1.4.2] - 2026-04-23
4
50
 
5
51
  ### Changed
data/README.md CHANGED
@@ -125,6 +125,19 @@ The gem uses fixed configurations for optimal compatibility:
125
125
 
126
126
  **Style URL**: Pass via URL parameter `?style_url=https://example.com/style.json`
127
127
 
128
+ ## Map Settings
129
+
130
+ The preview UI keeps focused MapLibre controls that are useful for tile, style, and terrain services:
131
+
132
+ - **Basemap opacity**: changes the injected OpenStreetMap preview layer opacity while preserving the tested style.
133
+ - **Terrain exaggeration**: appears for styles with `terrain` and updates `map.setTerrain({ source, exaggeration })`.
134
+ - **Antialias**: reloads the WebGL context with antialiasing enabled or disabled.
135
+ - **Cache**: disables browser cache for style and map requests when comparing freshly generated tiles/styles.
136
+ - **Tile Boundaries**: toggles MapLibre tile boundary overlay, including tile coordinate/zoom diagnostics in pitched views, and visible tile count diagnostics.
137
+ - **Collision Boxes**: toggles MapLibre symbol collision boxes for label/icon debugging.
138
+ - **Overdraw**: toggles MapLibre overdraw inspection for dense mixed styles and expensive layers.
139
+ - **Raster Fade**: toggles raster tile fade duration for raster/DEM cache and reconstruction checks.
140
+
128
141
  ## API Reference
129
142
 
130
143
  ### Sinatra Extension
data/bin/maplibre-preview CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'maplibre-preview'
4
+ require_relative '../lib/maplibre-preview'
5
5
  require 'optparse'
6
6
  require 'json'
7
7
  require 'fileutils'
data/docs/README_RU.md CHANGED
@@ -125,6 +125,19 @@ Gem использует фиксированные настройки:
125
125
 
126
126
  **URL стиля**: Передается через параметр URL `?style_url=https://example.com/style.json`
127
127
 
128
+ ## Настройки карты
129
+
130
+ Интерфейс preview оставляет только те настройки MapLibre, которые полезны для сервисов тайлов, стилей и рельефа:
131
+
132
+ - **Basemap opacity**: меняет прозрачность добавленной preview-подложки OpenStreetMap, не меняя тестируемый стиль.
133
+ - **Terrain exaggeration**: появляется для стилей с `terrain` и обновляет `map.setTerrain({ source, exaggeration })`.
134
+ - **Antialias**: перезагружает WebGL context с включенным или выключенным antialiasing.
135
+ - **Cache**: отключает browser cache для style и map requests при проверке свежесгенерированных тайлов/стилей.
136
+ - **Tile Boundaries**: включает MapLibre overlay границ тайлов, включая диагностику координат/зумов тайлов при наклоне карты, и счетчик видимых тайлов.
137
+ - **Collision Boxes**: включает MapLibre symbol collision boxes для отладки подписей и иконок.
138
+ - **Overdraw**: включает MapLibre overdraw inspection для плотных mixed styles и дорогих слоев.
139
+ - **Raster Fade**: включает или выключает fade duration у raster tiles для проверки raster/DEM cache и reconstruction.
140
+
128
141
  ## Справочник API
129
142
 
130
143
  ### Расширение Sinatra
@@ -37,16 +37,28 @@ class Filters {
37
37
  const languagePriority = ['en-US', 'en', 'ru'];
38
38
 
39
39
  for (const lang of languagePriority) {
40
- if (locale[lang]?.[filterId]) return locale[lang][filterId];
40
+ const label = this.normalizeLocalizedFilterName(locale[lang]?.[filterId]);
41
+ if (label) return label;
41
42
  }
42
43
 
43
44
  for (const lang in locale) {
44
- if (locale[lang]?.[filterId]) return locale[lang][filterId];
45
+ const label = this.normalizeLocalizedFilterName(locale[lang]?.[filterId]);
46
+ if (label) return label;
45
47
  }
46
48
 
47
49
  return filterId;
48
50
  }
49
51
 
52
+ normalizeLocalizedFilterName(value) {
53
+ if (!value) return null;
54
+ if (typeof value === 'string') return value;
55
+ if (typeof value === 'object') {
56
+ return value.title || value.name || value.label || null;
57
+ }
58
+
59
+ return String(value);
60
+ }
61
+
50
62
  applyFilterMode() {
51
63
  if (this.isUpdating) return;
52
64
  this.isUpdating = true;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TileGridManager - manages tile boundaries visualization and tile statistics
2
+ * TileGridManager - manages MapLibre tile boundary overlay and tile statistics
3
3
  */
4
4
  class TileGridManager {
5
5
  constructor(options) {
@@ -27,7 +27,7 @@ class TileGridManager {
27
27
 
28
28
  this.panelContainer.innerHTML = `
29
29
  <div class="tilegrid-header">
30
- <span class="tilegrid-title">Tile Grid</span>
30
+ <span class="tilegrid-title">Tile Boundaries</span>
31
31
  <button class="tilegrid-close" onclick="tileGridManager.toggle()">×</button>
32
32
  </div>
33
33
  <div class="tilegrid-stats">
@@ -36,7 +36,7 @@ class TileGridManager {
36
36
  </div>
37
37
  <div class="tilegrid-controls">
38
38
  <label class="tilegrid-checkbox-label">
39
- <span>Show tile borders</span>
39
+ <span>Show boundary overlay</span>
40
40
  <input type="checkbox" id="tilegrid-borders-checkbox" checked onchange="tileGridManager.toggleBorders(this.checked)">
41
41
  <span class="tilegrid-checkbox-custom"></span>
42
42
  </label>
@@ -118,7 +118,7 @@ class TileGridManager {
118
118
 
119
119
  const btn = document.getElementById('tilegrid-mode-btn');
120
120
  if (btn) {
121
- btn.textContent = this.isVisible ? 'Hide Tile Grid' : 'Tile Grid';
121
+ btn.textContent = this.isVisible ? 'Hide Tile Boundaries' : 'Tile Boundaries';
122
122
  btn.className = `control-button ${this.isVisible ? 'active' : ''}`;
123
123
  }
124
124
 
@@ -171,4 +171,3 @@ class TileGridManager {
171
171
  }
172
172
  }
173
173
  }
174
-
@@ -1,3 +1,3 @@
1
1
  module MapLibrePreview
2
- VERSION = '1.4.3'
2
+ VERSION = '1.7.2'
3
3
  end
@@ -175,37 +175,80 @@ html
175
175
  | .controls a:hover { text-decoration: underline; }
176
176
  | .layer-controls-wrapper {
177
177
  | position: absolute; top: 50%; left: 10px; transform: translateY(-50%); z-index: 1000;
178
- | display: flex; align-items: center; gap: 0;
178
+ | display: flex; align-items: flex-start; gap: 0;
179
179
  | }
180
180
  | .layer-controls {
181
- | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555; border-right: none; padding: 15px;
182
- | border-radius: 4px; color: #a9b7c6; display: flex; flex-direction: column; gap: 6px;
183
- | max-width: 300px; max-height: 80vh; transition: max-width 0.2s;
184
- | overflow: hidden; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); order: 1;
181
+ | background: transparent; border: none; padding: 0;
182
+ | border-radius: 4px; color: #a9b7c6; display: flex; flex-direction: column; gap: 12px;
183
+ | width: fit-content; max-width: min(90vw, 420px); max-height: 80vh; transition: max-width 0.2s;
184
+ | overflow: hidden; order: 1;
185
185
  | }
186
- | .layer-controls.collapsed {
187
- | max-width: 0; padding: 15px 0; border: none; opacity: 0; pointer-events: none;
186
+ | .control-section-wrapper {
187
+ | display: flex; align-items: center; width: max-content; max-width: min(90vw, 420px);
188
188
  | }
189
- | .layer-controls-toggle {
189
+ | .control-section-wrapper + .control-section-wrapper {
190
+ | margin-top: 4px;
191
+ | }
192
+ | .control-section {
193
+ | display: flex; flex-direction: column; gap: 8px; min-height: 0; width: max-content;
194
+ | max-width: min(90vw, 420px);
195
+ | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555; padding: 12px;
196
+ | border-radius: 4px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
197
+ | }
198
+ | .control-section-wrapper:not(.collapsed) .control-section {
199
+ | border-radius: 4px;
200
+ | }
201
+ | .control-section.collapsed {
202
+ | gap: 0;
203
+ | }
204
+ | .control-section-header {
205
+ | display: flex; align-items: center; justify-content: space-between; gap: 8px;
206
+ | }
207
+ | .control-section-title {
208
+ | color: #ffc66d; font-size: 11px; font-weight: bold; line-height: 1.2;
209
+ | text-transform: uppercase;
210
+ | }
211
+ | .control-section-toggle-external {
190
212
  | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555;
191
213
  | border-left: none; border-radius: 0 4px 4px 0; padding: 8px 4px;
192
214
  | color: #a9b7c6; cursor: pointer; display: flex; align-items: center; justify-content: center;
193
- | width: 18px; height: auto; min-height: 60px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); order: 2;
215
+ | width: 18px; height: auto; min-height: 60px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); flex-shrink: 0;
194
216
  | }
195
- | .layer-controls-toggle:hover {
217
+ | .control-section-toggle-external:hover {
196
218
  | background: rgba(75, 77, 79, 0.95); color: #ffc66d; border-color: #666666;
197
219
  | }
198
- | .layer-controls-wrapper.collapsed .layer-controls-toggle {
199
- | order: 0; border-left: 1px solid #555555; border-right: 1px solid #555555; border-radius: 4px;
220
+ | .control-section-wrapper.collapsed .control-section-toggle-external {
221
+ | display: none;
222
+ | }
223
+ | .control-section-toggle-inline {
224
+ | background: #4b4d4f; color: #a9b7c6; border: 1px solid #555555;
225
+ | border-radius: 3px; cursor: pointer; font-size: 10px; line-height: 1;
226
+ | padding: 0; width: 22px; height: 22px; transition: all 0.2s ease; flex-shrink: 0;
227
+ | display: none; align-items: center; justify-content: center;
200
228
  | }
201
- | .layer-controls-wrapper.collapsed .toggle-icon {
202
- | transform: rotate(180deg);
229
+ | .control-section-toggle-inline:hover {
230
+ | background: #5a5d5f; border-color: #666666;
231
+ | }
232
+ | .control-section.collapsed .control-section-toggle-inline {
233
+ | display: flex;
203
234
  | }
204
235
  | .toggle-icon {
205
236
  | font-size: 12px; font-weight: bold; transition: transform 0.2s;
206
237
  | }
238
+ | .control-section-body {
239
+ | display: flex; flex-direction: column; gap: 8px; min-height: 0;
240
+ | }
241
+ | .control-section.collapsed .control-section-body {
242
+ | display: none;
243
+ | }
244
+ | .map-settings-section {
245
+ | flex-shrink: 0;
246
+ | }
247
+ | .style-controls-section {
248
+ | overflow: hidden; min-width: 160px; max-height: calc(80vh - 120px);
249
+ | }
207
250
  | .mode-switcher {
208
- | display: flex; gap: 2px; margin-bottom: 10px; background: #3c3f41;
251
+ | display: flex; gap: 2px; margin-bottom: 10px; background: #3c3f41; width: 100%;
209
252
  | border-radius: 3px; padding: 2px; flex-shrink: 0;
210
253
  | }
211
254
  | .mode-button {
@@ -218,6 +261,125 @@ html
218
261
  | display: none; flex-direction: column; gap: 6px;
219
262
  | max-height: calc(80vh - 200px); overflow-y: auto;
220
263
  | }
264
+ | .settings-panel {
265
+ | max-height: none; overflow: visible; width: 220px;
266
+ | }
267
+ | .setting-group {
268
+ | display: flex; flex-direction: column; gap: 6px; padding-bottom: 8px;
269
+ | border-bottom: 1px solid #464749;
270
+ | }
271
+ | .setting-label {
272
+ | display: flex; align-items: center; justify-content: space-between; gap: 8px;
273
+ | color: #a9b7c6; font-size: 11px; font-weight: bold;
274
+ | }
275
+ | .setting-value {
276
+ | color: #6897bb; font-family: 'Courier New', monospace; font-weight: bold;
277
+ | }
278
+ | .setting-range {
279
+ | width: 100%; height: 18px; margin: 0; accent-color: #6897bb; cursor: pointer;
280
+ | background: transparent; appearance: none; -webkit-appearance: none;
281
+ | }
282
+ | .setting-range:focus {
283
+ | outline: none;
284
+ | }
285
+ | .setting-range:focus-visible::-webkit-slider-thumb {
286
+ | box-shadow: 0 0 0 2px #313335, 0 0 0 4px #6897bb;
287
+ | }
288
+ | .setting-range:focus-visible::-moz-range-thumb {
289
+ | box-shadow: 0 0 0 2px #313335, 0 0 0 4px #6897bb;
290
+ | }
291
+ | .setting-range::-webkit-slider-runnable-track {
292
+ | height: 6px; background: #313335; border: 1px solid #555555; border-radius: 3px;
293
+ | }
294
+ | .setting-range::-moz-range-track {
295
+ | height: 6px; background: #313335; border: 1px solid #555555; border-radius: 3px;
296
+ | }
297
+ | .setting-range::-moz-range-progress {
298
+ | height: 6px; background: #6897bb; border-radius: 3px;
299
+ | }
300
+ | .setting-range::-webkit-slider-thumb {
301
+ | width: 14px; height: 14px; margin-top: -5px; background: #6897bb;
302
+ | border: 1px solid #7aa8cc; border-radius: 50%; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
303
+ | -webkit-appearance: none; transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
304
+ | }
305
+ | .setting-range::-moz-range-thumb {
306
+ | width: 14px; height: 14px; background: #6897bb;
307
+ | border: 1px solid #7aa8cc; border-radius: 50%; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
308
+ | transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
309
+ | }
310
+ | .setting-range:hover::-webkit-slider-runnable-track {
311
+ | border-color: #666666;
312
+ | }
313
+ | .setting-range:hover::-moz-range-track {
314
+ | border-color: #666666;
315
+ | }
316
+ | .setting-range:hover::-webkit-slider-thumb {
317
+ | background: #7aa8cc; border-color: #8bb9dd;
318
+ | }
319
+ | .setting-range:hover::-moz-range-thumb {
320
+ | background: #7aa8cc; border-color: #8bb9dd;
321
+ | }
322
+ | .style-parameter-fields {
323
+ | display: flex; flex-direction: column; gap: 8px;
324
+ | }
325
+ | .style-parameters-overlay {
326
+ | position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%);
327
+ | z-index: 1000; width: min(520px, calc(100vw - 32px));
328
+ | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555;
329
+ | border-radius: 4px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
330
+ | color: #a9b7c6; overflow: hidden;
331
+ | }
332
+ | .style-parameters-header {
333
+ | display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 12px;
334
+ | width: 100%; background: transparent; color: inherit; border: none;
335
+ | padding: 10px 12px; cursor: pointer; text-align: left;
336
+ | }
337
+ | .style-parameters-header:hover {
338
+ | background: rgba(75, 77, 79, 0.95);
339
+ | }
340
+ | .style-parameters-title {
341
+ | color: #ffc66d; font-size: 11px; font-weight: bold; line-height: 1.2;
342
+ | text-transform: uppercase;
343
+ | }
344
+ | .style-parameters-summary {
345
+ | display: inline-flex; align-items: center; gap: 4px; color: #6897bb;
346
+ | font-family: 'Courier New', monospace; font-size: 11px; font-weight: bold;
347
+ | }
348
+ | .style-parameters-toggle-icon {
349
+ | color: #a9b7c6; font-size: 12px; font-weight: bold; line-height: 1;
350
+ | }
351
+ | .style-parameters-overlay:not(.collapsed) .style-parameters-toggle-icon {
352
+ | transform: rotate(180deg);
353
+ | }
354
+ | .style-parameters-body {
355
+ | display: flex; flex-direction: column; gap: 10px;
356
+ | max-height: min(48vh, 420px); overflow-y: auto;
357
+ | padding: 0 12px 12px; border-top: 1px solid #555555;
358
+ | }
359
+ | .style-parameters-overlay.collapsed .style-parameters-body {
360
+ | display: none;
361
+ | }
362
+ | .style-parameter-row {
363
+ | display: flex; flex-direction: column; gap: 4px;
364
+ | }
365
+ | .style-parameter-label {
366
+ | color: #808080; font-size: 10px; font-weight: bold;
367
+ | text-transform: uppercase;
368
+ | }
369
+ | .style-parameter-input {
370
+ | width: 100%; background: #313335; color: #a9b7c6; border: 1px solid #555555;
371
+ | border-radius: 3px; padding: 6px 8px; font-size: 11px; box-sizing: border-box;
372
+ | }
373
+ | .style-parameter-input:focus {
374
+ | outline: none; border-color: #6897bb; box-shadow: 0 0 0 1px #6897bb;
375
+ | }
376
+ | .style-parameter-actions {
377
+ | display: grid; grid-template-columns: 1fr 1fr; gap: 6px;
378
+ | }
379
+ | .style-panel {
380
+ | flex: 1; min-height: 0; max-height: calc(80vh - 245px); width: max-content; max-width: 100%;
381
+ | overflow: hidden;
382
+ | }
221
383
  | .control-panel::-webkit-scrollbar { width: 8px; }
222
384
  | .control-panel::-webkit-scrollbar-track { background: #3c3f41; border-radius: 4px; }
223
385
  | .control-panel::-webkit-scrollbar-thumb {
@@ -231,16 +393,26 @@ html
231
393
  | display: flex; flex-direction: column; gap: 6px;
232
394
  | }
233
395
  | .control-panel-content {
234
- | flex: 1; overflow-y: auto;
396
+ | flex: 1; min-height: 0; max-height: calc(80vh - 325px); overflow-y: auto;
397
+ | width: max-content; max-width: 100%;
398
+ | }
399
+ | #filter-buttons, #layer-buttons {
400
+ | display: inline-grid; grid-template-columns: max-content; gap: 2px; width: max-content;
401
+ | justify-items: stretch;
235
402
  | }
236
403
  | .control-button {
237
404
  | background: #4b4d4f; color: #a9b7c6; border: 1px solid #555555;
238
405
  | padding: 6px 12px; border-radius: 3px; cursor: pointer; font-size: 11px;
239
- | transition: all 0.2s ease; width: 100%; box-sizing: border-box;
406
+ | transition: all 0.2s ease; width: 100%; box-sizing: border-box; white-space: nowrap;
407
+ | }
408
+ | #filter-buttons .control-button, #layer-buttons .control-button {
409
+ | width: 100%; text-align: center;
240
410
  | }
241
411
  | .control-button:hover { background: #5a5d5f; border-color: #666666; }
242
412
  | .control-button.active { background: #6a9955; color: #ffffff; border-color: #7bb366; }
243
413
  | .control-button.inactive { background: #3c3f41; color: #808080; border-color: #464749; }
414
+ | .control-button.cache-off { background: #7a2f35; color: #ffffff; border-color: #9b3f47; font-weight: bold; }
415
+ | .control-button.cache-off:hover { background: #8f3841; border-color: #b64b55; }
244
416
  | .filter-group {
245
417
  | margin-bottom: 8px;
246
418
  | border: 1px solid #555555;
@@ -302,7 +474,7 @@ html
302
474
  | background: rgba(60, 63, 65, 0.95); border: 1px solid #555555;
303
475
  | border-radius: 6px; padding: 12px 20px; z-index: 1000;
304
476
  | backdrop-filter: blur(10px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
305
- | transition: all 0.3s ease; display: none;
477
+ | transition: opacity 0.3s ease; display: none;
306
478
  | }
307
479
  | .loading-progress { text-align: center; color: #a9b7c6; }
308
480
  | .loading-text { font-size: 14px; color: #a9b7c6; margin-bottom: 8px; }