feat(release): prepare 1.1.0 candidate

This commit is contained in:
Yun Chan 2026-08-29 18:33:45 +09:00
parent 5a34f66981
commit 5205dcdfa9
736 changed files with 115667 additions and 12203 deletions

View file

@ -0,0 +1,12 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.d3ro.voice",
"sha256_cert_fingerprints": [
"01:00:19:21:DB:4F:33:40:85:CE:21:E4:B8:DE:CC:BD:71:DA:87:67:C5:6E:3B:59:83:2A:A1:C8:29:EA:0D:AB"
]
}
}
]

View file

@ -0,0 +1,345 @@
:root {
--ink: #08090c;
--panel: #11141c;
--panel-raised: #171b25;
--line: #303647;
--line-soft: rgba(255, 255, 255, 0.07);
--text: #f4f4f5;
--muted: #a1a1aa;
--faint: #71717a;
--accent: #3b82f6;
--accent-hot: #60a5fa;
--success: #4ade80;
--danger: #fb7185;
font-family: "Pretendard Variable", Pretendard, Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: var(--text);
background: var(--ink);
}
* {
box-sizing: border-box;
}
html,
body {
min-height: 100%;
margin: 0;
}
body {
background:
linear-gradient(var(--line-soft) 1px, transparent 1px),
linear-gradient(90deg, var(--line-soft) 1px, transparent 1px),
radial-gradient(circle at 72% 18%, rgba(59, 130, 246, 0.13), transparent 34rem),
var(--ink);
background-size: 40px 40px, 40px 40px, auto, auto;
}
button,
a {
font: inherit;
}
a {
color: inherit;
}
.shell {
min-height: 100vh;
display: grid;
place-items: center;
padding: 32px 18px;
}
.invite-panel {
width: min(100%, 720px);
overflow: hidden;
border: 1px solid var(--line);
border-radius: 18px;
background: linear-gradient(145deg, rgba(23, 27, 37, 0.96), rgba(12, 14, 20, 0.98));
box-shadow: 0 28px 90px rgba(0, 0, 0, 0.48), inset 0 1px rgba(255, 255, 255, 0.06);
}
.brand-row,
.copy-block,
.token-card,
.actions,
.support-row,
.error {
margin-inline: clamp(22px, 7vw, 64px);
}
.brand-row {
min-height: 76px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
border-bottom: 1px solid var(--line-soft);
}
.brand,
.protocol,
.eyebrow,
.token-card dt,
.token-card dd {
font-family: "JetBrains Mono", "Cascadia Mono", Consolas, monospace;
}
.brand {
color: var(--text);
font-size: 14px;
font-weight: 500;
letter-spacing: 0.16em;
text-decoration: none;
}
.protocol {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--muted);
font-size: 10px;
letter-spacing: 0.12em;
}
.signal {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--success);
box-shadow: 0 0 12px rgba(74, 222, 128, 0.72);
}
.route-line {
height: 54px;
display: grid;
grid-template-columns: auto 1fr auto 1fr auto;
align-items: center;
padding-inline: clamp(22px, 7vw, 64px);
background: rgba(0, 0, 0, 0.2);
border-bottom: 1px solid var(--line-soft);
}
.route-node {
width: 9px;
height: 9px;
border: 1px solid var(--line);
border-radius: 50%;
background: var(--panel);
}
.route-node--active {
border-color: var(--accent-hot);
background: var(--accent);
box-shadow: 0 0 18px rgba(59, 130, 246, 0.72);
}
.route-track {
height: 1px;
background: linear-gradient(90deg, var(--accent), var(--line));
}
.copy-block {
padding-top: clamp(42px, 8vw, 72px);
}
.eyebrow {
margin: 0 0 16px;
color: var(--accent-hot);
font-size: 11px;
font-weight: 500;
letter-spacing: 0.15em;
}
h1 {
max-width: 600px;
margin: 0;
font-size: clamp(34px, 7.2vw, 60px);
font-weight: 790;
letter-spacing: -0.045em;
line-height: 1.08;
text-wrap: balance;
}
.description {
max-width: 590px;
margin: 26px 0 0;
color: #d4d4d8;
font-size: 16px;
line-height: 1.72;
word-break: keep-all;
}
.description--en {
margin-top: 8px;
color: var(--faint);
font-size: 13px;
}
.token-card {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1px;
margin-top: 38px;
padding: 1px;
border: 1px solid var(--line);
border-radius: 10px;
background: var(--line);
overflow: hidden;
}
.token-card div {
min-width: 0;
padding: 18px;
background: var(--panel-raised);
}
.token-card dt {
color: var(--faint);
font-size: 9px;
letter-spacing: 0.13em;
}
.token-card dd {
margin: 8px 0 0;
overflow: hidden;
color: var(--text);
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
.token-card dd[data-state="valid"] {
color: var(--success);
}
.token-card dd[data-state="invalid"] {
color: var(--danger);
}
.error {
margin-top: 18px;
padding: 13px 14px;
border: 1px solid rgba(251, 113, 133, 0.35);
border-radius: 8px;
color: #fecdd3;
background: rgba(251, 113, 133, 0.08);
font-size: 14px;
line-height: 1.5;
}
.actions {
display: grid;
grid-template-columns: 1.25fr 1fr;
gap: 12px;
margin-top: 24px;
}
.button {
min-height: 52px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid transparent;
border-radius: 8px;
padding: 0 18px;
font-weight: 750;
text-align: center;
text-decoration: none;
cursor: pointer;
transition: border-color 160ms ease, background 160ms ease, color 160ms ease, transform 160ms ease;
}
.button--primary {
color: #fff;
background: var(--accent);
box-shadow: 0 10px 30px rgba(59, 130, 246, 0.2);
}
.button--primary:hover {
background: var(--accent-hot);
transform: translateY(-1px);
}
.button--secondary {
color: var(--text);
border-color: var(--line);
background: rgba(255, 255, 255, 0.035);
}
.button--secondary:hover {
border-color: #535f7f;
background: rgba(255, 255, 255, 0.06);
}
.button[aria-disabled="true"],
.button:disabled {
opacity: 0.42;
cursor: not-allowed;
pointer-events: none;
transform: none;
}
.button:focus-visible,
.brand:focus-visible,
.support-row a:focus-visible {
outline: 3px solid rgba(255, 115, 66, 0.78);
outline-offset: 3px;
}
.support-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
margin-top: 38px;
padding-block: 22px 28px;
border-top: 1px solid var(--line-soft);
color: var(--faint);
font-size: 13px;
}
.support-row a {
color: #d4d4d8;
font-weight: 650;
text-underline-offset: 4px;
}
@media (max-width: 560px) {
.shell {
align-items: start;
padding: 12px;
}
.invite-panel {
border-radius: 12px;
}
.brand-row {
min-height: 66px;
}
.protocol {
font-size: 8px;
}
.token-card,
.actions {
grid-template-columns: 1fr;
}
.support-row {
align-items: flex-start;
flex-direction: column;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto !important;
transition-duration: 0.01ms !important;
}
}

View file

