Commit Graph

16 Commits

Author SHA1 Message Date
52c6b51e61 feat(ui): roll out light/dark theme to board, publish, rework, discover, etc.
- board: tokenized kanban columns/cards; page-header + 사용법 modal (drag guide)
- publish: tokenized table/tabs; accent tab-active; page-header + 사용법 modal
- rework: tokenized the many dark-assuming inline styles so the editor renders
  correctly in the light theme
- discover/channels/production/channel_detail: tokenized dark-assuming colors
  (white text, translucent-white surfaces, #1e1e2d) for light-theme readability

Verified board/publish/rework/discover render cleanly in light.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 06:07:51 +09:00
616003479b feat(ui): collection — compact category, help modal, fit-to-width table
- Category management collapsed into a slim single-row strip
- "사용법" button opens a reusable help modal (.modal-overlay/.modal-card +
  .help-item) explaining categories, filters, list view, and row actions
- Merge thumbnail+title+channel into one "영상" column and tighten cells so the
  whole table fits the viewport — no horizontal scroll on page or table card
- #mainContent min-width:0 to avoid flex overflow

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 23:12:03 +09:00
fc58a32a20 feat(ui): collection filter toolbar — toggle chips + tidy inline selects
Replace raw checkboxes + stacked-label dropdowns with pill toggle chips
(.chip, accent when checked via :has) and a single aligned toolbar row;
result count as a badge. Adds reusable .toolbar/.chip/.field components.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 22:45:05 +09:00
7497cb08ce fix(ui): collection page readable in light theme
Tokenized dark-assuming inline colors (white title/dropdown text, translucent
white surfaces, #1e1e2d modal) so the collection table renders correctly in the
light theme; header uses shared page-header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 22:39:45 +09:00
adb51943a3 feat(ui): light/dark SaaS design system + redesigned sidebar & dashboard
Phase A+dashboard of the UI redesign:
- variables.css: light-default tokens + [data-theme=dark] overrides; old var
  names aliased to theme tokens so existing markup adapts to both themes
- style.css: refined components (card/btn/nav/badge/bar/table/forms), tokenized
  the dark-assuming rgba colors
- theme toggle: pre-paint init in base.html <head> + toggleTheme() in common.js,
  persisted to localStorage; toggle button in sidebar
- sidebar.html: labeled nav with sections (분석/파이프라인/제작), active state,
  account; Korean labels
- dashboard.html: tokenized inline colors; verified light & dark with real data

Spec: docs/superpowers/specs/2026-06-12-ui-redesign-design.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 22:26:01 +09:00
8178d45209 feat(rework): speech-gap trimming + render, language override
Phase 3: remove "no-talk" gaps (Whisper-segment based, not audio silencedetect
which finds nothing under background music) and render a trimmed (+speed) video
via ffmpeg, with subtitles remapped to match.

- KeepIntervalPlanner + TimelineRemapper (pure, unit-tested): keep/remove plan
  from segments (pad/minGap) and timestamp remap f(t)=t-removedBefore(t)
- GET /{id}/trim-plan (preview: keep/remove/remapped segments/kept duration)
- POST /{id}/render (multipart: file,pad,minGap,speed) -> proxy Python /render
  (ffmpeg trim/atrim+concat+atempo) -> mp4 download; ffmpeg graph validated locally
- rework.html: export panel (speed + speech-gap trim preview + SRT/video export),
  client-side SRT from working segments, language selector (auto/ko/en/zh/ja)
- transcribeFromFile forwards optional language (Whisper auto-detect misfired -> zh)

Spec updated with the audio-silence -> speech-gap design correction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 16:53:32 +09:00
b9aa04d4a3 feat(rework): synced subtitle extraction via file upload + Whisper, SRT export
Phase 1 of subtitle timeline studio. Upload a video in the rework editor →
proxy to Python /transcribe (faster-whisper) → store segments → render a
playback-synced segment list and export CapCut-importable SRT.

- ScriptSegment + SrtFormatter (pure, unit-tested) with speed rescaling
- ChannelVideoScript.segmentsJson column (ddl-auto adds it)
- ChannelService.transcribeFromFile + getSegments/parseSegments
- POST /{id}/transcribe (multipart), GET /{id}/script.srt?speed=, /script now returns segments
- rework.html: upload button, local <video>, segment list, SRT export + speed
- multipart 200MB limit; python.base-url config (PYTHON_BASE_URL)
- repo: findFirstByVideoIdOrderByIdDesc/findAllByVideoId (dedup-safe lookups)

Spec: docs/superpowers/specs/2026-06-12-subtitle-timeline-studio-design.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 16:23:55 +09:00
9828918b97 style: dedupe to shared components (publish badge, filter selects, gap token)
- publish.html: status column uses shared .badge (badge-muted/warning/
  success) instead of its own .st-badge inline-color span
- collection.html / discover.html: drop .filter-sel rules now that the
  shared `select` styling covers them (row-sel kept for compact inline)
- style.css: .gap-2 uses var(--space-2) for token consistency

No behavior change; follow-up cleanup from the design-uplift review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:45:15 +09:00
632a2d4f8a fix: scope form focus rule, robust .hidden, drop unused shadow tokens, funnel amber stage 2026-05-31 01:38:08 +09:00
04891a7838 feat(collection): apply initial filters from URL params (dashboard deep-link target) 2026-05-31 01:01:18 +09:00
8fe90e431f fix(dashboard): null-ratio badge color, escape categoryId, robust refresh selector, animate once 2026-05-31 00:59:20 +09:00
2d6c962567 feat(dashboard): count-up, 3-tone bars, rank/ratio badges, click-through deep links
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 00:53:25 +09:00
2ec3915789 feat(dashboard): add full-set pipeline summary with single endpoint
Add GET /api/dashboard/summary aggregating pipeline status, category
distribution, publish summary, and outperformers in one call. Rewrite
dashboard.html with 5 KPI cards, pipeline funnel, publish status, and
category/source-format breakdowns (CSS bars, no chart lib).

Backend: ChannelVideoRepository counts (shorts/uncategorized),
PublishPackageRepository.countByStatus, pipelineStats shorts/longForm,
CategoryService.distribution, PublishService.dashboardSummary, new
DashboardService + DashboardApiController.

Fix: PublishService.list(null) hit UnsupportedOperationException because
findAll(Sort) uses Criteria, which rejects nullsLast precedence. Route the
no-status path through a @Query method so Sort is appended as HQL ORDER BY
(supports NULLS LAST). Also fixes the latent bug in /api/v1/publish all-list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 22:31:21 +09:00
hehih
f232d20f46 feat(discover): add discovery page for finding rework candidates
New /discover page surfaces collected ChannelVideos for picking rework
targets, filtered by period/min-ratio/source/shorts/unprocessed and
sorted by ratio/velocity/views/recency (EXCLUDED always hidden, null
ratios sorted last). Reuses existing curation endpoints for row actions
(status, bookmark).

- ChannelVideoRepository.discover() JPQL query
- ChannelVideoCurationService.discover() (defaults, limit cap, nullsLast)
- GET /api/v1/channel-videos/discover endpoint
- /discover page route + discover.html + sidebar link
Verified by clean compileJava.
2026-05-30 21:51:28 +09:00
hehih
9bd7e80542 Retire Opal/YtVideo pipeline; ChannelVideo is the single video master
The legacy Opal content pipeline (YtVideo + ScriptGen + OpalDraft/Final/
FinalAsset, driven by AnalysisWorkflowService via hardcoded Google Docs)
is no longer used. The active flow is ChannelVideo: collect -> curate
(board) -> rework -> publish.

Removed:
- service: AnalysisWorkflowService, YtVideoService, external/ExternalApiService(+Impl/Stub)
- web: YtVideoController, VideoActionController (/api/videos), video_detail.html
- web/dto: Video{Response,SearchCondition,AddRequest,DetailResponse},
  FinalAssetResponse, OpalDraftResponse, DraftGenerateRequest
- domain/video: YtVideo, YtVideoRepository, dto/Video{List,Detail}Response
- domain/script: ScriptGen(+Repository)
- domain/opal: OpalDraft/OpalFinal/OpalFinalAsset(+Repositories, dto)

Preserved the active YouTube search by extracting searchYoutubeVideos()
into a new dedicated YoutubeSearchService (no Opal deps); rewired
YoutubeSearchApiController. WebController drops the /videos/{id} Opal
detail route + YtVideoService dependency.

DB note: ddl-auto=update never drops tables, so yt_video / scriptgen /
opal_* remain as orphaned tables (harmless, no data loss). Verified by
clean compileJava + reference sweep across java/html/yml.
2026-05-30 19:51:53 +09:00
hehih
da04dbe15c Baseline before video model consolidation 2026-05-30 18:56:21 +09:00