feat: add configurable operator appearance and layout

This commit is contained in:
2026-07-23 11:14:10 +09:00
parent cf117de144
commit f43483533a
34 changed files with 5942 additions and 740 deletions

View File

@@ -1,12 +1,14 @@
<!doctype html>
<html lang="ko">
<html lang="ko" data-color-theme="system" data-view-mode="automatic"
data-start-workspace="stockCut">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>V-Stock 증권정보송출시스템</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<body data-color-theme="system" data-view-mode="automatic"
data-start-workspace="stockCut">
<main class="operator-shell" aria-label="V-Stock 증권정보송출시스템">
<section id="workspace-region" class="workspace-region" aria-label="작업 영역">
<aside id="workspace-navigation" class="workspace-navigation" role="navigation"
@@ -152,15 +154,10 @@
</svg>
</span>
<div class="workspace-heading-copy">
<small>현재 작업</small>
<h1 id="workspace-title">종목·컷</h1>
</div>
</div>
<div class="playout-connection-summary">
<span class="playout-connection-copy" aria-hidden="true">
<small>송출 연결</small>
<strong>Tornado2</strong>
</span>
<div class="connection-state" id="playout-connection-state" role="status"
aria-label="Tornado2 연결 상태" aria-live="polite"
aria-atomic="true">미연결</div>
@@ -180,6 +177,8 @@
</svg>
검색
</button>
<span id="stock-search-meta" class="search-meta" role="status"
aria-live="polite" aria-atomic="true" hidden></span>
</div>
<div id="stock-results" class="stock-results" role="listbox" aria-label="종목명"></div>
@@ -204,26 +203,12 @@
<section id="catalog-workspace" class="catalog-panel workspace-view"
aria-label="분류별 그래픽" hidden inert>
<label class="expand-line"><input id="catalog-expand-all" type="checkbox" checked> Expand</label>
<div class="category-title">Category</div>
<label class="expand-line"><input id="catalog-expand-all" type="checkbox" checked> 전체 펼치기</label>
<div id="category-tree" class="category-tree" aria-label="원본 카테고리"></div>
</section>
<section id="settings-workspace" class="settings-panel workspace-view"
aria-label="설정" hidden inert>
<div class="settings-intro">
<div class="settings-intro-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" focusable="false">
<circle cx="12" cy="12" r="3"></circle>
<path d="M19 12a7 7 0 0 0-.1-1l2-1.5-2-3.4-2.4 1A8 8 0 0 0 15 6.2L14.7 4h-4l-.3 2.2a8 8 0 0 0-1.5.9l-2.4-1-2 3.4 2 1.5a7 7 0 0 0 0 2l-2 1.5 2 3.4 2.4-1a8 8 0 0 0 1.5.9l.3 2.2h4l.3-2.2a8 8 0 0 0 1.5-.9l2.4 1 2-3.4-2-1.5a7 7 0 0 0 .1-1Z"></path>
</svg>
</div>
<div>
<h2>운영 설정</h2>
<p>이 PC에서 사용할 디자인·설정·배경 폴더와 시작 화면 환경을 관리합니다. 폴더는 Windows 선택 창에서만 지정할 수 있습니다.</p>
</div>
</div>
<div id="operator-settings-message" class="settings-message" role="status"
aria-live="polite" aria-atomic="true" hidden></div>
<div id="operator-settings-restart" class="settings-restart-banner" role="status"
@@ -232,19 +217,14 @@
<path d="M20 11a8 8 0 1 0-2.3 5.7M20 5v6h-6"></path>
</svg>
<div>
<strong>앱을 다시 시작하면 새 폴더가 적용됩니다.</strong>
<span>현재 송출 작업은 그대로 유지되며, 다음 실행부터 변경된 위치를 사용합니다.</span>
<strong>재시작하면 적용됩니다.</strong>
</div>
</div>
<div class="settings-grid">
<article class="settings-card settings-card-wide settings-folder-card">
<div class="settings-card-heading">
<div>
<h3>운영 폴더</h3>
<p>경로를 직접 입력하지 않고 폴더 선택 창으로 안전하게 지정합니다.</p>
</div>
<span class="settings-card-label">로컬 PC</span>
<h3>폴더</h3>
</div>
<div class="settings-folder-list">
@@ -261,17 +241,16 @@
<h4 id="operator-design-folder-title">디자인 폴더</h4>
<span class="settings-folder-kind">Cuts</span>
</div>
<p>송출 장면(.t2s)과 장면별 디자인 자산의 기준 위치입니다.</p>
<div class="settings-folder-value">
<strong id="operator-design-folder-name">앱 기본 디자인 폴더</strong>
<strong id="operator-design-folder-name">앱 기본</strong>
<span id="operator-design-folder-status" class="settings-status-badge neutral">확인 중</span>
<span id="operator-design-folder-source" class="settings-folder-source">앱 기본값</span>
<span id="operator-design-folder-source" class="settings-folder-source" hidden>앱 기본값</span>
</div>
</div>
<div class="settings-folder-actions">
<button id="operator-design-folder-select" class="settings-primary-action"
type="button">폴더 선택</button>
<button id="operator-design-folder-reset" type="button" disabled>기본값</button>
type="button">변경</button>
<button id="operator-design-folder-reset" type="button" disabled>초기화</button>
</div>
</section>
@@ -288,17 +267,16 @@
<h4 id="operator-resource-folder-title">설정 폴더</h4>
<span class="settings-folder-kind">Res</span>
</div>
<p>종목 목록 등 UI 설정과, MmoneyCoder.ini가 있으면 다음 시작부터 적용할 DB 프로필의 위치입니다.</p>
<div class="settings-folder-value">
<strong id="operator-resource-folder-name">앱 기본 설정 폴더</strong>
<strong id="operator-resource-folder-name">앱 기본</strong>
<span id="operator-resource-folder-status" class="settings-status-badge neutral">확인 중</span>
<span id="operator-resource-folder-source" class="settings-folder-source">앱 기본값</span>
<span id="operator-resource-folder-source" class="settings-folder-source" hidden>앱 기본값</span>
</div>
</div>
<div class="settings-folder-actions">
<button id="operator-resource-folder-select" class="settings-primary-action"
type="button">폴더 선택</button>
<button id="operator-resource-folder-reset" type="button" disabled>기본값</button>
type="button">변경</button>
<button id="operator-resource-folder-reset" type="button" disabled>초기화</button>
</div>
</section>
@@ -312,36 +290,62 @@
</div>
<div class="settings-folder-copy">
<div class="settings-folder-title-line">
<h4 id="operator-background-folder-title">운영 배경 폴더</h4>
<h4 id="operator-background-folder-title">배경 폴더</h4>
<span class="settings-folder-kind">VRV/JPG/PNG</span>
</div>
<p>F2에서 선택할 수 있는 검증된 VRV/JPG/PNG 배경의 기준·허용 위치입니다.</p>
<div class="settings-folder-value">
<strong id="operator-background-folder-name">앱 기본 배경 폴더</strong>
<strong id="operator-background-folder-name">자동</strong>
<span id="operator-background-folder-status" class="settings-status-badge neutral">확인 중</span>
<span id="operator-background-folder-source" class="settings-folder-source">앱 기본값</span>
<span id="operator-background-folder-source" class="settings-folder-source" hidden>앱 기본값</span>
</div>
</div>
<div class="settings-folder-actions">
<button id="operator-background-folder-select" class="settings-primary-action"
type="button">폴더 선택</button>
<button id="operator-background-folder-reset" type="button" disabled>기본값</button>
type="button">변경</button>
<button id="operator-background-folder-reset" type="button" disabled>초기화</button>
</div>
</section>
</div>
</article>
<article class="settings-card settings-appearance-card">
<div class="settings-card-heading">
<h3>화면</h3>
</div>
<div class="settings-choice-list">
<label class="settings-choice-row" for="operator-color-theme">
<strong>테마</strong>
<select id="operator-color-theme">
<option value="system">자동</option>
<option value="light">라이트</option>
<option value="dark">다크</option>
</select>
</label>
<label class="settings-choice-row" for="operator-view-mode">
<strong>보기</strong>
<select id="operator-view-mode">
<option value="automatic">자동</option>
<option value="compact">컴팩트</option>
<option value="cards">카드</option>
</select>
</label>
<label class="settings-choice-row" for="operator-start-workspace">
<strong>시작</strong>
<select id="operator-start-workspace">
<option value="stockCut">종목·컷</option>
<option value="lastWorkspace">마지막 화면</option>
</select>
</label>
</div>
</article>
<article class="settings-card">
<div class="settings-card-heading">
<div>
<h3>시작 화면</h3>
<p>앱을 열었을 때의 작업 공간 표시 방식을 정합니다.</p>
</div>
<h3>메뉴</h3>
</div>
<label class="settings-switch-row" for="operator-navigation-expanded">
<span>
<strong>왼쪽 메뉴 펼쳐서 시작</strong>
<small>끄면 아이콘만 보이는 간결한 메뉴로 시작합니다.</small>
<strong>메뉴 펼치기</strong>
</span>
<input id="operator-navigation-expanded" type="checkbox" checked>
<span class="settings-switch" aria-hidden="true"></span>
@@ -350,11 +354,7 @@
<article class="settings-card settings-diagnostics-card">
<div class="settings-card-heading">
<div>
<h3>현재 상태</h3>
<p>문제 확인에 필요한 읽기 전용 정보입니다.</p>
</div>
<span class="settings-card-label settings-card-label-readonly">읽기 전용</span>
<h3>상태</h3>
</div>
<dl class="settings-diagnostics">
<div><dt>앱 버전</dt><dd id="operator-settings-app-version">확인 중</dd></div>
@@ -366,24 +366,17 @@
</dl>
</article>
<article class="settings-card settings-card-wide">
<div class="settings-protected-copy">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<rect x="5" y="10" width="14" height="10" rx="2"></rect>
<path d="M8 10V7a4 4 0 0 1 8 0v3M12 14v2"></path>
</svg>
<div>
<h3>보호되는 운영 설정</h3>
<p>DB 자격 증명의 내용은 이 화면에 표시하거나 직접 편집할 수 없습니다. 다만 선택한 설정(Res) 폴더에 검증된 MmoneyCoder.ini가 있으면 일반 실행의 다음 시작부터 그 DB 프로필을 사용합니다. Gate A 검증 실행에서는 선택한 프로필 대신 검증 계획에 고정된 DB 구성을 사용합니다. Tornado2 송출 대상, Live/DryRun 권한과 장면 허용 목록은 변경할 수 없으며 기존 송출 안전 규칙이 계속 적용됩니다.</p>
</div>
</div>
</article>
</div>
</section>
</div>
</section>
</section>
<div id="workspace-splitter" class="workspace-splitter" role="separator"
aria-label="작업 영역과 송출 스케줄 너비 조절" aria-orientation="vertical"
aria-controls="workspace-region" aria-valuemin="34" aria-valuemax="48"
aria-valuenow="39" tabindex="0"></div>
<section class="playlist-panel" aria-label="플레이리스트">
<header class="playlist-heading">
<div class="playlist-heading-icon" aria-hidden="true">
@@ -392,20 +385,33 @@
<path d="M8 9h8M8 12h8M8 15h5"></path>
</svg>
</div>
<div><small>항상 표시</small><h2>송출 스케줄</h2></div>
<div><h2>송출 스케줄</h2></div>
<div class="playlist-state-legend" aria-label="스케줄 상태">
<span class="legend-selected"><i aria-hidden="true"></i>선택</span>
<span class="legend-next"><i aria-hidden="true"></i>다음</span>
<span class="legend-on-air"><i aria-hidden="true"></i>ON AIR</span>
</div>
<div id="playlist-heading-count" class="playlist-heading-count"
role="status" aria-live="polite" aria-atomic="true">총 0개 · 송출 0개</div>
</header>
<div class="playlist-toolbar">
<label><input id="playlist-enable-all" type="checkbox" checked disabled> 전체</label>
<label><input id="moving-average-5" type="checkbox" checked> 5일선</label>
<label><input id="moving-average-20" type="checkbox" checked> 20일선</label>
<button id="playlist-move-up" type="button" disabled aria-label="선택 행 위로 이동"></button>
<button id="playlist-move-down" type="button" disabled aria-label="선택 행 아래로 이동"></button>
<span class="toolbar-spacer"></span>
<label><input id="playout-background-none" type="checkbox" disabled> 배경없음(F3)</label>
<button id="playout-background-file" type="button" disabled>배경파일(F2)</button>
<input id="playout-background-name" class="background-name" value="배경없음" readonly>
<div class="playlist-toolbar-group" aria-label="표시">
<label><input id="playlist-enable-all" type="checkbox" checked disabled> 전체</label>
<label><input id="moving-average-5" type="checkbox" checked> 5일</label>
<label><input id="moving-average-20" type="checkbox" checked> 20일</label>
</div>
<div class="playlist-toolbar-group playlist-toolbar-order" aria-label="순서">
<button id="playlist-move-up" type="button" disabled aria-label="선택 행 위로 이동"></button>
<button id="playlist-move-down" type="button" disabled aria-label="선택 행 아래로 이동"></button>
</div>
<div class="playlist-toolbar-group playlist-toolbar-background" aria-label="배경">
<label><input id="playout-background-none" type="checkbox" disabled
aria-keyshortcuts="F3"> 없음</label>
<button id="playout-background-file" type="button" disabled
aria-keyshortcuts="F2">배경 선택</button>
<input id="playout-background-name" class="background-name" value="배경없음" readonly
aria-label="선택한 배경">
</div>
<label class="fade-control" for="playout-fade-duration" hidden
aria-hidden="true">DissolveTime</label>
<select id="playout-fade-duration" disabled hidden aria-hidden="true" tabindex="-1"
@@ -421,10 +427,15 @@
<option value="16">17</option><option value="17">18</option>
<option value="18">19</option><option value="19">20</option>
</select>
<button id="db-save-button" type="button" disabled>DB 저장</button>
<button id="db-load-button" type="button" disabled>DB 불러오기</button>
<button id="playlist-delete-selected" type="button" disabled>선택삭제(DEL)</button>
<button id="playlist-delete-all" type="button" disabled>전체삭제</button>
<div class="playlist-toolbar-group playlist-toolbar-db" aria-label="DB">
<button id="db-load-button" type="button" disabled>불러오기</button>
<button id="db-save-button" type="button" disabled>저장</button>
</div>
<div class="playlist-toolbar-group playlist-toolbar-danger" aria-label="삭제">
<button id="playlist-delete-selected" type="button" disabled
aria-keyshortcuts="Delete">선택 삭제</button>
<button id="playlist-delete-all" type="button" disabled>전체 삭제</button>
</div>
</div>
<div id="playlist-drop-zone" class="playlist-grid">
@@ -438,11 +449,11 @@
<button id="playout-prepare" class="prepare" type="button" disabled hidden
aria-hidden="true">PREPARE</button>
<button id="playout-take-in" class="take-in" type="button" disabled
aria-label="TAKE IN" aria-keyshortcuts="F8"> TAKE IN [F8]</button>
aria-label="TAKE IN" aria-keyshortcuts="F8"> TAKE IN [F8]</button>
<button id="playout-next" class="next" type="button" disabled
aria-label="NEXT"> NEXT</button>
<button id="playout-take-out" class="take-out" type="button" disabled
aria-label="TAKE OUT" aria-keyshortcuts="Escape">✖ TAKE OUT[Esc]</button>
aria-label="TAKE OUT" aria-keyshortcuts="Escape">✖ TAKE OUT [Esc]</button>
</div>
<div id="playout-status" class="playout-status" aria-live="polite">송출 상태를 확인하고 있습니다.</div>
</section>