@ -0,0 +1,76 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="dark" />
<meta name="theme-color" content="#08090c" />
<meta name="robots" content="noindex,nofollow" />
<meta name="referrer" content="no-referrer" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; base-uri 'none'; connect-src 'none'; font-src 'self'; form-action 'none'; img-src 'self' data:; object-src 'none'; script-src 'self'; style-src 'self'"
/>
<title>D3RO Voice — 팀 초대 열기</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="stylesheet" href="/accept-invite.css" />
</head>
<body>
<main class="shell">
<section class="invite-panel" aria-labelledby="invite-title">
<header class="brand-row">
<a class="brand" href="/" aria-label="D3RO Voice 홈">D3RO VOICE</a>
<span class="protocol"><span class="signal" aria-hidden="true"></span>SECURE INVITE</span>
</header>
<div class="route-line" aria-hidden="true">
<span class="route-node route-node--active"></span>
<span class="route-track"></span>
<span class="route-node"></span>
<span class="route-track"></span>
<span class="route-node"></span>
</div>
<div class="copy-block">
<p class="eyebrow">TEAM ACCESS / MOBILE HANDOFF</p>
<h1 id="invite-title">D3RO Voice 앱에서<br />팀 초대를 확인해.</h1>
<p class="description">
초대 수락은 로그인한 계정과 서버 권한을 확인한 뒤에만 완료돼. 이 페이지는 초대 토큰을
저장하거나 수락 결과를 만들지 않아.
</p>
<p class="description description--en" lang="en">
Acceptance is completed only after the app verifies your signed-in account and server permissions.
</p>
</div>
<dl class="token-card" aria-label="초대 링크 상태">
<div>
<dt>LINK STATUS</dt>
<dd id="invite-status" aria-live="polite">검증 중</dd>
</div>
<div>
<dt>TOKEN FINGERPRINT</dt>
<dd id="token-fingerprint"></dd>
</div>
</dl>
<p id="invite-error" class="error" role="alert" hidden></p>
<div class="actions">
<a id="open-app" class="button button--primary" href="#" aria-disabled="true">
D3RO Voice 앱 열기
</a>
<button id="copy-link" class="button button--secondary" type="button" disabled>
초대 링크 복사
</button>
</div>
<footer class="support-row">
<span>앱이 설치되지 않았어?</span>
<a href="/download.html">안전한 설치 파일 받기</a>
</footer>
</section>
</main>
<script src="/accept-invite.js" defer></script>
</body>
</html>

View file

@ -0,0 +1,37 @@
(() => {
'use strict'
const tokenPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
const status = document.getElementById('invite-status')
const fingerprint = document.getElementById('token-fingerprint')
const error = document.getElementById('invite-error')
const openApp = document.getElementById('open-app')
const copyLink = document.getElementById('copy-link')
const token = new URLSearchParams(window.location.search).get('token')?.trim() ?? ''
if (!tokenPattern.test(token)) {
status.textContent = '사용할 수 없는 링크'
status.dataset.state = 'invalid'
error.textContent = '초대 링크가 없거나 형식이 올바르지 않아. 새 초대 링크를 요청해.'
error.hidden = false
return
}
status.textContent = '형식 검증 완료 · 서버 확인 대기'
status.dataset.state = 'valid'
fingerprint.textContent = `${token.slice(0, 8)}${token.slice(-4)}`
openApp.href = `d3ro-voice://accept-invite?token=${encodeURIComponent(token)}`
openApp.removeAttribute('aria-disabled')
copyLink.disabled = false
copyLink.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(window.location.href)
copyLink.textContent = '복사했어'
window.setTimeout(() => { copyLink.textContent = '초대 링크 복사' }, 1800)
} catch {
error.textContent = '브라우저가 복사를 허용하지 않았어. 주소 표시줄에서 링크를 직접 복사해.'
error.hidden = false
}
})
})()

View file

@ -0,0 +1,76 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="dark" />
<meta name="theme-color" content="#08090c" />
<meta name="robots" content="noindex,nofollow" />
<meta name="referrer" content="no-referrer" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; base-uri 'none'; connect-src 'none'; font-src 'self'; form-action 'none'; img-src 'self' data:; object-src 'none'; script-src 'self'; style-src 'self'"
/>
<title>D3RO Voice — 팀 초대 열기</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="stylesheet" href="/accept-invite.css" />
</head>
<body>
<main class="shell">
<section class="invite-panel" aria-labelledby="invite-title">
<header class="brand-row">
<a class="brand" href="/" aria-label="D3RO Voice 홈">D3RO VOICE</a>
<span class="protocol"><span class="signal" aria-hidden="true"></span>SECURE INVITE</span>
</header>
<div class="route-line" aria-hidden="true">
<span class="route-node route-node--active"></span>
<span class="route-track"></span>
<span class="route-node"></span>
<span class="route-track"></span>
<span class="route-node"></span>
</div>
<div class="copy-block">
<p class="eyebrow">TEAM ACCESS / MOBILE HANDOFF</p>
<h1 id="invite-title">D3RO Voice 앱에서<br />팀 초대를 확인해.</h1>
<p class="description">
초대 수락은 로그인한 계정과 서버 권한을 확인한 뒤에만 완료돼. 이 페이지는 초대 토큰을
저장하거나 수락 결과를 만들지 않아.
</p>
<p class="description description--en" lang="en">
Acceptance is completed only after the app verifies your signed-in account and server permissions.
</p>
</div>
<dl class="token-card" aria-label="초대 링크 상태">
<div>
<dt>LINK STATUS</dt>
<dd id="invite-status" aria-live="polite">검증 중</dd>
</div>
<div>
<dt>TOKEN FINGERPRINT</dt>
<dd id="token-fingerprint"></dd>
</div>
</dl>
<p id="invite-error" class="error" role="alert" hidden></p>
<div class="actions">
<a id="open-app" class="button button--primary" href="#" aria-disabled="true">
D3RO Voice 앱 열기
</a>
<button id="copy-link" class="button button--secondary" type="button" disabled>
초대 링크 복사
</button>
</div>
<footer class="support-row">
<span>앱이 설치되지 않았어?</span>
<a href="/download.html">안전한 설치 파일 받기</a>
</footer>
</section>
</main>
<script src="/accept-invite.js" defer></script>
</body>
</html>

View file

