// ==UserScript== // @name 鸿蒙资源日记 // @namespace harmony-resource-diary // @version 1.0.0 // @description 鸿蒙浏览器原生资源捕获工具 // @author YC // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjMEE4NEZGIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjNUJDMEJFIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNiIgZmlsbD0idXJsKCNnKSIvPjxwYXRoIGQ9Ik0zLjUgNy41TDEyIDMuNUwyMC41IDcuNUwxMiAxMS41WiIgZmlsbD0id2hpdGUiIGZpbGwtb3BhY2l0eT0iMC45IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zLjUgNy41TDEyIDExLjVWMjAuNUwzLjUgMTYuNVoiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuNiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjAuNSA3LjVMMTIgMTEuNVYyMC41TDIwLjUgMTYuNVoiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuNiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4= // @match *://*/* // @grant GM_download // @run-at document-end // @license MIT // ==/UserScript== (function () { 'use strict'; if (!/^https?:$/.test(location.protocol)) return; if (window.top !== window.self) return; if (window.__HRD_LOADED__) return; window.__HRD_LOADED__ = true; const Icons = { appIcon: ``, cube3d: ``, close: ``, check: ``, wrapText: ``, noWrapText: ``, image: ``, media: ``, doc: ``, video: ``, audio: ``, code: ``, font: ``, json: ``, link: ``, copy: ``, play: ``, download: ``, sortTimeDesc: ``, sortTimeAsc: ``, sortNameAsc: ``, sortNameDesc: ``, listView: ``, gridView: `` }; const CONFIG = { MAX_RESOURCES: 800, MAX_URL_CACHE: 3000, BATCH_SIZE: 80, FULL_SCAN_INTERVAL: 5000, SHEET_TOP_GAP: 8, SWIPE_MIN_DX: 40, SWIPE_MAX_DY: 120, SWIPE_MAX_TIME: 1000, MAX_SOURCE_LINES: 20000, SOURCE_CHUNK: 500 }; const DANGEROUS_PROTOCOLS = [ 'javascript:', 'vbscript:', 'about:', 'file:', 'ftp:', 'telnet:', 'ssh:' ]; const INVALID_URL_PATTERNS = [ /(?:^|[/_.\-])(?:1x1|pixel|spacer|blank|empty|transparent|clear|tracking|track|beacon|ping)[._\-]?(?:gif|png|jpg|jpeg|webp)(?:[?#]|$)/i, /google-analytics\.com/i, /googletagmanager\.com/i, /doubleclick\.net/i, /googleadservices\.com/i, /googlesyndication\.com/i, /facebook\.com\/tr/i, /connect\.facebook\.net/i, /hotjar\.com/i, /mixpanel\.com/i, /segment\.(io|com)/i, /amplitude\.com/i, /sentry\.io/i, /bugsnag\.com/i, /newrelic\.com/i, /\.clarity\.ms/i, /matomo\.(org|cloud)/i, /plausible\.io/i, /umami\.is/i, /baidu\.com\/hm\.js/i, /cnzz\.com/i, /hm\.baidu\.com/i, /^data:image\/svg\+xml.*?(?:width=['"]?1(?:px)?['"]?|height=['"]?1(?:px)?['"]?)/i ]; const MIN_THUMB_SIZE = 16; const EXT_TO_TYPE = { mp4: 'mp4', webm: 'webm', ogg: 'ogg', ogv: 'ogv', mov: 'mov', avi: 'avi', mkv: 'mkv', flv: 'flv', m3u8: 'm3u8', mpd: 'mpd', ts: 'ts', m4v: 'm4v', '3gp': '3gp', wmv: 'wmv', m2ts: 'ts', mp3: 'mp3', wav: 'wav', flac: 'flac', aac: 'aac', m4a: 'm4a', wma: 'wma', oga: 'oga', weba: 'weba', opus: 'opus', aiff: 'aiff', mid: 'mid', midi: 'mid', ac3: 'ac3', amr: 'amr', jpg: 'jpg', jpeg: 'jpg', png: 'png', gif: 'gif', webp: 'webp', svg: 'svg', ico: 'ico', bmp: 'bmp', avif: 'avif', jxl: 'jxl', css: 'stylesheet', js: 'script', mjs: 'script', json: 'json', xml: 'xml', woff: 'font', woff2: 'font', ttf: 'font', otf: 'font', eot: 'font' }; const MIME_TO_TYPE = { 'video/mp4': 'mp4', 'video/webm': 'webm', 'video/ogg': 'ogg', 'video/quicktime': 'mov', 'video/x-matroska': 'mkv', 'video/x-flv': 'flv', 'video/x-msvideo': 'avi', 'video/3gpp': '3gp', 'video/x-m4v': 'm4v', 'application/vnd.apple.mpegurl': 'm3u8', 'application/x-mpegURL': 'm3u8', 'application/dash+xml': 'mpd', 'video/mp2t': 'ts', 'audio/mpeg': 'mp3', 'audio/wav': 'wav', 'audio/flac': 'flac', 'audio/aac': 'aac', 'audio/ogg': 'oga', 'audio/webm': 'weba', 'audio/opus': 'opus', 'audio/x-ms-wma': 'wma', 'audio/midi': 'mid', 'audio/x-m4a': 'm4a', 'image/jpeg': 'jpg', 'image/png': 'png', 'image/gif': 'gif', 'image/webp': 'webp', 'image/svg+xml': 'svg', 'image/avif': 'avif', 'image/bmp': 'bmp', 'image/x-icon': 'ico', 'image/tiff': 'bmp', 'text/css': 'stylesheet', 'application/javascript': 'script', 'text/javascript': 'script', 'application/json': 'json', 'application/xml': 'xml' }; const TYPE_ICON_MAP = { mp4: 'video', webm: 'video', ogg: 'video', ogv: 'video', mov: 'video', avi: 'video', mkv: 'video', flv: 'video', m3u8: 'video', mpd: 'video', ts: 'video', m4v: 'video', '3gp': 'video', wmv: 'video', mp3: 'audio', wav: 'audio', flac: 'audio', aac: 'audio', m4a: 'audio', wma: 'audio', oga: 'audio', weba: 'audio', opus: 'audio', audio: 'audio', aiff: 'audio', mid: 'audio', ac3: 'audio', amr: 'audio', jpg: 'image', jpeg: 'image', png: 'image', gif: 'image', webp: 'image', svg: 'image', ico: 'image', bmp: 'image', avif: 'image', jxl: 'image', image: 'image', script: 'code', js: 'code', mjs: 'code', stylesheet: 'code', css: 'code', xml: 'code', json: 'json', font: 'font', woff: 'font', woff2: 'font', ttf: 'font', otf: 'font', xhr: 'link', fetch: 'link', datauri: 'link', other: 'doc' }; const TYPE_TAG_MAP = { mp4: 'MP4', webm: 'WEBM', ogg: 'OGG', ogv: 'OGV', mov: 'MOV', avi: 'AVI', mkv: 'MKV', flv: 'FLV', m3u8: 'M3U8', mpd: 'MPD', ts: 'TS', m4v: 'M4V', '3gp': '3GP', wmv: 'WMV', m2ts: 'TS', mp3: 'MP3', wav: 'WAV', flac: 'FLAC', aac: 'AAC', m4a: 'M4A', wma: 'WMA', oga: 'OGA', weba: 'WEBA', opus: 'OPUS', aiff: 'AIFF', mid: 'MIDI', ac3: 'AC3', amr: 'AMR', jpg: 'JPG', jpeg: 'JPG', png: 'PNG', gif: 'GIF', webp: 'WEBP', svg: 'SVG', ico: 'ICO', bmp: 'BMP', avif: 'AVIF', jxl: 'JXL', css: 'CSS', js: 'JS', mjs: 'JS', json: 'JSON', xml: 'XML', script: 'JS', stylesheet: 'CSS', woff: 'FONT', woff2: 'FONT', ttf: 'FONT', otf: 'FONT', eot: 'FONT', font: 'FONT', xhr: 'XHR', fetch: 'FETCH', datauri: 'DATA', other: 'FILE', image: 'IMG', audio: 'AUDIO', video: 'VIDEO' }; const SORT_MODES = [ { key: 'name-asc', label: '名称 A→Z', icon: 'sortNameAsc' }, { key: 'name-desc', label: '名称 Z→A', icon: 'sortNameDesc' }, { key: 'time-desc', label: '最新优先', icon: 'sortTimeDesc' }, { key: 'time-asc', label: '最旧优先', icon: 'sortTimeAsc' } ]; const FILTER_ORDER = ['image', 'media', 'other']; const Security = { _cache: new Map(), _invalidCache: new Map(), isSafe(url) { if (!url || typeof url !== 'string') return false; const c = this._cache.get(url); if (c !== undefined) return c; if (this._cache.size > 1000) { const arr = Array.from(this._cache.entries()).slice(-500); this._cache.clear(); arr.forEach(([k, v]) => this._cache.set(k, v)); } const lower = url.toLowerCase().trim(); for (const p of DANGEROUS_PROTOCOLS) { if (lower.startsWith(p)) { this._cache.set(url, false); return false; } } if (lower.startsWith('data:')) { const mime = lower.split(',')[0].split(':')[1]?.split(';')[0] || ''; const safe = mime.startsWith('image/') || mime.startsWith('audio/') || mime.startsWith('video/') || mime.startsWith('text/plain') || mime.startsWith('application/json'); this._cache.set(url, safe); return safe; } this._cache.set(url, true); return true; }, isInvalid(url) { if (!url || typeof url !== 'string') return true; const c = this._invalidCache.get(url); if (c !== undefined) return c; let invalid = false; for (const re of INVALID_URL_PATTERNS) { if (re.test(url)) { invalid = true; break; } } this._invalidCache.set(url, invalid); return invalid; }, key(url) { if (!url || typeof url !== 'string') return null; const s = url.trim(); if (!s || s.length > 65536) return null; if (s.toLowerCase().startsWith('data:')) { return this.isSafe(s) ? 'data:' + s.slice(0, 220) + '#len' + s.length : null; } try { const u = new URL(s, location.href); u.hash = ''; if (u.protocol === 'http:' && u.port === '80') u.port = ''; if (u.protocol === 'https:' && u.port === '443') u.port = ''; return u.toString(); } catch { return null; } }, filename(url) { try { if (url.startsWith('data:')) { const ext = url.match(/data:image\/(\w+)/)?.[1] || 'bin'; return `datauri_${Date.now()}.${ext}`; } const u = new URL(url, location.href); const path = decodeURIComponent(u.pathname); const name = path.split('/').pop() || 'download'; return name.replace(/[<>:"/\\|?*\x00-\x1f]/g, '_').slice(0, 255) || 'download'; } catch { return `download_${Date.now()}`; } }, displayType(url, ct) { if (ct) { const mt = ct.split(';')[0].toLowerCase(); if (MIME_TO_TYPE[mt]) return MIME_TO_TYPE[mt]; if (mt.includes('video')) return 'mp4'; if (mt.includes('audio')) return 'mp3'; if (mt.includes('image')) return 'image'; if (mt.includes('script')) return 'script'; if (mt.includes('css')) return 'stylesheet'; if (mt.includes('font')) return 'font'; } try { const u = new URL(url, location.href); const ext = u.pathname.split('.').pop()?.toLowerCase() || ''; if (EXT_TO_TYPE[ext]) return EXT_TO_TYPE[ext]; } catch {} return 'other'; }, filterType(dt) { const t = (dt || '').toLowerCase(); const media = ['mp4', 'webm', 'ogg', 'ogv', 'mov', 'avi', 'mkv', 'flv', 'm3u8', 'mpd', 'ts', 'm4v', '3gp', 'wmv', 'mp3', 'wav', 'flac', 'aac', 'm4a', 'wma', 'oga', 'weba', 'opus', 'audio', 'aiff', 'mid', 'ac3', 'amr']; const image = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'ico', 'bmp', 'avif', 'jxl', 'image']; if (media.includes(t)) return 'media'; if (image.includes(t)) return 'image'; return 'other'; } }; function buildHighlightedLines(html) { const lines = [[]]; let currentLine = 0; function push(cls, text) { if (!text) return; const parts = text.split('\n'); for (let i = 0; i < parts.length; i++) { if (i > 0) { currentLine++; lines.push([]); } if (parts[i].length > 0) { lines[currentLine].push({ cls, text: parts[i] }); } } } function pushTag(tag) { const openMatch = tag.match(/^(<\/?)([a-zA-Z][a-zA-Z0-9\-:]*)/); if (!openMatch) { push('hl-tag', tag); return; } push('hl-punct', openMatch[1]); push('hl-tag-name', openMatch[2]); let rest = tag.slice(openMatch[0].length); let closeStr = ''; if (rest.endsWith('/>')) { closeStr = '/>'; rest = rest.slice(0, -2); } else if (rest.endsWith('>')) { closeStr = '>'; rest = rest.slice(0, -1); } const attrRe = /(\s+)([a-zA-Z_:][a-zA-Z0-9_:.\-]*)(\s*=\s*)?("[^"]*"|'[^']*'|[^\s>]+)?/g; let lastIdx = 0; let m; while ((m = attrRe.exec(rest)) !== null) { if (m.index > lastIdx) { push('hl-text', rest.slice(lastIdx, m.index)); } push('hl-text', m[1]); push('hl-attr-name', m[2]); if (m[3]) push('hl-punct', m[3]); if (m[4]) push('hl-attr-value', m[4]); lastIdx = attrRe.lastIndex; } if (lastIdx < rest.length) { push('hl-text', rest.slice(lastIdx)); } push('hl-punct', closeStr); } const re = /()|(]*>)|(<\/?[a-zA-Z][^>]*\/?>)|([^<]+)/gi; let lastIdx = 0; let m; while ((m = re.exec(html)) !== null) { if (m.index > lastIdx) { push('hl-text', html.slice(lastIdx, m.index)); } if (m[1]) push('hl-comment', m[1]); else if (m[2]) push('hl-doctype', m[2]); else if (m[3]) pushTag(m[3]); else if (m[4]) push('hl-text', m[4]); lastIdx = re.lastIndex; } if (lastIdx < html.length) { push('hl-text', html.slice(lastIdx)); } return lines; } function renderSourceLinesChunked(container, lines) { const total = lines.length; const limit = Math.min(total, CONFIG.MAX_SOURCE_LINES); let i = 0; function buildChunk() { const end = Math.min(i + CONFIG.SOURCE_CHUNK, limit); const frag = document.createDocumentFragment(); for (; i < end; i++) { const segs = lines[i]; const lineDiv = document.createElement('div'); lineDiv.className = 'src-line'; const lineno = document.createElement('span'); lineno.className = 'src-lineno'; lineno.textContent = String(i + 1); lineDiv.appendChild(lineno); const content = document.createElement('span'); content.className = 'src-line-content'; if (segs.length === 0) { content.appendChild(document.createTextNode('')); } else { for (let k = 0; k < segs.length; k++) { const seg = segs[k]; const span = document.createElement('span'); span.className = seg.cls; span.textContent = seg.text; content.appendChild(span); } } lineDiv.appendChild(content); frag.appendChild(lineDiv); } container.appendChild(frag); if (i < limit) { requestAnimationFrame(buildChunk); } else if (total > limit) { const more = document.createElement('div'); more.className = 'src-more'; more.textContent = `... 仅显示前 ${limit} 行,共 ${total} 行`; container.appendChild(more); } } buildChunk(); } const Store = { resources: [], urlIndex: new Map(), processed: new Set(), sortMode: 'name-asc', viewMode: 'list', currentFilter: 'image', add(url, ct, it) { if (Security.isInvalid(url)) return false; const k = Security.key(url); if (!k) return false; const dt = Security.displayType(url, ct); if (dt === 'other') { try { const u = new URL(url, location.href); if (!/\.\w{2,5}$/.test(u.pathname)) return false; } catch { return false; } } if (this.processed.has(k)) { const ex = this.urlIndex.get(k); if (ex && ct && !ex.contentType) { ex.contentType = ct; ex.displayType = Security.displayType(ex.url, ct); ex.filterType = Security.filterType(ex.displayType); } return false; } this.processed.add(k); if (this.processed.size > CONFIG.MAX_URL_CACHE) { const iter = this.processed.values(); for (let i = 0; i < 500; i++) this.processed.delete(iter.next().value); } const item = { url, contentType: ct || '', initiatorType: it || 'scan', timestamp: Date.now(), displayType: dt, filterType: Security.filterType(dt), filename: Security.filename(url) }; if (this.resources.length >= CONFIG.MAX_RESOURCES) { const ev = this.resources.pop(); if (ev) this.urlIndex.delete(Security.key(ev.url)); } this.resources.unshift(item); this.urlIndex.set(k, item); UI.scheduleUpdate(); return true; }, removeSilent(url) { const k = Security.key(url); if (!k) return; const idx = this.resources.findIndex(r => r.url === url); if (idx >= 0) this.resources.splice(idx, 1); this.urlIndex.delete(k); this.processed.delete(k); }, counts() { const c = { image: 0, media: 0, other: 0 }; for (const r of this.resources) { if (c[r.filterType] !== undefined) c[r.filterType]++; } return c; }, filtered() { let arr = this.resources; if (this.currentFilter) { arr = arr.filter(r => r.filterType === this.currentFilter); } arr = arr.slice(); const m = this.sortMode; arr.sort((a, b) => { switch (m) { case 'time-desc': return b.timestamp - a.timestamp; case 'time-asc': return a.timestamp - b.timestamp; case 'name-asc': { const x = (a.filename || '').toLowerCase(); const y = (b.filename || '').toLowerCase(); return x < y ? -1 : x > y ? 1 : 0; } case 'name-desc': { const x = (a.filename || '').toLowerCase(); const y = (b.filename || '').toLowerCase(); return x < y ? 1 : x > y ? -1 : 0; } } return 0; }); return arr.slice(0, CONFIG.BATCH_SIZE); } }; const Theme = { mq: null, _observer: null, _rafId: null, _current: null, detect() { try { if (this.mq && this.mq.matches) return 'dark'; } catch {} const html = document.documentElement; const body = document.body; const hasDarkMarker = (el) => { if (!el) return false; try { const cls = (typeof el.className === 'string' ? el.className : (el.className && el.className.baseVal) || '' ).toLowerCase(); if (/(^|\s)(dark|theme-dark|night|dark-mode|dark-theme|night-mode)(\s|$)/.test(cls)) { return true; } const attrTheme = el.getAttribute && el.getAttribute('data-theme'); if (attrTheme === 'dark') return true; const attrColorScheme = el.getAttribute && el.getAttribute('color-scheme'); if (attrColorScheme === 'dark') return true; if (el.hasAttribute && el.hasAttribute('dark')) return true; } catch {} return false; }; if (hasDarkMarker(html)) return 'dark'; if (hasDarkMarker(body)) return 'dark'; try { const cs = getComputedStyle(html).colorScheme || ''; if (cs && cs.includes('dark') && !cs.includes('light')) return 'dark'; } catch {} try { const getBg = (el) => { if (!el) return null; const bg = getComputedStyle(el).backgroundColor || ''; const m = bg.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)/); if (!m) return null; const r = Number(m[1]), g = Number(m[2]), b = Number(m[3]); const a = m[4] !== undefined ? parseFloat(m[4]) : 1; if (a < 0.5) return null; return [r, g, b]; }; const rgb = getBg(body) || getBg(html); if (rgb) { const [r, g, b] = rgb; const brightness = (r * 299 + g * 587 + b * 114) / 1000; if (brightness < 80) return 'dark'; } } catch {} return 'light'; }, apply() { const theme = this.detect(); if (theme === this._current) return; this._current = theme; if (!UI.host) return; UI.host.setAttribute('data-theme', theme); let meta = document.querySelector('meta[name="theme-color"][data-hrd]'); if (!meta) { meta = document.createElement('meta'); meta.setAttribute('name', 'theme-color'); meta.setAttribute('data-hrd', '1'); document.head.appendChild(meta); } meta.setAttribute('content', theme === 'dark' ? '#141820' : '#F5F7FA'); }, schedule() { if (this._rafId) return; this._rafId = requestAnimationFrame(() => { this._rafId = null; this.apply(); }); }, init() { try { this.mq = window.matchMedia('(prefers-color-scheme: dark)'); const onMqChange = () => this.apply(); if (this.mq.addEventListener) { this.mq.addEventListener('change', onMqChange); } else if (this.mq.addListener) { this.mq.addListener(onMqChange); } } catch {} try { this._observer = new MutationObserver(() => this.schedule()); this._observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class', 'data-theme', 'color-scheme', 'style', 'dark'] }); if (document.body) { this._observer.observe(document.body, { attributes: true, attributeFilter: ['class', 'data-theme', 'color-scheme', 'style', 'dark'] }); } } catch {} this.apply(); }, destroy() { try { if (this._observer) this._observer.disconnect(); } catch {} if (this._rafId) cancelAnimationFrame(this._rafId); } }; const SafeArea = { _top: 24, detect() { let h = 24; if (window.visualViewport && window.visualViewport.offsetTop > 0) { h = window.visualViewport.offsetTop; } else { const diff = (window.screen ? window.screen.height : 0) - window.innerHeight; if (diff > 0 && diff < 200) h = diff; } this._top = h; if (UI.host) { UI.host.style.setProperty('--hrd-safe-top', h + 'px'); } }, init() { this.detect(); window.addEventListener('resize', () => this.detect()); window.addEventListener('orientationchange', () => { setTimeout(() => this.detect(), 300); }); if (window.visualViewport) { window.visualViewport.addEventListener('resize', () => this.detect()); } } }; const UI = { host: null, root: null, panel: null, list: null, _raf: null, _scrollLocked: false, _savedBodyOverflow: '', _savedHtmlOverflow: '', _toastTimer: null, _itemMap: new Map(), _activeOverlay: null, _swipeBound: false, init() { if (this.host) return; this.host = document.createElement('div'); this.host.setAttribute('data-theme', 'light'); this.host.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;overflow:visible;pointer-events:none;z-index:2147483647;'; this.root = this.host.attachShadow({ mode: 'open' }); this.injectStyle(); this.buildUI(); document.body.appendChild(this.host); this.bindEvents(); }, injectStyle() { const s = document.createElement('style'); s.textContent = ` :host { all: initial; position: fixed; top: 0; left: 0; width: 0; height: 0; overflow: visible; pointer-events: none; z-index: 2147483647; font-family: 'HarmonyOS Sans SC', 'HarmonyOS Sans', 'PingFang SC', -apple-system, BlinkMacSystemFont, sans-serif; -webkit-font-smoothing: antialiased; --hrd-safe-top: 24px; --hrd-sheet-gap: ${CONFIG.SHEET_TOP_GAP}px; --hrd-safe-bottom: max(env(safe-area-inset-bottom, 0px), 0px); --hrd-bg-primary: rgba(245, 247, 250, 0.94); --hrd-bg-solid: #F5F7FA; --hrd-bg-tertiary: #F0F1F5; --hrd-bg-elevated: #FFFFFF; --hrd-bg-mask: rgba(10, 15, 30, 0.32); --hrd-border: rgba(10, 30, 80, 0.06); --hrd-border-strong: rgba(10, 30, 80, 0.10); --hrd-text-primary: #1C1C1E; --hrd-text-secondary: #5F6368; --hrd-text-tertiary: #98989E; --hrd-accent: #0A84FF; --hrd-accent-soft: rgba(10, 132, 255, 0.10); --hrd-accent-glow: rgba(10, 132, 255, 0.30); --hrd-shadow-card: 0 2px 8px rgba(10, 30, 80, 0.04), 0 1px 2px rgba(10, 30, 80, 0.03); --hrd-shadow-fab: 0 8px 28px rgba(10, 132, 255, 0.32), 0 2px 8px rgba(10, 132, 255, 0.16); --hrd-shadow-panel: 0 -12px 48px rgba(10, 30, 80, 0.12); --hrd-toast-bg: rgba(20, 28, 40, 0.92); --hrd-toast-fg: #F5F5F7; --hrd-toast-border: rgba(255, 255, 255, 0.10); --hrd-handle-bg: rgba(10, 30, 80, 0.18); } :host([data-theme="dark"]) { --hrd-bg-primary: rgba(20, 24, 32, 0.94); --hrd-bg-solid: #141820; --hrd-bg-tertiary: #2A2E38; --hrd-bg-elevated: #303440; --hrd-bg-mask: rgba(0, 0, 0, 0.55); --hrd-border: rgba(255, 255, 255, 0.06); --hrd-border-strong: rgba(255, 255, 255, 0.12); --hrd-text-primary: #F5F5F7; --hrd-text-secondary: #A8ACB3; --hrd-text-tertiary: #6C6C70; --hrd-accent-soft: rgba(10, 132, 255, 0.18); --hrd-accent-glow: rgba(10, 132, 255, 0.42); --hrd-shadow-card: 0 2px 8px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.16); --hrd-shadow-fab: 0 8px 28px rgba(10, 132, 255, 0.42), 0 2px 8px rgba(0, 0, 0, 0.24); --hrd-shadow-panel: 0 -12px 48px rgba(0, 0, 0, 0.48); --hrd-toast-bg: rgba(240, 240, 245, 0.92); --hrd-toast-fg: #1C1C1E; --hrd-toast-border: rgba(0, 0, 0, 0.06); --hrd-handle-bg: rgba(255, 255, 255, 0.22); } :host * { box-sizing: border-box; pointer-events: auto; } :host svg { display: block; } #hrd-fab { position: fixed; bottom: calc(120px + var(--hrd-safe-bottom)); right: 20px; width: 60px; height: 60px; border-radius: 50%; background: linear-gradient(135deg, #0A84FF, #5BC0BE); border: 0.5px solid rgba(255, 255, 255, 0.32); color: #fff; cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center; box-shadow: var(--hrd-shadow-fab); transition: transform 0.32s cubic-bezier(0.34, 1.56, 0.64, 1); z-index: 10; } #hrd-fab svg { width: 30px; height: 30px; } #hrd-fab:hover { transform: scale(1.06); } #hrd-fab:active { transform: scale(0.92); } #hrd-backdrop { position: fixed; inset: 0; background: var(--hrd-bg-mask); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); opacity: 0; visibility: hidden; transition: 0.3s ease; z-index: 9; } #hrd-backdrop.on { opacity: 1; visibility: visible; } #hrd-panel { position: fixed; left: 0; right: 0; bottom: 0; height: 74%; background: var(--hrd-bg-primary); backdrop-filter: blur(24px) saturate(180%); -webkit-backdrop-filter: blur(24px) saturate(180%); border-radius: 24px 24px 0 0; border-top: 0.5px solid var(--hrd-border-strong); transform: translateY(100%); transition: transform 0.4s cubic-bezier(0.32, 0.94, 0.6, 1); z-index: 11; display: flex; flex-direction: column; overflow: hidden; box-shadow: var(--hrd-shadow-panel); } #hrd-panel.on { transform: translateY(0); } .hrd-hd { display: flex; align-items: center; padding: 18px 20px 10px; gap: 8px; } .hrd-hd h3 { margin: 0; font-size: 18px; font-weight: 700; color: var(--hrd-text-primary); flex: 1; letter-spacing: -0.01em; display: flex; align-items: center; gap: 8px; min-width: 0; } .hrd-hd .hrd-title-icon { width: 26px; height: 26px; border-radius: 7px; overflow: hidden; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(10, 132, 255, 0.28); } .hrd-hd .hrd-title-icon svg { width: 100%; height: 100%; display: block; } .hrd-hd .hrd-title-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .hrd-hd .hrd-count { font-size: 12px; font-weight: 500; color: var(--hrd-text-tertiary); flex-shrink: 0; } .hrd-icon-btn { width: 34px; height: 34px; border-radius: 50%; border: none; background: var(--hrd-bg-tertiary); color: var(--hrd-text-secondary); cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center; transition: background 0.2s, transform 0.2s, color 0.2s; flex-shrink: 0; } .hrd-icon-btn svg { width: 18px; height: 18px; } .hrd-icon-btn:hover { background: var(--hrd-border-strong); color: var(--hrd-text-primary); } .hrd-icon-btn:active { transform: scale(0.9); } .hrd-filters { display: flex; gap: 8px; padding: 0 20px 12px; } .hrd-chip { flex: 1; padding: 8px 0; border-radius: 14px; border: none; background: var(--hrd-bg-tertiary); font-size: 13px; font-weight: 600; color: var(--hrd-text-secondary); cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 6px; transition: background 0.2s, color 0.2s, transform 0.2s; font-family: inherit; } .hrd-chip svg { width: 16px; height: 16px; } .hrd-chip:hover { background: var(--hrd-border-strong); } .hrd-chip:active { transform: scale(0.96); } .hrd-chip.on { background: var(--hrd-accent); color: #fff; box-shadow: 0 4px 14px var(--hrd-accent-glow); } .hrd-list { flex: 1; overflow-y: auto; padding: 4px 16px calc(24px + var(--hrd-safe-bottom)); touch-action: pan-y; overscroll-behavior: contain; scroll-behavior: smooth; } .hrd-list::-webkit-scrollbar { width: 4px; } .hrd-list::-webkit-scrollbar-thumb { background: var(--hrd-border-strong); border-radius: 4px; } .hrd-list.view-empty { display: flex; align-items: center; justify-content: center; } .hrd-list.view-list .hrd-item { display: flex; gap: 12px; padding: 12px; background: var(--hrd-bg-elevated); border: 0.5px solid var(--hrd-border); border-radius: 16px; margin-bottom: 8px; align-items: center; cursor: pointer; box-shadow: var(--hrd-shadow-card); transition: transform 0.2s, box-shadow 0.2s, background 0.2s; animation: hrdSlideIn 0.3s ease both; } @keyframes hrdSlideIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } .hrd-list.view-list .hrd-item:hover { transform: translateY(-1px); box-shadow: 0 6px 20px rgba(10, 30, 80, 0.08); } .hrd-list.view-list .hrd-item:active { transform: scale(0.985); } .hrd-list.view-list .hrd-thumb { width: 48px; height: 48px; border-radius: 12px; flex-shrink: 0; overflow: hidden; position: relative; display: flex; align-items: center; justify-content: center; background: var(--hrd-bg-tertiary); } .hrd-list.view-grid { display: flex; flex-wrap: wrap; gap: 8px; align-content: flex-start; align-items: flex-start; padding: 8px 16px calc(24px + var(--hrd-safe-bottom)); } .hrd-list.view-grid .hrd-item { flex: 0 0 calc((100% - 16px) / 3); width: calc((100% - 16px) / 3); min-width: 0; display: flex; flex-direction: column; gap: 6px; padding: 8px; background: var(--hrd-bg-elevated); border: 0.5px solid var(--hrd-border); border-radius: 14px; cursor: pointer; box-shadow: var(--hrd-shadow-card); transition: transform 0.2s, box-shadow 0.2s; animation: hrdSlideIn 0.3s ease both; overflow: hidden; height: auto; } .hrd-list.view-grid .hrd-item:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(10, 30, 80, 0.10); } .hrd-list.view-grid .hrd-item:active { transform: scale(0.98); } .hrd-list.view-grid .hrd-thumb { width: 100%; height: 74px; border-radius: 10px; overflow: hidden; position: relative; display: flex; align-items: center; justify-content: center; background: var(--hrd-bg-tertiary); flex-shrink: 0; } .hrd-list.view-grid .hrd-info { width: 100%; min-width: 0; display: flex; flex-direction: column; gap: 4px; flex-shrink: 0; } .hrd-list.view-grid .hrd-name { flex-direction: row; align-items: center; gap: 4px; font-size: 11px; line-height: 1.2; min-height: 16px; overflow: hidden; } .hrd-list.view-grid .hrd-url { display: none; } .hrd-list.view-grid .hrd-tag { height: 14px; font-size: 8px; padding: 0 4px; border-radius: 4px; margin-right: 0; flex-shrink: 0; letter-spacing: 0.2px; } .hrd-list.view-grid .hrd-name-text { flex: 1; min-width: 0; font-size: 11px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .hrd-list.view-grid .hrd-acts { display: flex; flex-direction: row; justify-content: space-between; align-items: center; width: 100%; gap: 2px; flex-shrink: 0; padding-top: 2px; } .hrd-list.view-grid .hrd-btn { width: 24px; height: 24px; flex: 0 0 auto; } .hrd-list.view-grid .hrd-btn svg { width: 12px; height: 12px; } .hrd-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: 0; transition: opacity 0.3s; } .hrd-thumb img.loaded { opacity: 1; } .hrd-thumb .hrd-icon { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; } .hrd-list.view-grid .hrd-thumb .hrd-icon { width: 28px; height: 28px; } .hrd-thumb .hrd-icon svg { width: 100%; height: 100%; } .hrd-thumb[data-kind="image"] { background: rgba(91, 192, 190, 0.14); } .hrd-thumb[data-kind="image"] .hrd-icon { color: #26A69A; } .hrd-thumb[data-kind="video"] { background: rgba(255, 82, 82, 0.14); } .hrd-thumb[data-kind="video"] .hrd-icon { color: #EF5350; } .hrd-thumb[data-kind="audio"] { background: rgba(10, 132, 255, 0.14); } .hrd-thumb[data-kind="audio"] .hrd-icon { color: #0A84FF; } .hrd-thumb[data-kind="code"] { background: rgba(255, 167, 38, 0.16); } .hrd-thumb[data-kind="code"] .hrd-icon { color: #FF9800; } .hrd-thumb[data-kind="json"] { background: rgba(92, 107, 192, 0.14); } .hrd-thumb[data-kind="json"] .hrd-icon { color: #5C6BC0; } .hrd-thumb[data-kind="font"] { background: rgba(161, 136, 127, 0.16); } .hrd-thumb[data-kind="font"] .hrd-icon { color: #A1887F; } .hrd-thumb[data-kind="link"] { background: rgba(66, 165, 245, 0.14); } .hrd-thumb[data-kind="link"] .hrd-icon { color: #42A5F5; } .hrd-thumb[data-kind="doc"] { background: rgba(120, 144, 156, 0.14); } .hrd-thumb[data-kind="doc"] .hrd-icon { color: #78909C; } :host([data-theme="dark"]) .hrd-thumb[data-kind="image"] { background: rgba(38, 166, 154, 0.22); } :host([data-theme="dark"]) .hrd-thumb[data-kind="video"] { background: rgba(239, 83, 80, 0.22); } :host([data-theme="dark"]) .hrd-thumb[data-kind="audio"] { background: rgba(10, 132, 255, 0.22); } :host([data-theme="dark"]) .hrd-thumb[data-kind="code"] { background: rgba(255, 152, 0, 0.22); } :host([data-theme="dark"]) .hrd-thumb[data-kind="json"] { background: rgba(92, 107, 192, 0.24); } :host([data-theme="dark"]) .hrd-thumb[data-kind="font"] { background: rgba(161, 136, 127, 0.24); } :host([data-theme="dark"]) .hrd-thumb[data-kind="link"] { background: rgba(66, 165, 245, 0.22); } :host([data-theme="dark"]) .hrd-thumb[data-kind="doc"] { background: rgba(120, 144, 156, 0.22); } :host([data-theme="dark"]) .hrd-thumb[data-kind="image"] .hrd-icon { color: #4DB6AC; } :host([data-theme="dark"]) .hrd-thumb[data-kind="video"] .hrd-icon { color: #FF7043; } :host([data-theme="dark"]) .hrd-thumb[data-kind="code"] .hrd-icon { color: #FFB74D; } :host([data-theme="dark"]) .hrd-thumb[data-kind="json"] .hrd-icon { color: #9FA8DA; } :host([data-theme="dark"]) .hrd-thumb[data-kind="font"] .hrd-icon { color: #BCAAA4; } :host([data-theme="dark"]) .hrd-thumb[data-kind="link"] .hrd-icon { color: #64B5F6; } :host([data-theme="dark"]) .hrd-thumb[data-kind="doc"] .hrd-icon { color: #90A4AE; } .hrd-info { flex: 1; min-width: 0; } .hrd-name { display: flex; align-items: center; gap: 0; font-size: 13px; font-weight: 600; color: var(--hrd-text-primary); line-height: 1.4; min-width: 0; } .hrd-tag { display: inline-flex; align-items: center; justify-content: center; height: 18px; padding: 0 6px; margin-right: 6px; border-radius: 6px; font-size: 10px; font-weight: 700; letter-spacing: 0.4px; color: #fff; flex-shrink: 0; text-transform: uppercase; background: #78909C; } .hrd-tag[data-kind="image"] { background: #5BC0BE; } .hrd-tag[data-kind="video"] { background: #EF5350; } .hrd-tag[data-kind="audio"] { background: #0A84FF; } .hrd-tag[data-kind="code"] { background: #FF9800; } .hrd-tag[data-kind="json"] { background: #5C6BC0; } .hrd-tag[data-kind="font"] { background: #A1887F; } .hrd-tag[data-kind="link"] { background: #42A5F5; } .hrd-tag[data-kind="doc"] { background: #78909C; } :host([data-theme="dark"]) .hrd-tag[data-kind="image"] { background: #4DB6AC; } :host([data-theme="dark"]) .hrd-tag[data-kind="video"] { background: #E57373; } :host([data-theme="dark"]) .hrd-tag[data-kind="audio"] { background: #64B5F6; } :host([data-theme="dark"]) .hrd-tag[data-kind="code"] { background: #FFB74D; } :host([data-theme="dark"]) .hrd-tag[data-kind="json"] { background: #9FA8DA; } :host([data-theme="dark"]) .hrd-tag[data-kind="font"] { background: #BCAAA4; } :host([data-theme="dark"]) .hrd-tag[data-kind="link"] { background: #64B5F6; } :host([data-theme="dark"]) .hrd-tag[data-kind="doc"] { background: #90A4AE; } .hrd-name-text { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .hrd-url { font-size: 10.5px; color: var(--hrd-text-tertiary); margin-top: 3px; font-family: 'HarmonyOS Sans Mono', 'SF Mono', Monaco, Consolas, monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .hrd-acts { display: flex; gap: 4px; flex-shrink: 0; } .hrd-btn { width: 32px; height: 32px; border-radius: 50%; border: none; background: transparent; color: var(--hrd-accent); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background 0.2s, transform 0.15s; padding: 0; } .hrd-btn svg { width: 16px; height: 16px; } .hrd-btn:hover { background: var(--hrd-accent-soft); } .hrd-btn:active { transform: scale(0.88); } .hrd-empty { text-align: center; padding: 40px 20px; color: var(--hrd-text-secondary); font-size: 14px; display: flex; flex-direction: column; align-items: center; gap: 14px; } .hrd-empty > svg { width: 64px; height: 64px; color: var(--hrd-text-tertiary); opacity: 0.75; margin-bottom: 4px; } .hrd-empty .sub { font-size: 12px; color: var(--hrd-text-tertiary); } #hrd-toast { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.94); background: var(--hrd-toast-bg); color: var(--hrd-toast-fg); padding: 12px 22px; border-radius: 999px; font-size: 13px; font-weight: 500; z-index: 2147483647; pointer-events: none; opacity: 0; transition: opacity 0.3s, transform 0.3s; box-shadow: 0 12px 36px rgba(0, 0, 0, 0.28); border: 0.5px solid var(--hrd-toast-border); display: flex; align-items: center; gap: 8px; } #hrd-toast.on { opacity: 1; transform: translate(-50%, -50%) scale(1); } #hrd-player, #hrd-preview, #hrd-source-panel { position: fixed; top: calc(var(--hrd-safe-top) + var(--hrd-sheet-gap)); left: 0; right: 0; bottom: 0; background: var(--hrd-bg-primary); backdrop-filter: blur(24px) saturate(180%); -webkit-backdrop-filter: blur(24px) saturate(180%); border-radius: 24px 24px 0 0; border-top: 0.5px solid var(--hrd-border-strong); box-shadow: var(--hrd-shadow-panel); z-index: 2147483647; overflow: hidden; padding-bottom: var(--hrd-safe-bottom); animation: hrdSheetIn 0.38s cubic-bezier(0.32, 0.94, 0.6, 1); } @keyframes hrdSheetIn { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: translateY(0); } } .hrd-sheet-handle { width: 36px; height: 4px; border-radius: 2px; background: var(--hrd-handle-bg); margin: 8px auto 0; flex-shrink: 0; } #hrd-player { display: flex; flex-direction: column; } #hrd-player .hd { display: flex; align-items: center; gap: 12px; padding: 14px 20px 12px; flex-shrink: 0; } #hrd-player .hd .ttl { flex: 1; font-size: 16px; font-weight: 700; color: var(--hrd-text-primary); display: flex; align-items: center; gap: 10px; } #hrd-player .hd .ttl svg { width: 22px; height: 22px; color: var(--hrd-accent); } #hrd-player .hd button { width: 36px; height: 36px; border-radius: 50%; border: none; background: var(--hrd-bg-tertiary); color: var(--hrd-text-secondary); cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center; transition: background 0.2s, transform 0.2s; } #hrd-player .hd button:hover { background: var(--hrd-border-strong); } #hrd-player .hd button:active { transform: scale(0.9); } #hrd-player .hd button svg { width: 18px; height: 18px; } #hrd-player .media-wrap { flex: 1; display: flex; align-items: center; justify-content: center; padding: 0 16px 16px; overflow: hidden; min-height: 0; } #hrd-player video, #hrd-player audio { max-width: 100%; max-height: 100%; border-radius: 12px; background: #000; } #hrd-preview { display: flex; align-items: center; justify-content: center; cursor: zoom-out; padding: 48px 16px calc(24px + var(--hrd-safe-bottom)); } #hrd-preview img { max-width: 100%; max-height: 100%; border-radius: 12px; object-fit: contain; } #hrd-preview button { position: absolute; top: 12px; right: 16px; width: 36px; height: 36px; border-radius: 50%; border: none; background: var(--hrd-bg-tertiary); color: var(--hrd-text-secondary); cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center; transition: background 0.2s, transform 0.2s; } #hrd-preview button:hover { background: var(--hrd-border-strong); } #hrd-preview button:active { transform: scale(0.9); } #hrd-preview button svg { width: 18px; height: 18px; } #hrd-source-panel { display: flex; flex-direction: column; padding-bottom: max(env(safe-area-inset-bottom, 0px), 24px); --hrd-src-bg: #1B1F27; --hrd-src-text: #E5E7EB; --hrd-src-lineno: #4A5260; --hrd-src-border: rgba(255, 255, 255, 0.06); --hrd-src-hover: rgba(10, 132, 255, 0.06); } #hrd-source-panel .hd { display: flex; align-items: center; gap: 8px; padding: 12px 16px 10px; flex-shrink: 0; } #hrd-source-panel .hd .ttl { flex: 1; font-size: 16px; font-weight: 700; color: var(--hrd-text-primary); display: flex; align-items: center; gap: 10px; min-width: 0; overflow: hidden; } #hrd-source-panel .hd .ttl > svg { width: 22px; height: 22px; color: var(--hrd-accent); flex-shrink: 0; } #hrd-source-panel .hd .ttl > span:first-of-type { white-space: nowrap; } #hrd-source-panel .hd .meta { font-size: 11px; font-weight: 500; color: var(--hrd-text-tertiary); margin-left: 4px; white-space: nowrap; flex-shrink: 0; } #hrd-source-panel .hd button { width: 34px; height: 34px; border-radius: 50%; border: none; background: var(--hrd-bg-tertiary); color: var(--hrd-text-secondary); cursor: pointer; padding: 0; flex-shrink: 0; display: flex; align-items: center; justify-content: center; transition: background 0.2s, transform 0.2s, color 0.2s; } #hrd-source-panel .hd button:hover { background: var(--hrd-border-strong); color: var(--hrd-text-primary); } #hrd-source-panel .hd button:active { transform: scale(0.9); } #hrd-source-panel .hd button svg { width: 18px; height: 18px; } #hrd-source-panel .hd button.active { color: var(--hrd-accent); background: var(--hrd-accent-soft); } #hrd-source-panel .src-container { flex: 1; min-height: 0; overflow: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; background: var(--hrd-src-bg); border-radius: 12px; margin: 4px 12px 8px; padding: 8px 0; font-family: 'HarmonyOS Sans Mono', 'SF Mono', 'Menlo', 'Monaco', 'Cascadia Code', 'Fira Code', Consolas, monospace; font-size: 12px; line-height: 1.55; color: var(--hrd-src-text); will-change: scroll-position; tab-size: 4; -moz-tab-size: 4; } #hrd-source-panel .src-container::-webkit-scrollbar { width: 6px; height: 6px; } #hrd-source-panel .src-container::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.12); border-radius: 3px; } #hrd-source-panel .src-container::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.22); } #hrd-source-panel .src-container::-webkit-scrollbar-corner { background: transparent; } .src-code { display: inline-block; min-width: 100%; padding: 4px 0 8px; vertical-align: top; } #hrd-source-panel.wrap .src-code { display: block; } .src-line { min-height: 1.55em; transition: background 0.15s; display: block; white-space: nowrap; } #hrd-source-panel.wrap .src-line { display: flex; align-items: flex-start; white-space: normal; } .src-line:hover { background: var(--hrd-src-hover); } .src-lineno { display: inline-block; min-width: 2.5em; padding-left: 10px; padding-right: 10px; text-align: right; color: var(--hrd-src-lineno); user-select: none; -webkit-user-select: none; border-right: 1px solid var(--hrd-src-border); font-variant-numeric: tabular-nums; position: sticky; left: 0; background: var(--hrd-src-bg); z-index: 2; vertical-align: top; } #hrd-source-panel.wrap .src-lineno { flex: 0 0 auto; } .src-line:hover .src-lineno { background: #22272F; } .src-line-content { display: inline-block; padding-right: 12px; vertical-align: top; white-space: pre; } #hrd-source-panel.wrap .src-line-content { display: block; flex: 1 1 auto; min-width: 0; white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere; } .src-more { padding: 12px 20px; text-align: center; color: #6C6C70; font-size: 12px; border-top: 1px dashed var(--hrd-src-border); margin: 8px 16px 0; } .hl-tag-name { color: #E06C75; font-weight: 500; } .hl-attr-name { color: #D19A66; } .hl-attr-value { color: #98C379; } .hl-punct { color: #ABB2BF; } .hl-comment { color: #5C6370; font-style: italic; } .hl-doctype { color: #C678DD; font-weight: 500; } .hl-tag { color: #E06C75; } .hl-text { color: var(--hrd-src-text); } `; this.root.appendChild(s); }, buildUI() { const c = document.createElement('div'); c.innerHTML = `