Library-Thumbnails: Auto-Capture + Base64-Preview in der UI
Beim Hinzufuegen oder Importieren eines Library-Items wird automatisch ein PNG-Thumbnail vom Item generiert (Top-View, 128x128) und in library/previews/<id>.png abgelegt. Frontend rendert die Previews als Base64-Data-URIs (sicher gegen WebKit-file://-Restriktionen). library.py: - _previews_dir(): legt previews/-Folder an - _preview_rel_for(asset_rel): predictable PNG-Pfad pro Item - _capture_thumbnail_of_objects(): hided andere Objekte temporaer, switcht auf Top-Parallel, ZoomBoundingBox, CaptureToBitmap → PNG, restored Viewport + Hidden-State - read_preview_data_uri(): liest PNG + encoded als data:image/png;base64 - Hook in convert_to_3dm_via_import (vor Cleanup) + save_selection_to_asset rhinopanel.py: - _enrich_library_items_with_previews(): haengt previewDataUri an jedes Item das ein preview-Feld hat - Initial-Params + _send_library + ElementeBridge._cmd_list_library liefern angereicherte Items - _add_library_file + _save_selection_as_library setzen preview-Pfad im Item wenn Thumbnail-Datei existiert Frontend: - SymbolPicker.ItemPreview: rendert <div backgroundImage> mit Base64-URI wenn vorhanden, sonst Icon-Fallback - ProjectSettingsDialog Symbole-Tab: List-Row + Detail-Identity zeigen Thumbnail (32px in Liste, 56px im Detail), Icon nur als Fallback Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+3
-2
@@ -8096,9 +8096,10 @@ class ElementeBridge(panel_base.BaseBridge):
|
|||||||
Library-Items + Handling von PICK (User waehlt Item → CREATE_SYMBOL
|
Library-Items + Handling von PICK (User waehlt Item → CREATE_SYMBOL
|
||||||
im aktiven Doc) und CANCEL (Fenster schliessen)."""
|
im aktiven Doc) und CANCEL (Fenster schliessen)."""
|
||||||
try:
|
try:
|
||||||
import library
|
import library, rhinopanel
|
||||||
manifest = library.load_manifest()
|
manifest = library.load_manifest()
|
||||||
items = manifest.get("items", [])
|
items = rhinopanel._enrich_library_items_with_previews(
|
||||||
|
manifest.get("items", []))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[ELEMENTE] list library:", ex)
|
print("[ELEMENTE] list library:", ex)
|
||||||
items = []
|
items = []
|
||||||
|
|||||||
@@ -210,6 +210,137 @@ def add_item(item):
|
|||||||
return ok, load_manifest()
|
return ok, load_manifest()
|
||||||
|
|
||||||
|
|
||||||
|
def _previews_dir():
|
||||||
|
"""Pfad zum previews/-Subfolder. Wird angelegt falls fehlt."""
|
||||||
|
p = os.path.join(library_root(), "previews")
|
||||||
|
if not os.path.isdir(p):
|
||||||
|
try: os.makedirs(p)
|
||||||
|
except Exception: pass
|
||||||
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
def _preview_rel_for(asset_rel_or_id):
|
||||||
|
"""Erzeugt einen relativen Preview-Pfad fuer ein Asset (oder Item-ID).
|
||||||
|
Liefert z.B. 'previews/<name>.png'. Wir benutzen den Stamm des
|
||||||
|
.3dm-Files damit Asset + Preview gleichen Namen haben (debuggbar)."""
|
||||||
|
stem = asset_rel_or_id
|
||||||
|
if "/" in stem: stem = stem.split("/")[-1]
|
||||||
|
if "\\" in stem: stem = stem.split("\\")[-1]
|
||||||
|
if stem.lower().endswith(".3dm"): stem = stem[:-4]
|
||||||
|
safe = "".join(c if (c.isalnum() or c in "-_") else "_" for c in stem)
|
||||||
|
return "previews/" + safe + ".png"
|
||||||
|
|
||||||
|
|
||||||
|
def _capture_thumbnail_of_objects(target_objects, png_abs_path, size=128):
|
||||||
|
"""Captured einen Top-View der gegebenen Objekte als PNG. Hided
|
||||||
|
temporaer alle anderen Objekte damit der Background sauber ist.
|
||||||
|
Returns True/False."""
|
||||||
|
if Rhino is None: return False
|
||||||
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
|
if doc is None or not target_objects: return False
|
||||||
|
try:
|
||||||
|
from System.Drawing import Size
|
||||||
|
import scriptcontext as sc
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
# IDs der Ziel-Objekte
|
||||||
|
target_ids = set()
|
||||||
|
for o in target_objects:
|
||||||
|
try:
|
||||||
|
if not o.IsDeleted: target_ids.add(str(o.Id))
|
||||||
|
except Exception: pass
|
||||||
|
if not target_ids: return False
|
||||||
|
# Andere Objekte temporaer ausblenden
|
||||||
|
hidden_by_us = []
|
||||||
|
try:
|
||||||
|
for o in list(doc.Objects):
|
||||||
|
try:
|
||||||
|
if o.IsDeleted: continue
|
||||||
|
if str(o.Id) in target_ids: continue
|
||||||
|
if o.IsHidden: continue
|
||||||
|
if not o.IsNormal: continue # bereits hidden/locked → skip
|
||||||
|
doc.Objects.Hide(o.Id, True)
|
||||||
|
hidden_by_us.append(o.Id)
|
||||||
|
except Exception: pass
|
||||||
|
except Exception: pass
|
||||||
|
capture_ok = False
|
||||||
|
try:
|
||||||
|
view = doc.Views.ActiveView
|
||||||
|
if view is None:
|
||||||
|
return False
|
||||||
|
vp = view.ActiveViewport
|
||||||
|
# Viewport-State sichern damit User nichts verliert
|
||||||
|
saved_target = vp.CameraTarget
|
||||||
|
saved_loc = vp.CameraLocation
|
||||||
|
saved_proj = vp.IsParallelProjection
|
||||||
|
try:
|
||||||
|
# Auf Top-Parallel wechseln + Zoom auf Ziel
|
||||||
|
vp.SetProjection(Rhino.Display.DefinedViewportProjection.Top, "Top", True)
|
||||||
|
try:
|
||||||
|
bbox = Rhino.Geometry.BoundingBox.Empty
|
||||||
|
for o in target_objects:
|
||||||
|
g = o.Geometry
|
||||||
|
if g is None: continue
|
||||||
|
try:
|
||||||
|
bb = g.GetBoundingBox(True)
|
||||||
|
if bb.IsValid: bbox.Union(bb)
|
||||||
|
except Exception: pass
|
||||||
|
if bbox.IsValid:
|
||||||
|
# Etwas Padding
|
||||||
|
bbox.Inflate(bbox.Diagonal.Length * 0.1)
|
||||||
|
vp.ZoomBoundingBox(bbox)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[LIBRARY] thumbnail zoom:", ex)
|
||||||
|
view.Redraw()
|
||||||
|
# Capture
|
||||||
|
try:
|
||||||
|
bmp = view.CaptureToBitmap(Size(int(size), int(size)))
|
||||||
|
if bmp is not None:
|
||||||
|
# Sicherstellen dass Verzeichnis da ist
|
||||||
|
try:
|
||||||
|
d = os.path.dirname(png_abs_path)
|
||||||
|
if d and not os.path.isdir(d): os.makedirs(d)
|
||||||
|
except Exception: pass
|
||||||
|
bmp.Save(png_abs_path)
|
||||||
|
capture_ok = True
|
||||||
|
except Exception as ex:
|
||||||
|
print("[LIBRARY] thumbnail capture:", ex)
|
||||||
|
finally:
|
||||||
|
# Viewport wiederherstellen
|
||||||
|
try:
|
||||||
|
vp.SetCameraLocation(saved_loc, False)
|
||||||
|
vp.SetCameraTarget(saved_target, True)
|
||||||
|
if saved_proj:
|
||||||
|
vp.IsParallelProjection = True
|
||||||
|
else:
|
||||||
|
vp.IsParallelProjection = False
|
||||||
|
except Exception: pass
|
||||||
|
finally:
|
||||||
|
# Hidden Objekte wieder einblenden
|
||||||
|
for gid in hidden_by_us:
|
||||||
|
try: doc.Objects.Show(gid, True)
|
||||||
|
except Exception: pass
|
||||||
|
try: doc.Views.Redraw()
|
||||||
|
except Exception: pass
|
||||||
|
return capture_ok
|
||||||
|
|
||||||
|
|
||||||
|
def read_preview_data_uri(rel_path):
|
||||||
|
"""Liest die PNG-Vorschau als data:image/png;base64-URI fuer
|
||||||
|
direktes Einsetzen in <img src='...'>. Liefert None wenn Datei fehlt."""
|
||||||
|
if not rel_path: return None
|
||||||
|
abs_p = os.path.join(library_root(), rel_path)
|
||||||
|
if not os.path.isfile(abs_p): return None
|
||||||
|
try:
|
||||||
|
import base64
|
||||||
|
with open(abs_p, "rb") as f:
|
||||||
|
data = f.read()
|
||||||
|
return "data:image/png;base64," + base64.b64encode(data).decode("ascii")
|
||||||
|
except Exception as ex:
|
||||||
|
print("[LIBRARY] read_preview:", ex)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def convert_to_3dm_via_import(src_path, target_name):
|
def convert_to_3dm_via_import(src_path, target_name):
|
||||||
"""Konvertiert eine beliebige CAD-Datei (.dwg/.obj/.fbx/.dae/.stl/...)
|
"""Konvertiert eine beliebige CAD-Datei (.dwg/.obj/.fbx/.dae/.stl/...)
|
||||||
nach .3dm. Strategie: Rhinos _-Import in den aktiven Doc, dann die
|
nach .3dm. Strategie: Rhinos _-Import in den aktiven Doc, dann die
|
||||||
@@ -321,6 +452,14 @@ def convert_to_3dm_via_import(src_path, target_name):
|
|||||||
print("[LIBRARY] convert_to_3dm File3dm:", ex)
|
print("[LIBRARY] convert_to_3dm File3dm:", ex)
|
||||||
return None
|
return None
|
||||||
finally:
|
finally:
|
||||||
|
# Thumbnail-Capture BEVOR die Objekte geloescht werden.
|
||||||
|
try:
|
||||||
|
rel_for_preview = os.path.relpath(target, library_root())
|
||||||
|
preview_rel = _preview_rel_for(rel_for_preview)
|
||||||
|
preview_abs = os.path.join(library_root(), preview_rel)
|
||||||
|
_capture_thumbnail_of_objects(new_objs, preview_abs)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[LIBRARY] convert thumbnail:", ex)
|
||||||
# Cleanup: importierte Objekte wieder loeschen
|
# Cleanup: importierte Objekte wieder loeschen
|
||||||
for o in new_objs:
|
for o in new_objs:
|
||||||
try: doc.Objects.Delete(o.Id, True)
|
try: doc.Objects.Delete(o.Id, True)
|
||||||
@@ -419,6 +558,13 @@ def save_selection_to_asset(doc, target_name):
|
|||||||
print("[LIBRARY] save_selection write:", ex)
|
print("[LIBRARY] save_selection write:", ex)
|
||||||
return None
|
return None
|
||||||
rel = os.path.relpath(target, library_root())
|
rel = os.path.relpath(target, library_root())
|
||||||
|
# Thumbnail aus den (noch selektierten) Objekten capturen
|
||||||
|
try:
|
||||||
|
preview_rel = _preview_rel_for(rel)
|
||||||
|
preview_abs = os.path.join(library_root(), preview_rel)
|
||||||
|
_capture_thumbnail_of_objects(sel, preview_abs)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[LIBRARY] save_selection thumbnail:", ex)
|
||||||
print("[LIBRARY] save_selection: {} objs → {}".format(len(geoms), rel))
|
print("[LIBRARY] save_selection: {} objs → {}".format(len(geoms), rel))
|
||||||
return rel
|
return rel
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
|||||||
+38
-8
@@ -147,6 +147,24 @@ def _list_hatch_patterns_full(doc):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _enrich_library_items_with_previews(items):
|
||||||
|
"""Liefert Liste mit zusaetzlichem 'previewDataUri'-Feld pro Item
|
||||||
|
(base64-PNG falls preview-Datei existiert). Items werden NICHT
|
||||||
|
persistiert — nur fuer den Transport ans Frontend angereichert."""
|
||||||
|
import library
|
||||||
|
out = []
|
||||||
|
for it in (items or []):
|
||||||
|
if not isinstance(it, dict):
|
||||||
|
out.append(it); continue
|
||||||
|
enriched = dict(it)
|
||||||
|
prev = it.get("preview")
|
||||||
|
if prev:
|
||||||
|
uri = library.read_preview_data_uri(prev)
|
||||||
|
if uri: enriched["previewDataUri"] = uri
|
||||||
|
out.append(enriched)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
def _list_linetypes_full(doc):
|
def _list_linetypes_full(doc):
|
||||||
"""Vollstaendiges Linetype-Listing fuer die Verwaltungs-UI.
|
"""Vollstaendiges Linetype-Listing fuer die Verwaltungs-UI.
|
||||||
Mac Rhino 8 GetSegment(i) returnt (length: float, isLine: bool):
|
Mac Rhino 8 GetSegment(i) returnt (length: float, isLine: bool):
|
||||||
@@ -893,7 +911,8 @@ class EbenenBridge(panel_base.BaseBridge):
|
|||||||
try:
|
try:
|
||||||
import library
|
import library
|
||||||
lib_manifest = library.load_manifest()
|
lib_manifest = library.load_manifest()
|
||||||
lib_items = lib_manifest.get("items", [])
|
lib_items = _enrich_library_items_with_previews(
|
||||||
|
lib_manifest.get("items", []))
|
||||||
lib_root = library.library_root()
|
lib_root = library.library_root()
|
||||||
except Exception:
|
except Exception:
|
||||||
lib_items = []; lib_root = ""
|
lib_items = []; lib_root = ""
|
||||||
@@ -1183,12 +1202,15 @@ class EbenenBridge(panel_base.BaseBridge):
|
|||||||
# ---- Library-Item CRUD ----
|
# ---- Library-Item CRUD ----
|
||||||
def _send_library(self):
|
def _send_library(self):
|
||||||
"""Sendet aktuelle Library-Items ans Frontend
|
"""Sendet aktuelle Library-Items ans Frontend
|
||||||
(LIBRARY_ITEMS-Message)."""
|
(LIBRARY_ITEMS-Message). Items kommen mit
|
||||||
|
previewDataUri (base64 PNG) wenn Vorschau vorhanden."""
|
||||||
try:
|
try:
|
||||||
import library
|
import library
|
||||||
m = library.load_manifest()
|
m = library.load_manifest()
|
||||||
|
enriched = _enrich_library_items_with_previews(
|
||||||
|
m.get("items", []))
|
||||||
self.send("LIBRARY_ITEMS", {
|
self.send("LIBRARY_ITEMS", {
|
||||||
"items": m.get("items", []),
|
"items": enriched,
|
||||||
"libraryRoot": library.library_root(),
|
"libraryRoot": library.library_root(),
|
||||||
})
|
})
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@@ -1247,18 +1269,21 @@ class EbenenBridge(panel_base.BaseBridge):
|
|||||||
self.send("LIBRARY_ERROR", {
|
self.send("LIBRARY_ERROR", {
|
||||||
"msg": "Konnte Datei nicht importieren — siehe Log."})
|
"msg": "Konnte Datei nicht importieren — siehe Log."})
|
||||||
return
|
return
|
||||||
|
# Preview-Pfad falls Thumbnail erzeugt wurde
|
||||||
|
preview_rel = library._preview_rel_for(rel)
|
||||||
|
preview_abs = os.path.join(library.library_root(), preview_rel)
|
||||||
|
has_preview = os.path.isfile(preview_abs)
|
||||||
if target_id:
|
if target_id:
|
||||||
# Bestehendes Item updaten
|
|
||||||
m = library.load_manifest()
|
m = library.load_manifest()
|
||||||
for it in m.get("items", []):
|
for it in m.get("items", []):
|
||||||
if it.get("id") == target_id:
|
if it.get("id") == target_id:
|
||||||
key = "files" + variant
|
key = "files" + variant
|
||||||
it[key] = [rel]
|
it[key] = [rel]
|
||||||
if not it.get("type"): it["type"] = item_type
|
if not it.get("type"): it["type"] = item_type
|
||||||
|
if has_preview: it["preview"] = preview_rel
|
||||||
break
|
break
|
||||||
library.save_manifest(m)
|
library.save_manifest(m)
|
||||||
else:
|
else:
|
||||||
# Neues Item — stem aus dem Pfad oben bereits berechnet
|
|
||||||
import uuid as _uuid
|
import uuid as _uuid
|
||||||
new_id = "obj-" + _uuid.uuid4().hex[:10]
|
new_id = "obj-" + _uuid.uuid4().hex[:10]
|
||||||
item = {
|
item = {
|
||||||
@@ -1269,6 +1294,7 @@ class EbenenBridge(panel_base.BaseBridge):
|
|||||||
"tags": [],
|
"tags": [],
|
||||||
"files" + variant: [rel],
|
"files" + variant: [rel],
|
||||||
}
|
}
|
||||||
|
if has_preview: item["preview"] = preview_rel
|
||||||
library.add_item(item)
|
library.add_item(item)
|
||||||
self._send_library()
|
self._send_library()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@@ -1343,10 +1369,13 @@ class EbenenBridge(panel_base.BaseBridge):
|
|||||||
self.send("LIBRARY_ERROR", {
|
self.send("LIBRARY_ERROR", {
|
||||||
"msg": "Konnte Selection nicht speichern."})
|
"msg": "Konnte Selection nicht speichern."})
|
||||||
return
|
return
|
||||||
|
preview_rel = library._preview_rel_for(rel)
|
||||||
|
preview_abs = os.path.join(library.library_root(), preview_rel)
|
||||||
|
has_preview = os.path.isfile(preview_abs)
|
||||||
if target_id:
|
if target_id:
|
||||||
library.update_item(target_id, {
|
patch = {("files" + variant): [rel]}
|
||||||
("files" + variant): [rel],
|
if has_preview: patch["preview"] = preview_rel
|
||||||
})
|
library.update_item(target_id, patch)
|
||||||
else:
|
else:
|
||||||
import uuid as _uuid
|
import uuid as _uuid
|
||||||
new_id = "obj-" + _uuid.uuid4().hex[:10]
|
new_id = "obj-" + _uuid.uuid4().hex[:10]
|
||||||
@@ -1358,6 +1387,7 @@ class EbenenBridge(panel_base.BaseBridge):
|
|||||||
"tags": [],
|
"tags": [],
|
||||||
("files" + variant): [rel],
|
("files" + variant): [rel],
|
||||||
}
|
}
|
||||||
|
if has_preview: item["preview"] = preview_rel
|
||||||
library.add_item(item)
|
library.add_item(item)
|
||||||
self._send_library()
|
self._send_library()
|
||||||
b = _ProjectSettingsBridge()
|
b = _ProjectSettingsBridge()
|
||||||
|
|||||||
@@ -1115,8 +1115,8 @@ export default function ProjectSettingsDialog({
|
|||||||
onClick={() => setSelLib(it.id)}
|
onClick={() => setSelLib(it.id)}
|
||||||
style={{
|
style={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: '20px 1fr',
|
gridTemplateColumns: '32px 1fr',
|
||||||
alignItems: 'center', gap: 6,
|
alignItems: 'center', gap: 8,
|
||||||
padding: '5px 10px',
|
padding: '5px 10px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
background: isSel ? 'var(--accent-dim)' : 'transparent',
|
background: isSel ? 'var(--accent-dim)' : 'transparent',
|
||||||
@@ -1129,9 +1129,21 @@ export default function ProjectSettingsDialog({
|
|||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
if (!isSel) e.currentTarget.style.background = 'transparent'
|
if (!isSel) e.currentTarget.style.background = 'transparent'
|
||||||
}}>
|
}}>
|
||||||
|
<div style={{
|
||||||
|
width: 32, height: 32, borderRadius: 4,
|
||||||
|
background: it.previewDataUri
|
||||||
|
? `url("${it.previewDataUri}") center/contain no-repeat, var(--bg-input)`
|
||||||
|
: 'var(--bg-input)',
|
||||||
|
border: '1px solid var(--border-light)',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
flexShrink: 0,
|
||||||
|
}}>
|
||||||
|
{!it.previewDataUri && (
|
||||||
<Icon name={it.type === 'symbol' ? 'navigation' : 'forest'}
|
<Icon name={it.type === 'symbol' ? 'navigation' : 'forest'}
|
||||||
size={13}
|
size={14}
|
||||||
style={{ color: 'var(--text-muted)' }} />
|
style={{ color: 'var(--text-muted)' }} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div style={{ minWidth: 0 }}>
|
<div style={{ minWidth: 0 }}>
|
||||||
<div style={{ fontSize: 11,
|
<div style={{ fontSize: 11,
|
||||||
color: 'var(--text-primary)',
|
color: 'var(--text-primary)',
|
||||||
@@ -1177,10 +1189,22 @@ export default function ProjectSettingsDialog({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DetailSection title="Identität">
|
<DetailSection title="Identität">
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||||
|
<div style={{
|
||||||
|
width: 56, height: 56, borderRadius: 6,
|
||||||
|
background: it.previewDataUri
|
||||||
|
? `url("${it.previewDataUri}") center/contain no-repeat, var(--bg-input)`
|
||||||
|
: 'var(--bg-input)',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
flexShrink: 0,
|
||||||
|
}}>
|
||||||
|
{!it.previewDataUri && (
|
||||||
<Icon name={it.type === 'symbol' ? 'navigation' : 'forest'}
|
<Icon name={it.type === 'symbol' ? 'navigation' : 'forest'}
|
||||||
size={28}
|
size={22}
|
||||||
style={{ color: 'var(--accent)' }} />
|
style={{ color: 'var(--accent)' }} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<input type="text" value={it.name || ''}
|
<input type="text" value={it.name || ''}
|
||||||
onChange={(ev) => {
|
onChange={(ev) => {
|
||||||
const nm = ev.target.value
|
const nm = ev.target.value
|
||||||
|
|||||||
@@ -6,17 +6,29 @@ import { BarToggle, BarButton, BAR_H } from './BarControls'
|
|||||||
Grid. Klick auf Item → onPick(id). Schliesst via onClose. */
|
Grid. Klick auf Item → onPick(id). Schliesst via onClose. */
|
||||||
|
|
||||||
function ItemPreview({ item }) {
|
function ItemPreview({ item }) {
|
||||||
// Vorschau: type-spezifisches Icon im Center auf bg-input.
|
// Thumbnail wenn vorhanden, sonst type-spezifisches Icon
|
||||||
// Spaeter: PNG-Thumbnail aus library/previews/.
|
if (item.previewDataUri) {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
height: 80,
|
||||||
|
background: 'var(--bg-input)',
|
||||||
|
borderBottom: '1px solid var(--border-light)',
|
||||||
|
backgroundImage: `url("${item.previewDataUri}")`,
|
||||||
|
backgroundSize: 'contain',
|
||||||
|
backgroundPosition: 'center',
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
}} />
|
||||||
|
)
|
||||||
|
}
|
||||||
const iconName = item.type === 'symbol' ? 'navigation' : 'forest'
|
const iconName = item.type === 'symbol' ? 'navigation' : 'forest'
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
height: 56,
|
height: 80,
|
||||||
background: 'var(--bg-input)',
|
background: 'var(--bg-input)',
|
||||||
borderBottom: '1px solid var(--border-light)',
|
borderBottom: '1px solid var(--border-light)',
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
}}>
|
}}>
|
||||||
<Icon name={iconName} size={24}
|
<Icon name={iconName} size={28}
|
||||||
style={{ color: 'var(--text-muted)' }} />
|
style={{ color: 'var(--text-muted)' }} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user