@ -0,0 +1,44 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="D3RO Voice 계정 및 데이터 삭제 요청">
<title>D3RO Voice 계정 삭제</title>
<link rel="stylesheet" href="/legal.css">
</head>
<body>
<div class="shell">
<a class="brand" href="/"><span class="brand-mark"></span>D3RO VOICE</a>
<main>
<p class="eyebrow">Account deletion</p>
<h1>계정 및 데이터 삭제</h1>
<p class="lead">D3RO Voice 계정과 연결된 데이터를 앱 안에서 즉시 삭제하거나, 앱에 접근할 수 없는 경우 외부 요청을 제출할 수 있다.</p>
<h2>앱에서 삭제</h2>
<ol>
<li>D3RO Voice에 로그인한다.</li>
<li><strong>설정 → 계정 → 계정 삭제</strong>를 연다.</li>
<li>안내를 확인하고 삭제를 승인한다.</li>
</ol>
<div class="notice"><strong>활성 유료 구독이 있다면 먼저 Google Play의 결제 및 정기 결제에서 구독을 취소해야 한다.</strong> 앱 계정 삭제만으로 스토어 구독이 자동 취소되지는 않는다.</div>
<h2>앱 없이 삭제 요청</h2>
<p>아래 버튼은 YUN CHAN의 D3RO Voice 개인정보 담당자에게 이메일 요청을 연다. 계정에 사용한 이메일 주소를 발신 주소로 사용하고, 본문에 계정 이메일을 적어야 한다. 계정 소유 확인을 마친 뒤 삭제를 처리한다.</p>
<div class="actions">
<a class="button" href="mailto:yunchan8804@gmail.com?subject=D3RO%20Voice%20account%20deletion%20request&amp;body=Account%20email%3A%0A%0AI%20request%20permanent%20deletion%20of%20my%20D3RO%20Voice%20account%20and%20associated%20data.">외부 삭제 요청 보내기</a>
</div>
<h2>삭제되는 데이터</h2>
<p>인증 계정, 프로필, 전사 기록, 회의, 메모, 템플릿, 생성 문서, 팀 소속, 업로드 파일, 기기 및 푸시 등록 등 계정에 연결된 서비스 데이터를 삭제한다.</p>
<h2>제한적으로 보관될 수 있는 데이터</h2>
<p>법적 의무, 결제 분쟁, 보안 또는 부정 사용 방지를 위해 필요한 최소한의 결제·감사 기록은 관련 목적과 의무가 끝날 때까지 제한적으로 보관될 수 있다. 해당 기록은 서비스 개인화나 광고에 사용하지 않는다.</p>
<h2>삭제 후</h2>
<p>삭제는 되돌릴 수 없다. 같은 이메일로 새 계정을 만들더라도 삭제된 콘텐츠는 복구되지 않는다. 기기에 남은 캐시와 녹음 대기열은 앱의 개인정보 정리 절차에서 제거된다.</p>
</main>
<footer><span>© 2026 YUN CHAN · D3RO Voice</span><nav><a href="/privacy/">개인정보처리방침</a><a href="/terms/">이용약관</a><a href="/delete-account/">계정 삭제</a></nav></footer>
</div>
</body>
</html>

View file

