ui布局自适应
This commit is contained in:
1 parent
98f44890f8
commit
3587129b90
40 files changed
+929
-142
No files matched your search
@@ -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) => {
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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ú.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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": "メニューコントロール領域のドラッグボタンの表示階層を修正しました。",
|
||||
|
||||
@@ -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": "메뉴 컨트롤 영역의 드래그 버튼 표시 레이어를 수정했습니다.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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": "Исправлен порядок отображения кнопки перетаскивания в области управления меню.",
|
||||
|
||||
@@ -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": "修正菜单控制区拖动按钮显示层级",
|
||||
|
||||
@@ -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": "修正選單控制區拖曳按鈕顯示層級",
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user