ui布局自适应

This commit is contained in:
tianmo committed 2026-11-09 10:08:29 +08:00
1 parent 98f44890f8
commit 3587129b90
40 files changed
+929 -142

No files matched your search

+28 -6
View File
@@ -2,7 +2,9 @@ import type { ParticleSystem } from '../store/particleStore'
import type { SpineExportVersion } from './spineExportSettings'
import { translate } from '../i18n'
const CONNECTOR_URL = 'http://127.0.0.1:27843'
const CONNECTOR_HTTP_URL = 'http://127.0.0.1:27843'
const CONNECTOR_HTTPS_URL = 'https://127.0.0.1:27844'
let connectorUrl = CONNECTOR_HTTP_URL
export interface SpineConnectorHealth {
available: boolean
@@ -10,6 +12,8 @@ export interface SpineConnectorHealth {
connectorVersion?: string
spineFound?: boolean
spinePath?: string
secureAvailable?: boolean
pairUrl?: string
platform?: string
error?: string
}
@@ -109,12 +113,28 @@ export async function collectSpineExportImages(systems: ParticleSystem[], onProg
}
export async function checkSpineConnector(): Promise<SpineConnectorHealth> {
const isSafari = typeof navigator !== 'undefined'
&& /Safari\//.test(navigator.userAgent)
&& !/(?:Chrome|Chromium|CriOS|Edg|EdgiOS|OPR|Firefox|FxiOS)\//.test(navigator.userAgent)
const candidates = typeof window !== 'undefined' && window.location.protocol === 'https:'
? isSafari ? [CONNECTOR_HTTPS_URL, CONNECTOR_HTTP_URL] : [CONNECTOR_HTTP_URL, CONNECTOR_HTTPS_URL]
: [CONNECTOR_HTTP_URL]
try {
const response = await fetch(`${CONNECTOR_URL}/v1/health`, { cache: 'no-store' })
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const body = await response.json()
return { available: true, ...body }
let lastError: unknown
for (const candidate of candidates) {
try {
const response = await fetch(`${candidate}/v1/health`, { cache: 'no-store' })
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const body = await response.json()
connectorUrl = candidate
return { available: true, ...body }
} catch (error) {
lastError = error
}
}
throw lastError
} catch (error) {
connectorUrl = ''
return { available: false, authorized: false, error: error instanceof Error ? error.message : String(error) }
}
}
@@ -125,6 +145,8 @@ export async function convertWithSpineConnector(input: {
skeletonJson: string
systems: ParticleSystem[]
}, onProgress?: (progress: SpineConnectorProgress) => void) {
if (!connectorUrl) await checkSpineConnector()
if (!connectorUrl) throw new SpineConnectorError(translate('errors.connectorOffline'), 'CONNECTOR_OFFLINE')
const images = await collectSpineExportImages(input.systems, (completed, total) => {
onProgress?.({ phase: 'images', progress: total ? completed / total : 1 })
})
@@ -138,7 +160,7 @@ export async function convertWithSpineConnector(input: {
return new Promise<{ blob: Blob; fileName: string; spineEditorVersion: string; imageCount: number }>((resolve, reject) => {
const request = new XMLHttpRequest()
request.open('POST', `${CONNECTOR_URL}/v1/convert`)
request.open('POST', `${connectorUrl}/v1/convert`)
request.responseType = 'blob'
request.setRequestHeader('Content-Type', 'application/json')
request.upload.onprogress = (event) => {
+3
View File
@@ -775,6 +775,9 @@
"tabsLabel": "Hilfebereiche",
"updates": "Versionshinweise",
"updatesSubtitle": "Aktuelle Funktionsupdates anzeigen",
"releaseSummaryV11107": "V1.11.07 aktualisiert",
"releaseHeadingV11107": "Update vom 07.11.2026:",
"releaseSafariConnector": "Safari-Browser-Unterstützung für den Connector hinzugefügt.",
"releaseSummaryV11106": "V1.11.06 aktualisiert",
"releaseHeadingV11106": "Update vom 06.11.2026:",
"releaseMenuLayerFix": "Die Anzeigeebene des Ziehpunkts im Menüsteuerungsbereich wurde korrigiert.",
+3
View File
@@ -798,6 +798,9 @@
"tabsLabel": "Help sections",
"updates": "Release Notes",
"updatesSubtitle": "View recent feature updates",
"releaseSummaryV11107": "Updated to V1.11.07",
"releaseHeadingV11107": "Updated 2026-11-07:",
"releaseSafariConnector": "Added Safari browser support to the connector.",
"releaseSummaryV11106": "Updated to V1.11.06",
"releaseHeadingV11106": "Updated 2026-11-06:",
"releaseMenuLayerFix": "Fixed the display layer of the drag button in the menu control area.",
+3
View File
@@ -775,6 +775,9 @@
"tabsLabel": "Secciones de ayuda",
"updates": "Actualizaciones de versión",
"updatesSubtitle": "Ver actualizaciones recientes de funciones",
"releaseSummaryV11107": "Actualizado a V1.11.07",
"releaseHeadingV11107": "Actualización del 07/11/2026:",
"releaseSafariConnector": "Se agregó compatibilidad con el navegador Safari al conector.",
"releaseSummaryV11106": "Actualizado a V1.11.06",
"releaseHeadingV11106": "Actualización del 06/11/2026:",
"releaseMenuLayerFix": "Corregida la capa de visualización del botón de arrastre en el área de control del menú.",
+3
View File
@@ -775,6 +775,9 @@
"tabsLabel": "Sections de l’aide",
"updates": "Mises à jour",
"updatesSubtitle": "Voir les mises à jour récentes des fonctionnalités",
"releaseSummaryV11107": "Mis à jour vers V1.11.07",
"releaseHeadingV11107": "Mise à jour du 07/11/2026 :",
"releaseSafariConnector": "Ajout de la prise en charge du navigateur Safari au connecteur.",
"releaseSummaryV11106": "Mis à jour vers V1.11.06",
"releaseHeadingV11106": "Mise à jour du 06/11/2026 :",
"releaseMenuLayerFix": "Correction de l’ordre d’affichage du bouton de glissement dans la zone de contrôle du menu.",
+3
View File
@@ -775,6 +775,9 @@
"tabsLabel": "ヘルプのセクション",
"updates": "バージョン更新",
"updatesSubtitle": "最近の機能更新を見る",
"releaseSummaryV11107": "V1.11.07 更新済み",
"releaseHeadingV11107": "2026-11-07 更新:",
"releaseSafariConnector": "コネクタのSafariブラウザ対応を追加しました。",
"releaseSummaryV11106": "V1.11.06 更新済み",
"releaseHeadingV11106": "2026-11-06 更新:",
"releaseMenuLayerFix": "メニューコントロール領域のドラッグボタンの表示階層を修正しました。",
+3
View File
@@ -775,6 +775,9 @@
"tabsLabel": "도움말 섹션",
"updates": "버전 업데이트",
"updatesSubtitle": "최근 기능 업데이트 보기",
"releaseSummaryV11107": "V1.11.07 업데이트됨",
"releaseHeadingV11107": "2026-11-07 업데이트:",
"releaseSafariConnector": "커넥터에 Safari 브라우저 지원을 추가했습니다.",
"releaseSummaryV11106": "V1.11.06 업데이트됨",
"releaseHeadingV11106": "2026-11-06 업데이트:",
"releaseMenuLayerFix": "메뉴 컨트롤 영역의 드래그 버튼 표시 레이어를 수정했습니다.",
+3
View File
@@ -775,6 +775,9 @@
"tabsLabel": "Seções da ajuda",
"updates": "Atualizações da versão",
"updatesSubtitle": "Ver atualizações recentes de recursos",
"releaseSummaryV11107": "Atualizado para V1.11.07",
"releaseHeadingV11107": "Atualização de 07/11/2026:",
"releaseSafariConnector": "Adicionado suporte ao navegador Safari no conector.",
"releaseSummaryV11106": "Atualizado para V1.11.06",
"releaseHeadingV11106": "Atualização de 06/11/2026:",
"releaseMenuLayerFix": "Corrigida a camada de exibição do botão de arrastar na área de controle do menu.",
+3
View File
@@ -798,6 +798,9 @@
"tabsLabel": "Разделы справки",
"updates": "Обновления версии",
"updatesSubtitle": "Последние обновления функций",
"releaseSummaryV11107": "Обновлено до V1.11.07",
"releaseHeadingV11107": "Обновление от 07.11.2026:",
"releaseSafariConnector": "Добавлена поддержка браузера Safari в коннектор.",
"releaseSummaryV11106": "Обновлено до V1.11.06",
"releaseHeadingV11106": "Обновление от 06.11.2026:",
"releaseMenuLayerFix": "Исправлен порядок отображения кнопки перетаскивания в области управления меню.",
+3
View File
@@ -798,6 +798,9 @@
"tabsLabel": "使用说明栏目",
"updates": "版本更新",
"updatesSubtitle": "查看近期功能更新记录",
"releaseSummaryV11107": "V1.11.07 已更新",
"releaseHeadingV11107": "2026-11-07更新:",
"releaseSafariConnector": "增加 Safari 浏览器连接器支持",
"releaseSummaryV11106": "V1.11.06 已更新",
"releaseHeadingV11106": "2026-11-06更新:",
"releaseMenuLayerFix": "修正菜单控制区拖动按钮显示层级",
+3
View File
@@ -775,6 +775,9 @@
"tabsLabel": "使用說明欄目",
"updates": "版本更新",
"updatesSubtitle": "查看近期功能更新記錄",
"releaseSummaryV11107": "V1.11.07 已更新",
"releaseHeadingV11107": "2026-11-07更新:",
"releaseSafariConnector": "新增 Safari 瀏覽器連接器支援",
"releaseSummaryV11106": "V1.11.06 已更新",
"releaseHeadingV11106": "2026-11-06更新:",
"releaseMenuLayerFix": "修正選單控制區拖曳按鈕顯示層級",
+6
View File
@@ -255,6 +255,12 @@ const activeTab = ref<'guide' | 'updates'>('guide')
const openModules = ref<boolean[]>(Array.from({ length: 9 }, () => false))
const releases = [
{
date: '2026-11-07',
title: 'help.releaseSummaryV11107',
heading: 'help.releaseHeadingV11107',
updates: ['help.releaseSafariConnector'],
},
{
date: '2026-11-06',
title: 'help.releaseSummaryV11106',
+23 -20
View File
@@ -178,8 +178,11 @@ onBeforeUnmount(closePromotionPreview)
<style scoped>
.welcome-shell {
/* --u 是“1px”在 1920 宽下的缩放单位:宽度不超过 1920 时恒为 1px(外观不变),
更宽的屏幕按 100vw / 1920 线性放大,保证 2K/4K 与 1080p 的画面比例一致。 */
--u: max(1px, calc(100vw / 1920));
position: fixed; inset: 0; display: flex; flex-direction: column; overflow: hidden; box-sizing: border-box;
padding: clamp(22px, 3vw, 42px) clamp(22px, 4vw, 64px) 28px;
padding: clamp(22px, 3vw, calc(42 * var(--u))) clamp(22px, 4vw, calc(64 * var(--u))) calc(28 * var(--u));
color: #eef0f5;
background:
radial-gradient(circle at 17% 0%, rgba(218, 45, 57, .16), transparent 32%),
@@ -190,33 +193,33 @@ onBeforeUnmount(closePromotionPreview)
.welcome-shell::before {
position: fixed; inset: 0; pointer-events: none; content: ""; opacity: .22;
background-image: linear-gradient(rgba(255,255,255,.025) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.025) 1px, transparent 1px);
background-size: 42px 42px;
background-size: calc(42 * var(--u)) calc(42 * var(--u));
mask-image: linear-gradient(to bottom, #000, transparent 70%);
}
.welcome-header, .welcome-intro, .hub-grid { position: relative; }
.welcome-header { display: flex; min-height: 42px; align-items: center; gap: 18px; }
.welcome-header { display: flex; min-height: calc(42 * var(--u)); align-items: center; gap: calc(18 * var(--u)); }
.welcome-header { z-index: 40; }
.brand-link { display: inline-flex; border-radius: 6px; transition: opacity .16s ease; }
.brand-link:hover { opacity: .78; }
.brand-link:focus-visible { outline: 2px solid #ef5147; outline-offset: 5px; }
.brand-logo { display: block; width: clamp(128px, 10vw, 156px); height: auto; }
.welcome-intro { padding: clamp(34px, 6vh, 64px) 0 clamp(22px, 3.5vh, 34px); text-align: center; }
.brand-logo { display: block; width: clamp(128px, 10vw, calc(156 * var(--u))); height: auto; }
.welcome-intro { padding: clamp(34px, 6vh, calc(64 * var(--u))) 0 clamp(22px, 3.5vh, calc(34 * var(--u))); text-align: center; }
.welcome-intro h1 {
max-width: none; margin: 0 auto; color: #f3f3f5;
font-size: clamp(20px, 2.4vw, 40px); line-height: 1.1; letter-spacing: -.025em; white-space: nowrap;
font-size: clamp(20px, 2.4vw, calc(40 * var(--u))); line-height: 1.1; letter-spacing: -.025em; white-space: nowrap;
}
.welcome-intro h1 span { color: #ff4e47; }
.welcome-intro p { max-width: 390px; margin: 0 0 3px; color: #8c93a3; font-size: 13px; line-height: 1.75; }
.hub-grid {
display: grid; min-height: 0; flex: 0 1 auto;
grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(14px, 1.5vw, 22px);
width: min(100%, 1540px); margin: 0 auto;
grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(14px, 1.5vw, calc(22 * var(--u)));
width: min(100%, calc(1540 * var(--u))); margin: 0 auto;
}
.hub-card {
--hub-border: #242731;
--hub-divider: #242731;
position: relative; display: flex; min-width: 0; min-height: 0; flex-direction: column;
padding: 0; overflow: hidden; border: 1px solid var(--hub-border); border-radius: 18px;
padding: 0; overflow: hidden; border: 1px solid var(--hub-border); border-radius: calc(18 * var(--u));
background: linear-gradient(180deg, rgba(26,29,38,.94), rgba(15,17,23,.98));
color: inherit; font: inherit; text-align: left; text-decoration: none; cursor: pointer;
box-shadow: 0 20px 52px rgba(0,0,0,.25);
@@ -228,24 +231,24 @@ onBeforeUnmount(closePromotionPreview)
.viewer-card:hover, .thumbnail-card:hover { --hub-border: #f04d45; }
.hub-preview { position: relative; width: 100%; aspect-ratio: 3 / 2; flex: 0 0 auto; overflow: hidden; border-bottom: 1px solid #252832; }
.preview-badge {
position: absolute; top: 14px; left: 14px; z-index: 4; padding: 5px 9px;
position: absolute; top: calc(14 * var(--u)); left: calc(14 * var(--u)); z-index: 4; padding: calc(5 * var(--u)) calc(9 * var(--u));
border: 1px solid rgba(255,255,255,.11); border-radius: 999px;
background: rgba(10,11,16,.72); color: #c7cbd5; font-size: 9px; letter-spacing: .08em;
background: rgba(10,11,16,.72); color: #c7cbd5; font-size: calc(9 * var(--u)); letter-spacing: .08em;
backdrop-filter: blur(7px);
}
.badge-with-icon { display: inline-flex; align-items: center; gap: 6px; }
.badge-with-icon img { display: block; width: 16px; height: 16px; border-radius: 4px; }
.card-body { display: flex; flex: 1 1 auto; flex-direction: column; padding: clamp(16px, 1.7vw, 24px) clamp(16px, 1.8vw, 25px) 0; }
.card-body h2 { margin: 0 0 8px; color: #f1f2f5; font-size: clamp(22px, 1.8vw, 28px); line-height: 1.1; letter-spacing: -.02em; }
.card-body p { min-height: 40px; margin: 0; color: #858c9c; font-size: 12px; line-height: 1.65; }
.tag-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 14px 0 18px; }
.tag-row span { padding: 4px 8px; border: 1px solid #34394a; border-radius: 999px; background: #1a1e2a; color: #8f98ad; font-size: 9px; }
.badge-with-icon { display: inline-flex; align-items: center; gap: calc(6 * var(--u)); }
.badge-with-icon img { display: block; width: calc(16 * var(--u)); height: calc(16 * var(--u)); border-radius: calc(4 * var(--u)); }
.card-body { display: flex; flex: 1 1 auto; flex-direction: column; padding: clamp(16px, 1.7vw, calc(24 * var(--u))) clamp(16px, 1.8vw, calc(25 * var(--u))) 0; }
.card-body h2 { margin: 0 0 calc(8 * var(--u)); color: #f1f2f5; font-size: clamp(22px, 1.8vw, calc(28 * var(--u))); line-height: 1.1; letter-spacing: -.02em; }
.card-body p { min-height: calc(40 * var(--u)); margin: 0; color: #858c9c; font-size: calc(12 * var(--u)); line-height: 1.65; }
.tag-row { display: flex; flex-wrap: wrap; gap: calc(6 * var(--u)); margin: calc(14 * var(--u)) 0 calc(18 * var(--u)); }
.tag-row span { padding: calc(4 * var(--u)) calc(8 * var(--u)); border: 1px solid #34394a; border-radius: 999px; background: #1a1e2a; color: #8f98ad; font-size: calc(9 * var(--u)); }
.editor-card .tag-row span { border-color: rgba(119,116,255,.35); background: rgba(74,72,150,.16); color: #aaa7f5; }
.viewer-card .tag-row span, .thumbnail-card .tag-row span { border-color: rgba(238,76,68,.24); background: rgba(111,36,37,.14); color: #d99a97; }
.card-action {
display: flex; width: 100%; min-height: 76px; flex: 0 0 auto; align-items: center; justify-content: center;
display: flex; width: 100%; min-height: calc(76 * var(--u)); flex: 0 0 auto; align-items: center; justify-content: center;
border-top: 1px solid var(--hub-divider); background: #13161d; color: #f3f4f7;
font-size: clamp(17px, 1.35vw, 21px); font-weight: 750; letter-spacing: -.01em;
font-size: clamp(17px, 1.35vw, calc(21 * var(--u))); font-weight: 750; letter-spacing: -.01em;
transition: background-color .18s ease, color .18s ease;
}
.card-action:hover { border-top-color: #d94740; background: #d94740; color: #fff; }
@@ -56,7 +56,7 @@ const connectorReady = computed(() => !!connectorHealth.value?.available && !!co
const connectorAuthorizationUrl = computed(() => {
const health = connectorHealth.value
if (!health?.available || !health.spineFound || health.authorized || typeof window === 'undefined') return ''
return `http://127.0.0.1:27843/pair?origin=${encodeURIComponent(window.location.origin)}`
return health.pairUrl || `http://127.0.0.1:27843/pair?origin=${encodeURIComponent(window.location.origin)}`
})
const connectorDisabledReason = computed(() => {
const health = connectorHealth.value