@ -1,489 +1,17 @@
<!DOCTYPE html>
<html lang="ko" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>D3RO Voice — Official Download Center & Release History</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&family=Geist:wght@300;400;500;600;700;800;900&family=Pretendard:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
d3ro: {
void: '#05070d',
base: '#090d19',
card: '#0f172a',
cardHover: '#141e36',
border: '#1e293b',
hairline: 'rgba(255, 255, 255, 0.08)',
accent: '#38bdf8',
accentHover: '#0ea5e9',
accentMuted: 'rgba(56, 189, 248, 0.12)',
green: '#22c55e',
amber: '#f59e0b',
text: {
bright: '#ffffff',
primary: '#f1f5f9',
secondary: '#94a3b8',
dim: '#64748b'
}
}
},
fontFamily: {
sans: ['Geist', 'Pretendard', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'sans-serif'],
mono: ['Fira Code', 'monospace']
}
}
}
}
</script>
<style>
body {
background-color: #05070d;
color: #f1f5f9;
font-family: 'Geist', 'Pretendard', sans-serif;
background-image:
radial-gradient(ellipse 80% 50% at 50% -20%, rgba(56, 189, 248, 0.12), transparent 70%),
radial-gradient(circle at 100% 100%, rgba(15, 23, 42, 0.8), transparent 40%);
background-attachment: fixed;
}
.glass-surface {
background: rgba(15, 23, 42, 0.65);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.07);
}
.glass-surface-interactive {
background: rgba(15, 23, 42, 0.65);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.07);
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.glass-surface-interactive:hover {
border-color: rgba(56, 189, 248, 0.35);
background: rgba(20, 30, 54, 0.85);
transform: translateY(-2px);
box-shadow: 0 20px 40px -15px rgba(0, 0, 0, 0.7), 0 0 30px -10px rgba(56, 189, 248, 0.2);
}
.pill-glow {
box-shadow: 0 0 20px -3px rgba(56, 189, 248, 0.4);
}
</style>
</head>
<body class="min-h-screen flex flex-col antialiased selection:bg-sky-500/30 selection:text-sky-200">
<!-- Header -->
<header class="sticky top-0 z-50 glass-surface border-b border-white/5 px-6 py-3.5 flex items-center justify-between">
<div class="flex items-center gap-3.5">
<div class="w-8 h-8 rounded-lg bg-gradient-to-tr from-sky-500 to-blue-600 flex items-center justify-center font-black text-white text-xs shadow-md shadow-sky-500/30">
D3
</div>
<div>
<div class="flex items-center gap-2">
<span class="font-extrabold tracking-tight text-sm text-white">D3RO VOICE</span>
<span class="px-2 py-0.5 rounded-full text-[10px] font-mono font-bold bg-sky-500/10 text-sky-400 border border-sky-500/25">v1.0.0 STABLE</span>
</div>
</div>
</div>
<nav class="hidden md:flex items-center gap-6 text-xs font-medium text-slate-400">
<a href="/" class="hover:text-white transition-colors">Overview</a>
<a href="#changelog" class="hover:text-white transition-colors">Changelog</a>
<a href="#integrity" class="hover:text-white transition-colors">SHA-256 Verifier</a>
<a href="https://git.chanpaca.net/yunchan/d3ro-voice" target="_blank" class="flex items-center gap-1 text-sky-400 hover:text-sky-300 font-mono">
<i data-lucide="git-branch" class="w-3.5 h-3.5"></i> Forgejo Git
</a>
</nav>
<div class="flex items-center gap-3">
<a href="http://admin.chanpaca.net:3001" target="_blank" class="px-3.5 py-1.5 rounded-lg glass-surface hover:border-sky-500/40 text-sky-300 text-xs font-mono font-semibold flex items-center gap-1.5 transition-all">
<i data-lucide="layout-dashboard" class="w-3.5 h-3.5"></i> Admin CRM
</a>
</div>
</header>
<!-- Hero & Primary OS Download Section -->
<main class="flex-1 max-w-6xl mx-auto w-full px-6 pt-12 pb-24">
<!-- Hero Header (Strict 2-line headline, <20 words subtext, no clutter) -->
<div class="text-center max-w-2xl mx-auto mb-10">
<div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-sky-500/10 border border-sky-500/25 text-sky-400 text-xs font-mono font-semibold mb-4">
<span class="w-1.5 h-1.5 rounded-full bg-sky-400 animate-pulse"></span>
OFFICIAL RELEASE • ZERO-LATENCY LOCAL WHISPER
</div>
<h1 class="text-3xl sm:text-4xl md:text-5xl font-black tracking-tight text-white mb-3">
Download <span class="bg-gradient-to-r from-sky-400 to-blue-500 bg-clip-text text-transparent">D3RO Voice</span> for Desktop
</h1>
<p class="text-sm sm:text-base text-slate-400 leading-relaxed">
100% on-device local transcription, intelligent meeting minutes, and multi-network ad rewards.
</p>
</div>
<!-- Primary Hero Download Box (Auto-detected OS card) -->
<div class="max-w-xl mx-auto glass-surface p-6 sm:p-8 rounded-2xl border border-sky-500/30 relative overflow-hidden mb-16 shadow-2xl shadow-sky-950/40">
<div class="absolute -right-16 -top-16 w-48 h-48 bg-sky-500/10 rounded-full blur-3xl pointer-events-none"></div>
<div class="flex items-center justify-between gap-4 mb-6">
<div class="flex items-center gap-3.5">
<div id="osIcon" class="w-12 h-12 rounded-xl bg-sky-500/15 border border-sky-500/30 flex items-center justify-center text-sky-400">
<i data-lucide="monitor" class="w-6 h-6"></i>
</div>
<div>
<div id="detectedOsTitle" class="font-bold text-base sm:text-lg text-white">Windows 64-bit Installer</div>
<div id="detectedOsMeta" class="text-xs text-slate-400 font-mono">D3RO-Voice-Setup-1.0.0-x64.exe • 102 MB • NSIS</div>
</div>
</div>
<span class="px-2.5 py-1 rounded-md text-[10px] font-mono font-bold bg-emerald-500/15 text-emerald-400 border border-emerald-500/30 uppercase tracking-wider">
Recommended
</span>
</div>
<!-- Single Primary Action Button (No Wrap, High Contrast WCAG AA) -->
<a id="primaryDownloadBtn" href="/releases/1.0.0/D3RO-Voice-Setup-1.0.0-x64.exe" download class="w-full py-3.5 px-6 rounded-xl bg-sky-400 hover:bg-sky-300 text-slate-950 font-bold text-sm flex items-center justify-center gap-2 transition-all transform active:scale-[0.98] shadow-lg shadow-sky-500/20">
<i data-lucide="download" class="w-4 h-4"></i>
<span id="downloadBtnText">Download for Windows (v1.0.0)</span>
</a>
<!-- Quick Verify Strip -->
<div class="mt-5 pt-4 border-t border-white/5 flex items-center justify-between text-xs text-slate-400">
<div class="flex items-center gap-1.5 font-mono truncate max-w-[320px]">
<span>SHA256:</span>
<span class="text-slate-300 truncate">b0ac051443151a2e34e8...</span>
<button onclick="copyHash('b0ac051443151a2e34e8192f1fcf795586bbafca6eb21f01a79170c079a53ba2')" class="text-sky-400 hover:text-sky-300 underline font-sans text-[11px] ml-1">Copy</button>
</div>
<span class="text-emerald-400 text-[11px] font-mono flex items-center gap-1">
<i data-lucide="shield-check" class="w-3.5 h-3.5"></i> Signed & Verified
</span>
</div>
</div>
<!-- Platform Packages Grid (Bento Structure with Asymmetry) -->
<div class="mb-20">
<div class="flex items-center justify-between mb-6">
<div>
<h2 class="text-xl font-bold text-white">All Platform Releases</h2>
<p class="text-xs text-slate-400">Optimized standalone packages for desktop operating systems.</p>
</div>
<div class="flex items-center gap-2 text-xs font-mono text-slate-400">
<span class="w-2 h-2 rounded-full bg-emerald-400"></span> Channel: latest.yml Active
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-5">
<!-- Windows Card -->
<div class="glass-surface-interactive p-6 rounded-2xl flex flex-col justify-between">
<div>
<div class="flex items-center justify-between mb-4">
<div class="w-9 h-9 rounded-lg bg-sky-500/10 border border-sky-500/20 flex items-center justify-center text-sky-400">
<i data-lucide="layout-grid" class="w-5 h-5"></i>
</div>
<span class="text-[11px] font-mono text-slate-400">Windows 10 / 11 x64</span>
</div>
<h3 class="font-bold text-base text-white mb-1">Windows</h3>
<p class="text-xs text-slate-400 mb-5 leading-relaxed">
NSIS one-click installer with background delta updates and DirectML GPU acceleration.
</p>
<div class="space-y-2 mb-6">
<a href="/releases/1.0.0/D3RO-Voice-Setup-1.0.0-x64.exe" class="flex items-center justify-between p-2.5 rounded-lg bg-white/[0.03] hover:bg-sky-500/10 border border-white/5 hover:border-sky-500/30 transition-all text-xs text-white">
<div class="flex items-center gap-2 font-medium">
<i data-lucide="download" class="w-3.5 h-3.5 text-sky-400"></i>
<span>Setup Installer (.exe)</span>
</div>
<span class="font-mono text-slate-400 text-[11px]">102 MB</span>
</a>
<a href="/releases/1.0.0/D3RO-Voice-Setup-1.0.0-x64.exe.blockmap" class="flex items-center justify-between p-2 rounded-lg bg-white/[0.02] hover:bg-white/[0.04] border border-white/5 text-[11px] text-slate-400">
<span class="font-mono">Blockmap (.blockmap)</span>
<span class="text-[10px]">105 KB</span>
</a>
</div>
</div>
<div class="pt-3 border-t border-white/5 text-[11px] font-mono text-slate-400 flex justify-between">
<span>Min: 4GB RAM / 1GB Disk</span>
</div>
</div>
<!-- macOS Card -->
<div class="glass-surface-interactive p-6 rounded-2xl flex flex-col justify-between">
<div>
<div class="flex items-center justify-between mb-4">
<div class="w-9 h-9 rounded-lg bg-purple-500/10 border border-purple-500/20 flex items-center justify-center text-purple-400">
<i data-lucide="apple" class="w-5 h-5"></i>
</div>
<span class="text-[11px] font-mono text-slate-400">macOS 12.0+</span>
</div>
<h3 class="font-bold text-base text-white mb-1">macOS (Apple Silicon & Intel)</h3>
<p class="text-xs text-slate-400 mb-5 leading-relaxed">
Metal accelerated build for Apple Silicon M1/M2/M3/M4 and universal Intel DMG.
</p>
<div class="space-y-2 mb-6">
<a href="/releases/1.0.0/D3RO-Voice-1.0.0-arm64.dmg" class="flex items-center justify-between p-2.5 rounded-lg bg-white/[0.03] hover:bg-purple-500/10 border border-white/5 hover:border-purple-500/30 transition-all text-xs text-white">
<div class="flex items-center gap-2 font-medium">
<i data-lucide="download" class="w-3.5 h-3.5 text-purple-400"></i>
<span>Apple Silicon DMG (.dmg)</span>
</div>
<span class="font-mono text-slate-400 text-[11px]">98 MB</span>
</a>
<a href="/releases/1.0.0/D3RO-Voice-1.0.0-arm64-mac.zip" class="flex items-center justify-between p-2 rounded-lg bg-white/[0.02] hover:bg-white/[0.04] border border-white/5 text-[11px] text-slate-400">
<span class="font-mono">Portable Zip (.zip)</span>
<span class="text-[10px]">96 MB</span>
</a>
</div>
</div>
<div class="pt-3 border-t border-white/5 text-[11px] font-mono text-slate-400 flex justify-between">
<span>Metal Acceleration Ready</span>
</div>
</div>
<!-- NAS & Server Card -->
<div class="glass-surface-interactive p-6 rounded-2xl flex flex-col justify-between">
<div>
<div class="flex items-center justify-between mb-4">
<div class="w-9 h-9 rounded-lg bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400">
<i data-lucide="server" class="w-5 h-5"></i>
</div>
<span class="text-[11px] font-mono text-slate-400">Docker / Synology NAS</span>
</div>
<h3 class="font-bold text-base text-white mb-1">Synology NAS & Docker</h3>
<p class="text-xs text-slate-400 mb-5 leading-relaxed">
Self-hosted private deployment package for NAS Container Manager and CRM services.
</p>
<div class="space-y-2 mb-6">
<a href="https://git.chanpaca.net/yunchan/d3ro-voice/src/branch/main/docker-compose.nas.yml" target="_blank" class="flex items-center justify-between p-2.5 rounded-lg bg-white/[0.03] hover:bg-emerald-500/10 border border-white/5 hover:border-emerald-500/30 transition-all text-xs text-white">
<div class="flex items-center gap-2 font-medium">
<i data-lucide="file-code" class="w-3.5 h-3.5 text-emerald-400"></i>
<span>docker-compose.nas.yml</span>
</div>
<span class="font-mono text-emerald-400 text-[11px]">Source →</span>
</a>
<a href="https://git.chanpaca.net/yunchan/d3ro-voice/src/branch/main/docs/deployment/nas-deployment-guide.md" target="_blank" class="flex items-center justify-between p-2 rounded-lg bg-white/[0.02] hover:bg-white/[0.04] border border-white/5 text-[11px] text-slate-400">
<span>NAS Deployment Manual</span>
<span class="text-[10px] text-slate-400">Guide →</span>
</a>
</div>
</div>
<div class="pt-3 border-t border-white/5 text-[11px] font-mono text-slate-400 flex justify-between">
<span>DSM 7.2+ Container Manager</span>
</div>
</div>
</div>
</div>
<!-- Client-Side SHA-256 Verifier (Security & Integrity) -->
<div id="integrity" class="glass-surface p-6 sm:p-8 rounded-2xl border border-sky-500/20 mb-20">
<div class="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 mb-6">
<div>
<div class="flex items-center gap-2 text-sky-400 text-xs font-mono font-bold mb-1">
<i data-lucide="hash" class="w-4 h-4"></i>
CLIENT-SIDE CRYPTOGRAPHIC VERIFICATION
</div>
<h3 class="text-lg font-bold text-white">SHA-256 Binary Integrity Verifier</h3>
<p class="text-xs text-slate-400">Drag & drop your downloaded installer file to compute and compare hash client-side.</p>
</div>
<button onclick="document.getElementById('fileVerifierInput').click()" class="px-3.5 py-2 rounded-lg bg-sky-500/10 hover:bg-sky-500/20 text-sky-300 border border-sky-500/30 text-xs font-semibold flex items-center gap-2 transition-all">
<i data-lucide="file-check" class="w-4 h-4"></i> Select File to Verify
</button>
<input type="file" id="fileVerifierInput" class="hidden" onchange="handleFileVerify(event)">
</div>
<div id="dropZone" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleFileDrop(event)" class="border-2 border-dashed border-slate-700/80 rounded-xl p-6 sm:p-8 text-center transition-all bg-black/20">
<div id="verifyIdleState">
<i data-lucide="upload-cloud" class="w-8 h-8 text-slate-500 mx-auto mb-2"></i>
<p class="text-xs font-medium text-slate-300 mb-1">Drop installer (.exe / .dmg / .zip) here</p>
<p class="text-[11px] text-slate-500 font-mono">Calculated locally in browser via WebCrypto API (no upload)</p>
</div>
<div id="verifyResultState" class="hidden text-left space-y-3 font-mono text-xs">
<div class="flex items-center justify-between">
<span class="text-slate-300 font-bold" id="verifyFileName">D3RO-Voice-Setup-1.0.0-x64.exe</span>
<span id="verifyBadge" class="px-2.5 py-0.5 rounded text-[11px] font-bold"></span>
</div>
<div class="p-3 rounded-lg bg-black/40 border border-white/5 space-y-1.5">
<div class="text-slate-400">Computed Hash: <span class="text-sky-300" id="calculatedHash">-</span></div>
<div class="text-slate-400">Official Hash: <span class="text-emerald-400" id="officialHash">b0ac051443151a2e34e8192f1fcf795586bbafca6eb21f01a79170c079a53ba2</span></div>
</div>
</div>
</div>
</div>
<!-- Release Changelog Timeline -->
<div id="changelog" class="mb-16">
<div class="flex items-center justify-between mb-6">
<div>
<h2 class="text-xl font-bold text-white">Release Changelog & History</h2>
<p class="text-xs text-slate-400">Complete version archives and feature milestones.</p>
</div>
<a href="https://git.chanpaca.net/yunchan/d3ro-voice/releases" target="_blank" class="text-xs text-sky-400 hover:text-sky-300 font-mono underline flex items-center gap-1">
Full Git Tags Archive →
</a>
</div>
<div class="space-y-4">
<!-- v1.0.0 Item -->
<div class="glass-surface p-6 rounded-2xl border-l-4 border-l-sky-400">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-2 mb-3">
<div class="flex items-center gap-3">
<span class="text-lg font-bold text-white font-mono">v1.0.0</span>
<span class="px-2.5 py-0.5 rounded-full text-[10px] font-mono font-bold bg-sky-500/20 text-sky-300 border border-sky-500/30">
LATEST STABLE
</span>
<span class="text-xs text-slate-500 font-mono">2026-08-20</span>
</div>
<a href="/releases/1.0.0/D3RO-Voice-Setup-1.0.0-x64.exe" class="px-3 py-1.5 rounded-lg bg-sky-500/10 hover:bg-sky-500/20 text-sky-300 border border-sky-500/30 text-xs font-semibold flex items-center gap-1.5 transition-all">
<i data-lucide="download" class="w-3.5 h-3.5"></i> Installer (.exe)
</a>
</div>
<div class="space-y-1.5 text-xs text-slate-300 leading-relaxed mb-4">
<p class="font-semibold text-white">Highlights & Features:</p>
<ul class="list-disc list-inside space-y-1 ml-2 text-slate-400">
<li><strong class="text-sky-300">10+ Ad Mediation System</strong>: Parallel header bidding auction (EthicalAds, Carbon, GAM, Playwire, AppLovin, Unity).</li>
<li><strong class="text-sky-300">Free Tier Rewarded Token Refill</strong>: 15s commercial playback grants +50 Cloud AI tokens.</li>
<li><strong class="text-sky-300">Forgejo CI/CD & Synology NAS Packaging</strong>: Multi-platform automated packaging and Docker CRM.</li>
<li><strong class="text-sky-300">100% Local Whisper Engine</strong>: Zero-latency offline speech transcription with hardware acceleration.</li>
</ul>
</div>
<div class="p-2.5 rounded-lg bg-black/30 font-mono text-[11px] text-slate-400 flex items-center justify-between">
<span class="truncate">SHA-256: b0ac051443151a2e34e8192f1fcf795586bbafca6eb21f01a79170c079a53ba2</span>
<span class="text-sky-400">102 MB</span>
</div>
</div>
<!-- v0.2.1-alpha Item -->
<div class="glass-surface p-5 rounded-xl border-l-4 border-l-slate-700 opacity-80">
<div class="flex items-center gap-3 mb-2">
<span class="text-base font-bold text-white font-mono">v0.2.1-alpha</span>
<span class="px-2 py-0.5 rounded text-[10px] font-mono bg-white/5 text-slate-400 border border-white/10">PRE-RELEASE</span>
<span class="text-xs text-slate-500 font-mono">2026-08-15</span>
</div>
<p class="text-xs text-slate-400">
Multi-provider cloud STT drivers dispatch (Deepgram, AssemblyAI, Groq) and cryptographic license verification engine.
</p>
</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="glass-surface border-t border-white/5 py-6 px-6 text-xs text-slate-500">
<div class="max-w-6xl mx-auto flex flex-col sm:flex-row items-center justify-between gap-3">
<div>
<span class="font-bold text-white">D3RO Voice</span> • Copyright © 2026 D3RO. All rights reserved.
</div>
<div class="flex items-center gap-4">
<a href="/privacy" class="hover:text-white transition-colors">Privacy Policy</a>
<a href="/terms" class="hover:text-white transition-colors">Terms</a>
<a href="https://git.chanpaca.net" target="_blank" class="text-sky-400 hover:text-sky-300 font-mono">git.chanpaca.net</a>
</div>
</div>
</footer>
<script>
lucide.createIcons();
const OFFICIAL_HASH = 'b0ac051443151a2e34e8192f1fcf795586bbafca6eb21f01a79170c079a53ba2';
// Auto OS Detection
(function detectOS() {
const userAgent = window.navigator.userAgent.toLowerCase();
const titleEl = document.getElementById('detectedOsTitle');
const metaEl = document.getElementById('detectedOsMeta');
const btnEl = document.getElementById('primaryDownloadBtn');
const btnTextEl = document.getElementById('downloadBtnText');
const iconEl = document.getElementById('osIcon');
if (userAgent.includes('mac') || userAgent.includes('darwin')) {
titleEl.textContent = 'macOS Apple Silicon Installer';
metaEl.textContent = 'D3RO-Voice-1.0.0-arm64.dmg • 98 MB • Apple Silicon (M1/M2/M3/M4)';
btnEl.href = '/releases/1.0.0/D3RO-Voice-1.0.0-arm64.dmg';
btnTextEl.textContent = 'Download for macOS (v1.0.0)';
iconEl.innerHTML = '<i data-lucide="apple" class="w-6 h-6"></i>';
} else if (userAgent.includes('linux')) {
titleEl.textContent = 'Linux Universal Package';
metaEl.textContent = 'D3RO-Voice-1.0.0.AppImage • 105 MB • AppImage';
btnEl.href = '/releases/1.0.0/D3RO-Voice-1.0.0.AppImage';
btnTextEl.textContent = 'Download for Linux (v1.0.0)';
}
lucide.createIcons();
})();
function copyHash(hash) {
navigator.clipboard.writeText(hash).then(() => {
alert('SHA-256 Hash copied to clipboard:\n' + hash);
});
}
function handleDragOver(e) {
e.preventDefault();
document.getElementById('dropZone').classList.add('border-sky-400', 'bg-sky-500/10');
}
function handleDragLeave(e) {
e.preventDefault();
document.getElementById('dropZone').classList.remove('border-sky-400', 'bg-sky-500/10');
}
function handleFileDrop(e) {
e.preventDefault();
document.getElementById('dropZone').classList.remove('border-sky-400', 'bg-sky-500/10');
const files = e.dataTransfer.files;
if (files.length > 0) calculateFileHash(files[0]);
}
function handleFileVerify(e) {
const files = e.target.files;
if (files.length > 0) calculateFileHash(files[0]);
}
async function calculateFileHash(file) {
document.getElementById('verifyIdleState').classList.add('hidden');
document.getElementById('verifyResultState').classList.remove('hidden');
document.getElementById('verifyFileName').textContent = file.name + ' (' + (file.size / (1024*1024)).toFixed(1) + ' MB)';
document.getElementById('calculatedHash').textContent = 'Computing SHA-256 hash locally...';
const badge = document.getElementById('verifyBadge');
badge.textContent = 'Hashing...';
badge.className = 'px-2.5 py-0.5 rounded text-[11px] font-bold bg-amber-500/20 text-amber-300 border border-amber-500/30';
const arrayBuffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
document.getElementById('calculatedHash').textContent = hashHex;
if (hashHex === OFFICIAL_HASH) {
badge.textContent = '✓ OFFICIAL MATCH (GENUINE)';
badge.className = 'px-2.5 py-0.5 rounded text-[11px] font-bold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30';
} else {
badge.textContent = '✓ LOCAL HASH COMPUTED';
badge.className = 'px-2.5 py-0.5 rounded text-[11px] font-bold bg-sky-500/20 text-sky-300 border border-sky-500/30';
}
}
</script>
</body>
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="refresh" content="0; url=/#download" />
<link rel="canonical" href="https://d3ro.chanpaca.net/#download" />
<title>D3RO Voice 릴리스 준비</title>
</head>
<body>
<main>
<h1>D3RO Voice 릴리스 준비</h1>
<p>설치 파일은 서명과 업데이트 경로 검증이 끝난 뒤 공식 페이지에서 제공합니다.</p>
<p><a href="/#download">공식 릴리스 준비 페이지로 이동</a></p>
</main>
</body>
</html>

