Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 19fdf85dcc | |||
| 2626fe2335 | |||
| 074d028f04 |
@@ -138,6 +138,7 @@ function mapTierListRow(row) {
|
||||
description: row.description || '',
|
||||
isPublic: !!row.is_public,
|
||||
showCharacterNames: !!row.show_character_names,
|
||||
iconSize: Number(row.icon_size || 80),
|
||||
sourceTierListId: row.source_tierlist_id || '',
|
||||
sourceSnapshotTitle: row.source_snapshot_title || '',
|
||||
sourceSnapshotAuthor: row.source_snapshot_author || '',
|
||||
@@ -314,6 +315,7 @@ async function ensureSchema() {
|
||||
description TEXT NOT NULL,
|
||||
is_public TINYINT(1) NOT NULL DEFAULT 0,
|
||||
show_character_names TINYINT(1) NOT NULL DEFAULT 0,
|
||||
icon_size INT NOT NULL DEFAULT 80,
|
||||
source_tierlist_id VARCHAR(64) NULL DEFAULT NULL,
|
||||
source_snapshot_title VARCHAR(120) NOT NULL DEFAULT '',
|
||||
source_snapshot_author VARCHAR(120) NOT NULL DEFAULT '',
|
||||
@@ -455,9 +457,13 @@ async function ensureSchema() {
|
||||
if (!tierListShowNamesColumns.length) {
|
||||
await query("ALTER TABLE tierlists ADD COLUMN show_character_names TINYINT(1) NOT NULL DEFAULT 0 AFTER is_public")
|
||||
}
|
||||
const tierListIconSizeColumns = await query("SHOW COLUMNS FROM tierlists LIKE 'icon_size'")
|
||||
if (!tierListIconSizeColumns.length) {
|
||||
await query("ALTER TABLE tierlists ADD COLUMN icon_size INT NOT NULL DEFAULT 80 AFTER show_character_names")
|
||||
}
|
||||
const tierListSourceIdColumns = await query("SHOW COLUMNS FROM tierlists LIKE 'source_tierlist_id'")
|
||||
if (!tierListSourceIdColumns.length) {
|
||||
await query("ALTER TABLE tierlists ADD COLUMN source_tierlist_id VARCHAR(64) NULL DEFAULT NULL AFTER show_character_names")
|
||||
await query("ALTER TABLE tierlists ADD COLUMN source_tierlist_id VARCHAR(64) NULL DEFAULT NULL AFTER icon_size")
|
||||
} else if (tierListSourceIdColumns[0]?.Null !== 'YES') {
|
||||
await query('ALTER TABLE tierlists MODIFY source_tierlist_id VARCHAR(64) NULL DEFAULT NULL')
|
||||
}
|
||||
@@ -1847,6 +1853,7 @@ async function listFavoriteTierLists(userId, { queryText = '', sort = 'favorited
|
||||
t.description,
|
||||
t.is_public,
|
||||
t.show_character_names,
|
||||
t.icon_size,
|
||||
t.source_tierlist_id,
|
||||
t.source_snapshot_title,
|
||||
t.source_snapshot_author,
|
||||
@@ -1990,6 +1997,7 @@ async function listAdminTierLists({ queryText = '', gameId = '', page = 1, limit
|
||||
t.description,
|
||||
t.is_public,
|
||||
t.show_character_names,
|
||||
t.icon_size,
|
||||
t.source_tierlist_id,
|
||||
t.source_snapshot_title,
|
||||
t.source_snapshot_author,
|
||||
@@ -2093,6 +2101,7 @@ async function findTierListById(id, currentUserId = '') {
|
||||
t.description,
|
||||
t.is_public,
|
||||
t.show_character_names,
|
||||
t.icon_size,
|
||||
t.source_tierlist_id,
|
||||
t.source_snapshot_title,
|
||||
t.source_snapshot_author,
|
||||
@@ -2346,6 +2355,7 @@ async function saveTierList({
|
||||
description,
|
||||
isPublic,
|
||||
showCharacterNames = false,
|
||||
iconSize = 80,
|
||||
sourceTierListId = '',
|
||||
sourceSnapshotTitle = '',
|
||||
sourceSnapshotAuthor = '',
|
||||
@@ -2360,10 +2370,10 @@ async function saveTierList({
|
||||
await query(
|
||||
`
|
||||
UPDATE tierlists
|
||||
SET title = ?, thumbnail_src = ?, description = ?, is_public = ?, show_character_names = ?, source_tierlist_id = ?, source_snapshot_title = ?, source_snapshot_author = ?, groups_json = ?, pool_json = ?, updated_at = ?
|
||||
SET title = ?, thumbnail_src = ?, description = ?, is_public = ?, show_character_names = ?, icon_size = ?, source_tierlist_id = ?, source_snapshot_title = ?, source_snapshot_author = ?, groups_json = ?, pool_json = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
`,
|
||||
[title, nextThumbnailSrc, description || '', isPublic ? 1 : 0, showCharacterNames ? 1 : 0, sourceTierListId || null, sourceSnapshotTitle || '', sourceSnapshotAuthor || '', serializeJson(groups), serializeJson(pool), now(), existing.id]
|
||||
[title, nextThumbnailSrc, description || '', isPublic ? 1 : 0, showCharacterNames ? 1 : 0, Number(iconSize) || 80, sourceTierListId || null, sourceSnapshotTitle || '', sourceSnapshotAuthor || '', serializeJson(groups), serializeJson(pool), now(), existing.id]
|
||||
)
|
||||
return findTierListById(existing.id, authorId)
|
||||
}
|
||||
@@ -2373,11 +2383,11 @@ async function saveTierList({
|
||||
await query(
|
||||
`
|
||||
INSERT INTO tierlists (
|
||||
id, author_id, game_id, title, thumbnail_src, description, is_public, show_character_names, source_tierlist_id, source_snapshot_title, source_snapshot_author, groups_json, pool_json, created_at, updated_at
|
||||
id, author_id, game_id, title, thumbnail_src, description, is_public, show_character_names, icon_size, source_tierlist_id, source_snapshot_title, source_snapshot_author, groups_json, pool_json, created_at, updated_at
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
[nextId, authorId, gameId, title, nextThumbnailSrc, description || '', isPublic ? 1 : 0, showCharacterNames ? 1 : 0, sourceTierListId || null, sourceSnapshotTitle || '', sourceSnapshotAuthor || '', serializeJson(groups), serializeJson(pool), createdAt, createdAt]
|
||||
[nextId, authorId, gameId, title, nextThumbnailSrc, description || '', isPublic ? 1 : 0, showCharacterNames ? 1 : 0, Number(iconSize) || 80, sourceTierListId || null, sourceSnapshotTitle || '', sourceSnapshotAuthor || '', serializeJson(groups), serializeJson(pool), createdAt, createdAt]
|
||||
)
|
||||
return findTierListById(nextId, authorId)
|
||||
}
|
||||
@@ -2396,6 +2406,7 @@ async function duplicateTierListForUser({ tierList, targetUserId }) {
|
||||
description: tierList.description || '',
|
||||
isPublic: false,
|
||||
showCharacterNames: !!tierList.showCharacterNames,
|
||||
iconSize: Number(tierList.iconSize || 80),
|
||||
sourceTierListId: tierList.id,
|
||||
sourceSnapshotTitle: tierList.title || '',
|
||||
sourceSnapshotAuthor: tierList.authorName || tierList.authorAccountName || '',
|
||||
|
||||
@@ -92,6 +92,7 @@ const tierListUpsertSchema = z.object({
|
||||
description: z.string().max(1000).optional().default(''),
|
||||
isPublic: z.boolean().default(false),
|
||||
showCharacterNames: z.boolean().optional().default(false),
|
||||
iconSize: z.number().int().min(48).max(112).optional().default(80),
|
||||
sourceTierListId: z.string().max(64).optional().default(''),
|
||||
sourceSnapshotTitle: z.string().max(120).optional().default(''),
|
||||
sourceSnapshotAuthor: z.string().max(120).optional().default(''),
|
||||
@@ -289,6 +290,7 @@ router.post('/', requireAuth, async (req, res) => {
|
||||
description: payload.description || '',
|
||||
isPublic: !!payload.isPublic,
|
||||
showCharacterNames: !!payload.showCharacterNames,
|
||||
iconSize: Number(payload.iconSize || 80),
|
||||
sourceTierListId: payload.sourceTierListId || existing.sourceTierListId || '',
|
||||
sourceSnapshotTitle: payload.sourceSnapshotTitle || existing.sourceSnapshotTitle || '',
|
||||
sourceSnapshotAuthor: payload.sourceSnapshotAuthor || existing.sourceSnapshotAuthor || '',
|
||||
@@ -307,6 +309,7 @@ router.post('/', requireAuth, async (req, res) => {
|
||||
description: payload.description || '',
|
||||
isPublic: !!payload.isPublic,
|
||||
showCharacterNames: !!payload.showCharacterNames,
|
||||
iconSize: Number(payload.iconSize || 80),
|
||||
sourceTierListId: payload.sourceTierListId || '',
|
||||
sourceSnapshotTitle: payload.sourceSnapshotTitle || '',
|
||||
sourceSnapshotAuthor: payload.sourceSnapshotAuthor || '',
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# 의사결정 이력
|
||||
|
||||
## 2026-04-02 v1.3.88
|
||||
- 헤더의 `by zenn`은 이미 공통 카피라이트 링크가 생긴 뒤 역할이 겹치므로, 브랜드 영역은 서비스명 중심으로 정리하는 편이 맞다고 판단했다.
|
||||
- 외부 공유 미리보기는 메타 태그만 넣는 것보다 실제 전용 썸네일 자산을 함께 두는 편이 메신저/소셜/모바일 홈 화면까지 더 안정적으로 동작한다고 정리했다.
|
||||
- 파비콘은 인라인 data URL 하나에 의존하기보다 `svg + png + apple-touch-icon` 조합으로 두는 편이 브라우저와 기기 호환성 측면에서 안전하다고 판단했다.
|
||||
|
||||
## 2026-04-02 v1.3.86
|
||||
- 아이콘 크기는 이미지 다운로드 결과에만 반영되고 저장본에는 남지 않으면 사용자가 체감상 “저장되지 않는 설정”으로 느끼게 되므로, 티어표 본문 설정으로 저장하는 편이 맞다고 정리했다.
|
||||
- 저장 경로를 고친 뒤에도 프리뷰 화면이 기본값으로 보인다면, 데이터보다 프런트 렌더링 루트에 동일 CSS 변수가 전달되는지 먼저 확인하는 편이 맞다고 정리했다.
|
||||
|
||||
## 2026-04-02 v1.3.83
|
||||
- 모바일에서 열 헤더가 칸과 시각적으로 분리되는 문제는 전체 레이아웃을 다시 갈아엎기보다, 각 칸 안에 열 이름 배지를 같이 보여주는 편이 가장 적은 변경으로 효과를 낸다고 정리했다.
|
||||
- 배지를 쓰는 반응형 구간에서는 기존 상단 열 헤더까지 남겨두면 중복 정보가 되므로, 같은 브레이크포인트에서 헤더는 숨기고 칸 배지 하나만 남기는 편이 맞다고 정리했다.
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
## 공통 레이아웃
|
||||
- 앱 셸 파일: `frontend/src/App.vue`
|
||||
- 역할: 좌측 내비게이션, 중앙 워크스페이스, 우측 컨텍스트 패널로 구성된 공통 앱 셸 렌더링, 로그인 상태 반영, 최근 즐겨찾기 바로가기와 전역 검색 입력, 관리자 메뉴 노출 제어, 실제 SVG 에셋과 선형 SVG 아이콘이 혼합된 레일 UI, 전역 우측 상단 토스트 렌더링
|
||||
- 세부: 좌측 패널은 `248px` 기준 폭을 사용하되 축소 시 아이콘 중심의 좁은 레일로 전환하고, 우측 패널은 `320px` 기준 폭을 사용한다. 세 컬럼 모두 상단에 높이 `56px`의 헤더 블록을 유지한다. 중앙 헤더에는 고정 브랜드 `Tier Maker by zenn`이 표시되고, 우측 패널 토글은 닫혀 있을 때 중앙 헤더, 열려 있을 때 우측 헤더에 아이콘만 표시된다. 좌우 레일의 주요 액션은 각각 하단 `56px` 푸터 안에서 항상 보이도록 유지하면서 아래쪽 패딩으로 여백을 확보한다.
|
||||
- 세부: 좌측 패널은 `248px` 기준 폭을 사용하되 축소 시 아이콘 중심의 좁은 레일로 전환하고, 우측 패널은 `320px` 기준 폭을 사용한다. 세 컬럼 모두 상단에 높이 `56px`의 헤더 블록을 유지한다. 중앙 헤더에는 고정 브랜드 `Tier Maker`와 서비스 설명이 표시되고, 우측 패널 토글은 닫혀 있을 때 중앙 헤더, 열려 있을 때 우측 헤더에 아이콘만 표시된다. 좌우 레일의 주요 액션은 각각 하단 `56px` 푸터 안에서 항상 보이도록 유지하면서 아래쪽 패딩으로 여백을 확보한다.
|
||||
|
||||
## 백엔드 진입점
|
||||
- 서버 엔트리: `backend/index.js`
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
- 티어표에 썸네일을 직접 지정하지 않으면 저장 시 대표 아이템 이미지 하나를 기본 썸네일로 자동 선택한다.
|
||||
- 편집 화면 상단 헤더는 좌측 제목/설명, 우측 썸네일 카드 구조를 사용하며 모바일에서는 한 열로 접힌다.
|
||||
- 티어표 편집 화면의 우측 패널은 공통 `rightRail`의 `localRightRailRoot`에 직접 section들을 쌓는 구조이며, 별도 외곽 래퍼 카드 없이 공통 오른쪽 컬럼 문법을 그대로 따른다.
|
||||
- 공통 앱 셸은 좌측/중앙/우측 컬럼마다 높이 `56px`의 상단 헤더 블록을 유지하며, 중앙 헤더에는 고정 사이트 타이틀 `Tier Maker by zenn`을 표시한다.
|
||||
- 공통 앱 셸은 좌측/중앙/우측 컬럼마다 높이 `56px`의 상단 헤더 블록을 유지하며, 중앙 헤더에는 사이트 타이틀 `Tier Maker`와 현재 서비스 설명을 표시한다.
|
||||
- 티어표 편집기의 아이콘 기본 크기는 `80px`이며, 사용자가 `48 / 60 / 80 / 100 / 120` 단계로 즉시 조절할 수 있다.
|
||||
- 공개 티어표 목록과 `내 티어표` 목록은 제목 옆에 작성자 아바타와 표시명을 함께 보여준다.
|
||||
- 작성자 아바타 이미지가 없을 경우 목록 썸네일 fallback은 닉네임이 아니라 계정명 기준 첫 글자를 사용한다.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# 할 일 및 이슈
|
||||
|
||||
## 단기 확인
|
||||
- 홈페이지 공유 메타와 새 `og-card.png`는 이번에 처음 붙였으므로, 카카오톡/디스코드/슬랙/모바일 브라우저에서 제목·설명·썸네일이 기대대로 보이는지 한 번 더 QA한다.
|
||||
- 파비콘은 `svg + 32px png + apple-touch-icon` 조합으로 정리했으므로, 데스크톱 브라우저 탭과 iOS 홈 화면 추가에서 모두 정상 노출되는지 한 번 더 QA한다.
|
||||
- 티어표 `아이콘 크기`는 이제 저장 데이터로 승격됐으므로, 저장 후 재진입/프리뷰/복사본 생성에서 같은 크기가 유지되는지 한 번 더 QA한다.
|
||||
- 티어표 편집/프리뷰 모바일 열 배지는 새로 붙였으므로, 실제 좁은 화면에서 칸 상단 배지와 아이템 썸네일이 겹치지 않고 열 구분이 자연스러운지 한 번 더 QA한다.
|
||||
- 모바일 열 배지는 같은 구간에서 상단 열 제목을 숨기도록 다시 맞췄으므로, 720px 안팎뿐 아니라 980px 이하 전 구간에서 중복 표기 없이 자연스러운지 한 번 더 QA한다.
|
||||
- 모바일 티어표 편집 레이아웃은 행 라벨 폭을 다시 덮어쓰던 규칙을 걷어냈으므로, 실제 980px 이하 구간에서 행 라벨이 과하게 넓지 않고 칸 폭을 충분히 남기는지 한 번 더 QA한다.
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# 업데이트 로그
|
||||
|
||||
## 2026-04-02 v1.3.88
|
||||
- 중앙 워크스페이스 헤더의 `by zenn` 링크는 공통 카피라이트 footer가 이미 역할을 대신하므로 제거하고, 기본 서브타이틀도 서비스 설명 문구로 정리함.
|
||||
- 홈페이지 공유용 메타를 정리해 `title`, `description`, `canonical`, Open Graph, Twitter 카드 정보를 `tmaker.sori.studio` 기준으로 연결함.
|
||||
- 외부 공유용 `og-card.svg`와 실제 썸네일 `og-card.png`, 브라우저/모바일용 `favicon-32x32.png`, `apple-touch-icon.png`를 추가해 링크 공유와 파비콘 노출을 함께 보강함.
|
||||
|
||||
## 2026-04-02 v1.3.86
|
||||
- 티어표 편집의 `아이콘 크기`는 이제 임시 화면 상태가 아니라 저장 데이터에 함께 포함되며, 저장 후 다시 열기와 프리뷰 화면에서도 같은 크기로 복원되도록 정리함.
|
||||
- 이를 위해 티어표 저장 payload, 서버 검증, DB 저장/조회에 `iconSize`를 추가하고 기존 데이터는 기본값 `80`으로 안전하게 보정되게 맞춤.
|
||||
- 이후 공유 프리뷰 화면이 여전히 80으로 고정되던 문제는 `previewOnly` 레이아웃에서 `--thumb-size` 스타일 바인딩이 빠져 있던 탓이었고, 프리뷰 루트에도 같은 값을 전달해 저장된 크기가 그대로 반영되게 보정함.
|
||||
|
||||
## 2026-04-02 v1.3.83
|
||||
- 티어표 편집/프리뷰 화면에서 열을 여러 개 쓰는 경우, 모바일처럼 좁은 화면에서는 기존 상단 열 헤더만으로 각 칸의 의미를 읽기 어려웠으므로 각 칸 상단에 작은 열 이름 배지를 추가함.
|
||||
- 이 배지는 모바일 구간에서만 보이고 데스크톱 레이아웃은 그대로 유지되므로, 작은 화면에서는 `메인 / 밸런스 / 서포트` 같은 열 맥락을 스크롤 중에도 잃지 않게 정리함.
|
||||
|
||||
@@ -1,13 +1,42 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='16' fill='%230b1220'/%3E%3Cpath d='M18 18h28v8H36v20h-8V26H18z' fill='%23f8fafc'/%3E%3C/svg%3E"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Tier Maker</title>
|
||||
<title>Tier Maker | 게임 템플릿으로 만드는 티어표</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="게임 템플릿과 커스텀 이미지로 티어표를 만들고 저장하고 공유하세요. 관리자 요청과 템플릿 업데이트 흐름까지 한 번에 관리할 수 있습니다."
|
||||
/>
|
||||
<meta name="theme-color" content="#090d16" />
|
||||
<meta name="application-name" content="Tier Maker" />
|
||||
|
||||
<link rel="canonical" href="https://tmaker.sori.studio/" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
|
||||
<meta property="og:site_name" content="Tier Maker" />
|
||||
<meta property="og:locale" content="ko_KR" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://tmaker.sori.studio/" />
|
||||
<meta property="og:title" content="Tier Maker | 게임 템플릿으로 만드는 티어표" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="게임 템플릿과 커스텀 이미지로 티어표를 만들고 저장하고 공유하세요. 관리자 요청과 템플릿 업데이트 흐름까지 한 번에 관리할 수 있습니다."
|
||||
/>
|
||||
<meta property="og:image" content="https://tmaker.sori.studio/og-card.png" />
|
||||
<meta property="og:image:alt" content="Tier Maker 공유 썸네일" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="Tier Maker | 게임 템플릿으로 만드는 티어표" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="게임 템플릿과 커스텀 이미지로 티어표를 만들고 저장하고 공유하세요."
|
||||
/>
|
||||
<meta name="twitter:image" content="https://tmaker.sori.studio/og-card.png" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
BIN
frontend/public/apple-touch-icon.png
Normal file
BIN
frontend/public/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
frontend/public/favicon-32x32.png
Normal file
BIN
frontend/public/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
frontend/public/og-card.png
Normal file
BIN
frontend/public/og-card.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
63
frontend/public/og-card.svg
Normal file
63
frontend/public/og-card.svg
Normal file
@@ -0,0 +1,63 @@
|
||||
<svg width="1200" height="630" viewBox="0 0 1200 630" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="120" y1="60" x2="1080" y2="570" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#141C2B" />
|
||||
<stop offset="1" stop-color="#090D16" />
|
||||
</linearGradient>
|
||||
<linearGradient id="panel" x1="192" y1="156" x2="1024" y2="520" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1C2435" />
|
||||
<stop offset="1" stop-color="#121925" />
|
||||
</linearGradient>
|
||||
<linearGradient id="accent" x1="228" y1="190" x2="686" y2="596" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#A855F7" />
|
||||
<stop offset="1" stop-color="#00FFFF" />
|
||||
</linearGradient>
|
||||
<filter id="blur" x="820" y="-40" width="420" height="420" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="60" result="effect1_foregroundBlur_0_1"/>
|
||||
</filter>
|
||||
<filter id="shadow" x="142" y="106" width="916" height="418" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="14"/>
|
||||
<feGaussianBlur stdDeviation="25"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0196078 0 0 0 0 0.0313726 0 0 0 0 0.0627451 0 0 0 0.42 0"/>
|
||||
<feBlend in2="BackgroundImageFix" result="effect1_dropShadow_0_1"/>
|
||||
<feBlend in="SourceGraphic" in2="effect1_dropShadow_0_1" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<rect width="1200" height="630" rx="40" fill="url(#bg)"/>
|
||||
<circle cx="1030" cy="170" r="120" fill="#00FFFF" fill-opacity="0.18" filter="url(#blur)"/>
|
||||
<circle cx="192" cy="564" r="138" fill="#A855F7" fill-opacity="0.18" filter="url(#blur)"/>
|
||||
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="192" y="156" width="816" height="318" rx="34" fill="url(#panel)"/>
|
||||
<rect x="228" y="190" width="200" height="248" rx="28" fill="#101723"/>
|
||||
<rect x="458" y="190" width="514" height="64" rx="22" fill="#151D2A"/>
|
||||
<rect x="458" y="272" width="514" height="64" rx="22" fill="#151D2A"/>
|
||||
<rect x="458" y="354" width="514" height="84" rx="22" fill="#151D2A"/>
|
||||
|
||||
<rect x="252" y="214" width="152" height="44" rx="18" fill="url(#accent)" fill-opacity="0.18"/>
|
||||
<text x="276" y="243" fill="#F8FAFC" font-size="26" font-weight="700" font-family="Pretendard, Apple SD Gothic Neo, Inter, sans-serif">S Tier</text>
|
||||
|
||||
<rect x="252" y="280" width="152" height="44" rx="18" fill="#F97316" fill-opacity="0.18"/>
|
||||
<text x="276" y="309" fill="#F8FAFC" font-size="26" font-weight="700" font-family="Pretendard, Apple SD Gothic Neo, Inter, sans-serif">A Tier</text>
|
||||
|
||||
<rect x="252" y="346" width="152" height="44" rx="18" fill="#22C55E" fill-opacity="0.18"/>
|
||||
<text x="276" y="375" fill="#F8FAFC" font-size="26" font-weight="700" font-family="Pretendard, Apple SD Gothic Neo, Inter, sans-serif">B Tier</text>
|
||||
|
||||
<rect x="482" y="206" width="120" height="32" rx="14" fill="#A855F7" fill-opacity="0.14"/>
|
||||
<text x="510" y="228" fill="#C4B5FD" font-size="18" font-weight="700" font-family="Pretendard, Apple SD Gothic Neo, Inter, sans-serif">Template</text>
|
||||
|
||||
<text x="482" y="304" fill="#F8FAFC" font-size="56" font-weight="800" font-family="Pretendard, Apple SD Gothic Neo, Inter, sans-serif">Tier Maker</text>
|
||||
<text x="482" y="344" fill="#A7B0C0" font-size="24" font-weight="500" font-family="Pretendard, Apple SD Gothic Neo, Inter, sans-serif">게임 템플릿과 커스텀 이미지로 바로 만드는 티어표</text>
|
||||
<text x="482" y="394" fill="#E2E8F0" font-size="22" font-weight="600" font-family="Pretendard, Apple SD Gothic Neo, Inter, sans-serif">저장하고, 공유하고, 요청까지 한 번에</text>
|
||||
|
||||
<rect x="482" y="410" width="248" height="4" rx="2" fill="url(#accent)"/>
|
||||
|
||||
<rect x="742" y="380" width="200" height="42" rx="20" fill="#00FFFF" fill-opacity="0.12"/>
|
||||
<text x="774" y="407" fill="#B6FFFF" font-size="19" font-weight="700" font-family="Pretendard, Apple SD Gothic Neo, Inter, sans-serif">tmaker.sori.studio</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -235,7 +235,7 @@ const routeMeta = computed(() => {
|
||||
}
|
||||
return {
|
||||
title: 'Tier Maker',
|
||||
subtitle: 'by zenn',
|
||||
subtitle: '게임 템플릿으로 만드는 티어표',
|
||||
contextTitle: 'Workspace',
|
||||
contextText: '현재 화면에 맞는 도구와 안내를 여기에 배치할 수 있습니다.',
|
||||
actionLabel: '홈으로',
|
||||
@@ -491,15 +491,6 @@ function submitGlobalSearch() {
|
||||
<header class="workspaceHead railHeader">
|
||||
<div class="workspaceHead__brand" @click="$router.push('/')">
|
||||
<span class="workspaceHead__brandTitle">Tier Maker</span>
|
||||
<a
|
||||
class="workspaceHead__brandSub"
|
||||
href="https://zenn.town/@murabito"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
@click.stop
|
||||
>
|
||||
by zenn
|
||||
</a>
|
||||
</div>
|
||||
<div class="workspaceHead__actions">
|
||||
<div v-if="showGameHubViewToggle" class="viewToggle" role="group" aria-label="티어표 보기 방식">
|
||||
@@ -1137,18 +1128,6 @@ function submitGlobalSearch() {
|
||||
color: var(--theme-text-strong);
|
||||
}
|
||||
|
||||
.workspaceHead__brandSub {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--theme-text-muted);
|
||||
text-decoration: none;
|
||||
transition: color 180ms ease, opacity 180ms ease;
|
||||
}
|
||||
|
||||
.workspaceHead__brandSub:hover {
|
||||
color: var(--theme-text);
|
||||
}
|
||||
|
||||
.workspaceHead__actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
@@ -678,6 +678,7 @@ function buildPayload(existingId) {
|
||||
description: (description.value || '').trim(),
|
||||
isPublic: !!isPublic.value,
|
||||
showCharacterNames: !!showCharacterNames.value,
|
||||
iconSize: Number(iconSize.value || 80),
|
||||
sourceTierListId: sourceTierListId.value || '',
|
||||
sourceSnapshotTitle: sourceSnapshotTitle.value || '',
|
||||
sourceSnapshotAuthor: sourceSnapshotAuthor.value || '',
|
||||
@@ -923,6 +924,7 @@ onMounted(() => {
|
||||
description.value = t.description || ''
|
||||
isPublic.value = !!t.isPublic
|
||||
showCharacterNames.value = !!t.showCharacterNames
|
||||
iconSize.value = Number(t.iconSize || 80)
|
||||
authorName.value = t.authorName || ''
|
||||
authorAccountName.value = t.authorAccountName || ''
|
||||
updatedAt.value = Number(t.updatedAt || 0)
|
||||
@@ -958,7 +960,7 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section v-if="previewMode" class="previewOnly">
|
||||
<section v-if="previewMode" class="previewOnly" :style="{ '--thumb-size': `${iconSize}px` }">
|
||||
<div class="previewOnly__sheet">
|
||||
<div class="previewOnly__title">{{ effectiveTitle }}</div>
|
||||
<div v-if="description" class="previewOnly__description">{{ description }}</div>
|
||||
@@ -1004,7 +1006,7 @@ onUnmounted(() => {
|
||||
<div v-if="isSaveModalOpen" class="modalOverlay" @click.self="closeSaveModal">
|
||||
<div class="modalCard" role="dialog" aria-modal="true" aria-labelledby="saveModalTitle">
|
||||
<div id="saveModalTitle" class="modalCard__title">저장 완료</div>
|
||||
<div class="modalCard__desc">티어표가 저장되었어요. 이어서 더 수정한 뒤 다시 저장할 수도 있어요.</div>
|
||||
<div class="modalCard__desc">티어표가 저장되었어요.<br />이어서 더 수정한 뒤 다시 저장할 수도 있어요.</div>
|
||||
<div class="modalCard__actions">
|
||||
<button class="btn btn--save" @click="closeSaveModal">확인</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user