View file

@ -1,8 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect width="64" height="64" rx="14" fill="#19191b"/>
<circle cx="32" cy="28" r="8" fill="none" stroke="#f25b29" stroke-width="3"/>
<rect x="29" y="36" width="6" height="10" rx="3" fill="#f25b29"/>
<path d="M20 32 Q20 44 32 44 Q44 44 44 32" fill="none" stroke="#f25b29" stroke-width="2.5" stroke-linecap="round"/>
<line x1="32" y1="44" x2="32" y2="52" stroke="#f25b29" stroke-width="2.5" stroke-linecap="round"/>
<line x1="26" y1="52" x2="38" y2="52" stroke="#f25b29" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="32" cy="28" r="8" fill="none" stroke="#3b82f6" stroke-width="3"/>
<rect x="29" y="36" width="6" height="10" rx="3" fill="#3b82f6"/>
<path d="M20 32 Q20 44 32 44 Q44 44 44 32" fill="none" stroke="#3b82f6" stroke-width="2.5" stroke-linecap="round"/>
<line x1="32" y1="44" x2="32" y2="52" stroke="#3b82f6" stroke-width="2.5" stroke-linecap="round"/>
<line x1="26" y1="52" x2="38" y2="52" stroke="#3b82f6" stroke-width="2.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 592 B

After

Width:  |  Height:  |  Size: 592 B

Before After
Before After

111
site/public/legal.css Normal file
View file

@ -0,0 +1,111 @@
:root {
color-scheme: dark;
font-family: Pretendard, "Noto Sans KR", system-ui, -apple-system, sans-serif;
background: #090b0f;
color: #f3f4f6;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
background:
radial-gradient(circle at 80% 0%, rgba(59, 130, 246, 0.11), transparent 34rem),
#090b0f;
}
a { color: #ff8a62; }
a:hover { color: #ffad91; }
.shell {
width: min(920px, calc(100% - 32px));
margin: 0 auto;
padding: 32px 0 72px;
}
.brand {
display: inline-flex;
gap: 10px;
align-items: center;
color: #f8fafc;
font-size: 14px;
font-weight: 500;
letter-spacing: .08em;
text-decoration: none;
}
.brand-mark {
width: 12px;
height: 12px;
border-radius: 3px;
background: #3b82f6;
box-shadow: 0 0 18px rgba(59, 130, 246, .5);
}
main {
margin-top: 28px;
padding: clamp(24px, 5vw, 52px);
border: 1px solid rgba(255,255,255,.09);
border-radius: 24px;
background: rgba(20, 23, 30, .92);
box-shadow: 0 24px 80px rgba(0,0,0,.35);
}
.eyebrow {
margin: 0 0 12px;
color: #ff8a62;
font: 700 11px/1.4 "JetBrains Mono", monospace;
letter-spacing: .14em;
text-transform: uppercase;
}
h1 { margin: 0; font-size: clamp(32px, 6vw, 54px); line-height: 1.05; }
h2 { margin: 40px 0 12px; font-size: 21px; }
h3 { margin: 24px 0 8px; font-size: 16px; }
p, li { color: #c7cbd4; font-size: 15px; line-height: 1.8; }
ul, ol { padding-left: 22px; }
.lead { max-width: 720px; color: #e6e8ed; font-size: 17px; }
.meta { color: #828895; font-size: 13px; }
.notice {
margin: 24px 0;
padding: 18px 20px;
border-left: 3px solid #3b82f6;
border-radius: 8px;
background: rgba(59, 130, 246, .08);
}
.actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }
.button {
display: inline-flex;
min-height: 48px;
align-items: center;
justify-content: center;
padding: 0 18px;
border-radius: 12px;
background: #3b82f6;
color: #fff;
font-weight: 500;
text-decoration: none;
}
.button:hover { background: #ff6b38; color: #fff; }
.button.secondary { background: #282d37; color: #f3f4f6; }
footer {
display: flex;
flex-wrap: wrap;
gap: 18px;
justify-content: space-between;
padding: 22px 4px 0;
color: #777e8b;
font-size: 12px;
}
footer nav { display: flex; flex-wrap: wrap; gap: 14px; }
@media (max-width: 560px) {
.shell { width: min(100% - 20px, 920px); padding-top: 18px; }
main { border-radius: 18px; }
}

View file

@ -0,0 +1,58 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="D3RO Voice 개인정보처리방침">
<title>D3RO Voice 개인정보처리방침</title>
<link rel="stylesheet" href="/legal.css">
</head>
<body>
<div class="shell">
<a class="brand" href="/"><span class="brand-mark"></span>D3RO VOICE</a>
<main>
<p class="eyebrow">Privacy policy</p>
<h1>개인정보처리방침</h1>
<p class="lead">YUN CHAN이 제공하는 D3RO Voice는 음성 기록, 전사, 문서화, 동기화 및 팀 협업 기능을 제공하기 위해 필요한 범위에서 데이터를 처리한다.</p>
<p class="meta">시행일: 2026년 8월 21일 · 최종 수정: 2026년 8월 24일</p>
<h2>1. 처리하는 데이터</h2>
<ul>
<li><strong>계정:</strong> 이메일, 표시 이름, 인증 제공자 식별자, 계정 및 구독 상태</li>
<li><strong>사용자 콘텐츠:</strong> 사용자가 선택하거나 녹음한 음성, 전사문, 메모, 회의, 템플릿, 생성 문서와 팀 데이터</li>
<li><strong>기기 및 알림:</strong> 앱 버전, 기기 등록 정보, 푸시 토큰, 권한 및 오류 상태</li>
<li><strong>결제 및 광고 검증:</strong> 스토어 상품·거래 식별자, 구독 상태, 보상형 광고 검증 식별자. 완전한 카드 번호는 D3RO Voice가 저장하지 않는다.</li>
<li><strong>AI 응답 신고:</strong> 사용자가 신고 화면에서 확인하고 제출한 AI 응답 일부(최대 4,000자), 신고 사유, 선택 입력한 설명(최대 500자), 신고 대상 생성 영수증, 처리 상태와 검토·조치 기록</li>
<li><strong>운영·보안:</strong> 사용량, 요청 결과, 감사 기록, 부정 사용 방지와 장애 진단에 필요한 제한된 로그</li>
</ul>
<h2>2. 이용 목적</h2>
<p>로그인과 계정 관리, 음성 전사·AI 처리, 기기 간 동기화, 팀 협업, 구매·구독 복원, Free 요금제 광고 및 보상 검증, 보안·오류 대응, 사용자 요청 이행에 이용한다. 사용자가 선택하지 않은 파일을 임의로 업로드하지 않는다.</p>
<h2>3. 외부 처리자와 전송</h2>
<p>기능 제공을 위해 Supabase(인증·데이터·파일), Google Play/Firebase/FCM/AdMob(배포·결제·알림·광고), 사용자가 선택한 결제 처리자, 구성된 STT·AI 제공자가 데이터를 처리할 수 있다. 음성 또는 텍스트는 해당 클라우드 기능을 사용한 경우에만 필요한 범위에서 전송된다. 로컬 전사 기능을 선택하면 지원되는 처리는 기기 안에서 수행된다.</p>
<h2>4. 광고</h2>
<p>Free 요금제에는 Google AdMob 광고가 표시될 수 있다. Google Mobile Ads SDK는 광고 기능이 작동할 때 IP 주소(IP 기반 대략적 위치 추정에 이용될 수 있음), 앱 실행·탭·동영상 조회 같은 앱 상호작용, 앱 및 SDK의 성능·오류·진단 정보, Android 광고 ID·앱 세트 ID 등 광고·기기·계정 식별자를 기본적으로 자동 수집하여 Google로 전송할 수 있다. 이 데이터는 광고 제공, 분석, 사기 및 부정 사용 방지 목적으로 처리된다. 광고 동의와 개인정보 옵션은 지원 지역의 요구사항에 따라 앱에서 제공한다. Pro 및 Pro+ 유효 entitlement에서는 앱이 광고 요청을 만들지 않는다.</p>
<h2>5. 보관과 삭제</h2>
<p>계정 데이터는 서비스를 제공하는 동안 보관한다. 사용자가 계정을 삭제하면 인증 계정과 연결된 콘텐츠·기기 등록을 삭제한다. 다만 결제, 분쟁, 보안 및 법적 의무 이행에 필요한 최소 기록은 관련 의무가 끝날 때까지 제한적으로 보관할 수 있으며, 그 목적 외에는 사용하지 않는다.</p>
<p>AI 생성 영수증은 신고할 수 있는 응답과 계정을 확인하기 위해 계정 식별자, 생성 유형, 모델, 발급·만료 시각만 저장하며 원래 프롬프트나 AI 응답 원문을 저장하지 않는다. 영수증은 발급 후 30일 동안 유효하며, 만료되면 권한이 제한된 서비스 전용 정리 절차의 삭제 대상이 된다. 신고 증거와 검토 기록은 상태가 보류 또는 검토 중인 동안 해결 시점까지 보관한다. 신고가 조치 완료 또는 기각으로 해결되면 180일의 이의 제기·부정 사용 대응 기간을 거친 뒤 같은 서비스 전용 정리 절차의 삭제 대상이 된다. 계정이 삭제되면 신고자 및 검토자 계정 식별자는 신고 기록에서 분리되며, 해결 전 신고 또는 앞서 설명한 제한 보관기간이 남은 신고 증거는 해당 목적을 위해 해당 계정 식별자와 분리된 상태로 보관될 수 있다.</p>
<h2>6. AI 응답 신고와 데이터 최소화</h2>
<p>지원되는 AI 응답을 신고할 때 사용자는 신고 대상 응답 일부를 화면에서 미리 확인하고, 신고 사유와 선택 설명을 직접 제출한다. 앱은 신고 과정에서 음성, 전체 대화, 원래 프롬프트를 자동으로 첨부하거나 전송하지 않는다. 신고 데이터는 신고 접수, 정책·안전 검토, 조치, 이의 제기와 부정 사용 방지에만 이용하며, 일반 애플리케이션 로그에 신고 응답 원문을 복사하지 않는다.</p>
<h2>7. 보호 조치와 사용자 권리</h2>
<p>전송 구간 암호화, 사용자별 접근 통제, 최소 권한, 민감 로컬 캐시 암호화, 보안 감사와 삭제 경계를 적용한다. 사용자는 앱에서 자신의 데이터를 조회·내보내기·수정·삭제할 수 있다.</p>
<h2>8. 계정 삭제와 문의</h2>
<p>앱의 <strong>설정 → 계정 → 계정 삭제</strong>를 이용하거나, 앱을 사용할 수 없는 경우 외부 삭제 요청 페이지를 이용할 수 있다. 개인정보 문의는 YUN CHAN의 D3RO Voice 담당자에게 보낼 수 있다.</p>
<div class="actions">
<a class="button" href="/delete-account/">계정 삭제 요청</a>
<a class="button secondary" href="mailto:yunchan8804@gmail.com?subject=D3RO%20Voice%20privacy%20inquiry">개인정보 문의</a>
</div>
</main>
<footer><span>© 2026 YUN CHAN · D3RO Voice</span><nav><a href="/privacy/">개인정보처리방침</a><a href="/terms/">이용약관</a><a href="/delete-account/">계정 삭제</a></nav></footer>
</div>
</body>
</html>

View file

@ -0,0 +1,68 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="D3RO Voice 이용약관">
<title>D3RO Voice 이용약관</title>
<link rel="stylesheet" href="/legal.css">
</head>
<body>
<div class="shell">
<a class="brand" href="/"><span class="brand-mark"></span>D3RO VOICE</a>
<main>
<p class="eyebrow">Terms of service</p>
<h1>이용약관</h1>
<p class="lead">이 약관은 YUN CHAN이 제공하는 D3RO Voice 앱과 관련 서비스의 이용 조건을 정한다.</p>
<p class="meta">시행일: 2026년 8월 21일 · 최종 수정: 2026년 8월 24일</p>
<h2>1. 서비스와 계정</h2>
<p>D3RO Voice는 음성 녹음·가져오기, 전사, AI 처리, 문서화, 동기화, 팀 협업과 구독 기능을 제공한다. 사용자는 정확한 계정 정보를 유지하고 자신의 인증 수단과 기기를 보호해야 한다.</p>
<h2>2. 사용자 콘텐츠</h2>
<p>사용자는 자신이 입력하거나 업로드한 콘텐츠에 대한 권리를 유지한다. 서비스를 제공하고 사용자가 요청한 처리를 수행하는 데 필요한 범위에서만 D3RO Voice가 콘텐츠를 처리하도록 허용한다. 타인의 권리나 법률을 침해하는 콘텐츠를 업로드해서는 안 된다.</p>
<h2>3. 전사와 AI 결과</h2>
<p>자동 전사와 AI 생성 결과는 부정확하거나 불완전하거나 부적절할 수 있으며, 항상 사실 또는 YUN CHAN의 견해를 나타내는 것은 아니다. 사용자는 결과를 사용하거나 공유하기 전에 정확성, 적법성과 적합성을 직접 검토해야 한다. 의료·법률·재무 또는 안전에 중대한 결정을 위한 전문 조언이나 긴급 서비스의 대체 수단으로 사용해서는 안 된다. 지원되는 AI 응답에서 유해하거나 부적절한 결과를 발견하면 앱의 신고 기능을 이용할 수 있다.</p>
<h2>4. 요금제, 구매와 광고</h2>
<ul>
<li>Free 요금제에는 광고와 사용량 제한이 적용될 수 있다.</li>
<li>유료 구독의 가격, 갱신, 취소와 환불은 구매 화면 및 Google Play 또는 해당 결제 처리자의 조건에 따른다.</li>
<li>구독 취소와 계정 삭제는 별개다. 활성 구독이 있다면 스토어에서 먼저 취소해야 계정 삭제가 가능할 수 있다.</li>
<li>광고 보상은 서버 검증이 완료된 경우에만 부여된다.</li>
</ul>
<h2>5. 허용되지 않는 사용</h2>
<p>다음 콘텐츠를 생성·업로드·공유하거나 이를 목적으로 서비스를 이용해서는 안 된다.</p>
<ul>
<li>불법 행위 또는 범죄를 조장하거나 수행하기 위한 콘텐츠</li>
<li>아동 성적 학대·착취, 미성년자의 성적 대상화 또는 이를 묘사·조장하는 콘텐츠</li>
<li>비동의 성적 콘텐츠, 음란물 또는 성적 착취를 조장하는 콘텐츠</li>
<li>보호되는 특성을 이유로 한 혐오·차별, 괴롭힘, 위협 또는 표적 학대를 조장하는 콘텐츠</li>
<li>폭력, 테러, 자해 또는 타해를 조장하거나 구체적으로 돕는 콘텐츠</li>
<li>사기, 스팸, 피싱, 악성코드 또는 기만적 행위를 위한 콘텐츠</li>
<li>개인정보, 초상권, 저작권 등 타인의 권리 또는 비밀을 침해하는 콘텐츠</li>
</ul>
<p>타인 계정 침해, 서비스·결제·광고 검증 우회, 악성코드 배포, 과도한 자동 요청, 역설계로 보안 조치를 우회하는 행위도 금지한다.</p>
<h2>6. 신고, 검토와 조치</h2>
<p>사용자는 지원되는 AI 응답 화면의 신고 기능으로 유해성, 성적 콘텐츠, 혐오, 폭력, 자해, 허위정보, 개인정보 침해, 스팸 또는 그 밖의 문제를 알릴 수 있다. 신고는 운영 검토 대상으로 접수되며, 확인 결과와 관련 법률·정책에 따라 신고를 기각하거나 콘텐츠·기능·계정 이용을 제한하는 등 필요한 조치를 할 수 있다. 선의로 제출한 신고에는 불이익을 주지 않지만, 타인을 괴롭히거나 보복하기 위한 신고, 고의로 허위인 신고 또는 반복적인 신고 기능 남용은 제한할 수 있다.</p>
<h2>7. 변경, 중단과 책임</h2>
<p>보안, 법률, 제공자 정책 또는 서비스 개선을 위해 기능을 변경할 수 있다. 합리적인 범위에서 중요한 변경을 알린다. 외부 네트워크·스토어·AI 제공자의 장애로 일부 기능이 일시적으로 제한될 수 있으며, D3RO Voice는 이를 성공으로 가장하지 않고 명시적인 오류로 처리한다.</p>
<h2>8. 종료와 데이터</h2>
<p>사용자는 언제든 구독을 취소하거나 계정을 삭제할 수 있다. 중대한 약관 위반이나 보안 위험이 있는 경우 관련 법률과 정책에 따라 서비스 이용을 제한할 수 있다. 데이터 처리와 삭제는 개인정보처리방침을 따른다.</p>
<h2>9. 준거와 문의</h2>
<p>강행 법규가 달리 정하지 않는 한 대한민국 법률을 적용한다. 서비스 또는 약관 문의는 아래 연락 경로를 이용한다.</p>
<div class="actions">
<a class="button" href="mailto:yunchan8804@gmail.com?subject=D3RO%20Voice%20terms%20inquiry">문의하기</a>
<a class="button secondary" href="/privacy/">개인정보처리방침</a>
</div>
</main>
<footer><span>© 2026 YUN CHAN · D3RO Voice</span><nav><a href="/privacy/">개인정보처리방침</a><a href="/terms/">이용약관</a><a href="/delete-account/">계정 삭제</a></nav></footer>
</div>
</body>
</html>