Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 6 additions & 24 deletions src/components/ai-edition/v4/V4Timeline.geometry.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function clip(startSec: number, endSec: number) {
}

/** The asset every clip above points at. No `cameraTrack`: this recording has no webcam,
* which is what the Full Camera button is gated on. */
* which is what Add Full Camera is gated on. */
const NO_CAMERA_ASSET = { id: "a1", label: "rec", durationSec: TOTAL_SEC };

/** By default one 30-minute clip carrying a single one-second annotation. */
Expand Down Expand Up @@ -221,29 +221,11 @@ describe("V4Timeline create-from-toolbar", () => {
expect(durationOf(tl)).toBeCloseTo(0.25, 3);
});

// #353. A camera-fullscreen region grows the webcam overlay, so with no webcam on the
// timeline it renders nothing in the preview and nothing in the export — the region is
// stored and forgotten. `addCameraFullscreen` now refuses to write one; the button says
// so before it is clicked instead of looking like it worked.
it("disables Add Full Camera when no clip on the timeline has a camera", () => {
renderTimeline();
expect(screen.getByTitle("buttons.addCameraFullscreen")).toBeDisabled();
});

it("enables Add Full Camera as soon as a clip's asset carries one", () => {
renderTimeline(undefined, undefined, [
{
...NO_CAMERA_ASSET,
cameraTrack: { sourcePath: "/tmp/cam.webm", startMs: 0, offsetMs: 0, visible: true },
},
]);
expect(screen.getByTitle("buttons.addCameraFullscreen")).toBeEnabled();
});

// The disabled button is only half the promise: an empty lane advertises the shortcut
// that fills it, so on a camera-less project it was still inviting a `C` press that
// `addCameraFullscreen` now refuses. It borrows the Layout pane's "No Webcam" wording
// instead, so the two surfaces agree about the same project.
// The toolbar button is gone, but Add Full Camera is still reachable via the `C`
// shortcut, and an empty lane advertises it — so on a camera-less project it was
// still inviting a `C` press that `addCameraFullscreen` refuses (#353). It borrows
// the Layout pane's "No Webcam" wording instead, so the two surfaces agree about
// the same project.
it("does not advertise the C shortcut on a lane that cannot be filled", () => {
renderTimeline();
expect(screen.getByText("layout.noWebcam")).toBeInTheDocument();
Expand Down
20 changes: 4 additions & 16 deletions src/components/ai-edition/v4/V4Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,9 @@ export function V4Timeline({
const clips = tl.clips;
// A camera-fullscreen region grows the webcam overlay, so on a project with no webcam
// it renders nothing in the preview and nothing in the export. `addCameraFullscreen`
// refuses to write one (see useTimeline) — this makes the control say so before it is
// clicked instead of looking like it worked. Same question, same helper as the Layout
// pane: is a camera attached anywhere on this timeline?
// refuses to write one (see useTimeline) — this is why the empty lane's hint below
// swaps to "no webcam" instead of advertising the `C` shortcut. Same question, same
// helper as the Layout pane: is a camera attached anywhere on this timeline?
const hasAnyCamera = useMemo(() => hasAnyClipWithCamera(tl.assets, clips), [tl.assets, clips]);
const total = useMemo(
() =>
Expand Down Expand Up @@ -1359,17 +1359,6 @@ export function V4Timeline({
>
<Crosshair size={15} />
</button>
<button
type="button"
className={styles.tlToolBtn}
title={t("buttons.addCameraFullscreen")}
aria-label={t("buttons.addCameraFullscreen")}
disabled={!hasAnyCamera}
style={!hasAnyCamera ? { opacity: 0.55, cursor: "not-allowed" } : undefined}
onClick={() => void tl.addCameraFullscreen(newRegionDurationSec())}
>
<Maximize2 size={15} />
</button>
</div>
) : (
// Media is an ARRANGING surface: add, remove, reorder. Nothing here
Expand Down Expand Up @@ -1463,8 +1452,7 @@ export function V4Timeline({
<div className={styles.tlLane}>{renderPills(zoomPills, t("hints.pressZoom"))}</div>
<div className={styles.tlLane}>
{/* Advertising "Press C" on a project with no webcam invites a keystroke
that `addCameraFullscreen` now refuses (#353). The toolbar button is
already disabled; this keeps the lane from contradicting it. */}
that `addCameraFullscreen` now refuses (#353). */}
{renderPills(
cameraFullscreenPills,
hasAnyCamera ? t("hints.pressCameraFullscreen") : ts("layout.noWebcam"),
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/ar/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "تفعيل التركيز التلقائي لجميع التكبيرات (الكاميرا تتبع المؤشر)",
"addTrim": "إضافة قص (T)",
"addAnnotation": "إضافة شرح (A)",
"addSpeed": "إضافة سرعة (S)",
"addCameraFullscreen": "إضافة كاميرا كاملة الشاشة (C)"
"addSpeed": "إضافة سرعة (S)"
},
"hints": {
"pressZoom": "اضغط Z لإضافة تكبير",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/en/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "Auto-Focus all zooms (camera follows the cursor)",
"addTrim": "Add Trim (T)",
"addAnnotation": "Add Annotation (A)",
"addSpeed": "Add Speed (S)",
"addCameraFullscreen": "Add Full Camera (C)"
"addSpeed": "Add Speed (S)"
},
"hints": {
"pressZoom": "Press Z to add zoom",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/es/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "Activar enfoque automático para todos los zooms (la cámara sigue el cursor)",
"addTrim": "Agregar recorte (T)",
"addAnnotation": "Agregar anotación (A)",
"addSpeed": "Agregar velocidad (S)",
"addCameraFullscreen": "Agregar cámara a pantalla completa (C)"
"addSpeed": "Agregar velocidad (S)"
},
"hints": {
"pressZoom": "Presiona Z para agregar zoom",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/fr/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "Activer la mise au point automatique pour tous les zooms (la caméra suit le curseur)",
"addTrim": "Ajouter une coupe (T)",
"addAnnotation": "Ajouter une annotation (A)",
"addSpeed": "Ajouter une vitesse (S)",
"addCameraFullscreen": "Ajouter Caméra plein écran (C)"
"addSpeed": "Ajouter une vitesse (S)"
},
"hints": {
"pressZoom": "Appuyez sur Z pour ajouter un zoom",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/it/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "Attiva la messa a fuoco automatica per tutti gli zoom (la fotocamera segue il cursore)",
"addTrim": "Aggiungi taglio (T)",
"addAnnotation": "Aggiungi annotazione (A)",
"addSpeed": "Aggiungi velocità (S)",
"addCameraFullscreen": "Aggiungi Camera a schermo intero (C)"
"addSpeed": "Aggiungi velocità (S)"
},
"hints": {
"pressZoom": "Premi Z per aggiungere zoom",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/ja-JP/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "すべてのズームでオートフォーカスをオンにする(カメラがカーソルに追従)",
"addTrim": "トリムを追加 (T)",
"addAnnotation": "注釈を追加 (A)",
"addSpeed": "再生速度を追加 (S)",
"addCameraFullscreen": "フルスクリーンカメラを追加 (C)"
"addSpeed": "再生速度を追加 (S)"
},
"hints": {
"pressZoom": "Zキーを押してズームを追加",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/ko-KR/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "모든 줌에 자동 초점 켜기 (카메라가 커서를 따라갑니다)",
"addTrim": "트림 추가 (T)",
"addAnnotation": "주석 추가 (A)",
"addSpeed": "속도 추가 (S)",
"addCameraFullscreen": "전체 화면 카메라 추가 (C)"
"addSpeed": "속도 추가 (S)"
},
"hints": {
"pressZoom": "Z를 눌러 줌 추가",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/pt-BR/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "Ativar foco automático para todos os zooms (a câmera segue o cursor)",
"addTrim": "Adicionar Recorte (T)",
"addAnnotation": "Adicionar Anotação (A)",
"addSpeed": "Adicionar Velocidade (S)",
"addCameraFullscreen": "Adicionar Câmera em Tela Cheia (C)"
"addSpeed": "Adicionar Velocidade (S)"
},
"hints": {
"pressZoom": "Pressione Z para adicionar zoom",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/ru/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "Включить автофокус для всех зумов (камера следует за курсором)",
"addTrim": "Добавить обрезку (T)",
"addAnnotation": "Добавить аннотацию (A)",
"addSpeed": "Изменить скорость (S)",
"addCameraFullscreen": "Добавить камеру на весь экран (C)"
"addSpeed": "Изменить скорость (S)"
},
"hints": {
"pressZoom": "Нажмите Z для добавления масштабирования",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/tr/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "Tüm yakınlaştırmalarda Otomatik Odağı aç (kamera imleci takip eder)",
"addTrim": "Kırpma Ekle (T)",
"addAnnotation": "Açıklama Ekle (A)",
"addSpeed": "Hız Ekle (S)",
"addCameraFullscreen": "Tam Ekran Kamera Ekle (C)"
"addSpeed": "Hız Ekle (S)"
},
"hints": {
"pressZoom": "Yakınlaştırma eklemek için Z tuşuna basın",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/vi/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "Bật lấy nét tự động cho tất cả các thu phóng (máy ảnh theo dõi con trỏ)",
"addTrim": "Thêm Cắt (T)",
"addAnnotation": "Thêm Chú thích (A)",
"addSpeed": "Thêm Tốc độ (S)",
"addCameraFullscreen": "Thêm Camera Toàn màn hình (C)"
"addSpeed": "Thêm Tốc độ (S)"
},
"hints": {
"pressZoom": "Nhấn Z để thêm thu phóng",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/zh-CN/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "为所有缩放开启自动对焦(摄像头跟随光标)",
"addTrim": "添加剪辑 (T)",
"addAnnotation": "添加标注 (A)",
"addSpeed": "添加速度 (S)",
"addCameraFullscreen": "添加全屏摄像头 (C)"
"addSpeed": "添加速度 (S)"
},
"hints": {
"pressZoom": "按 Z 添加缩放",
Expand Down
3 changes: 1 addition & 2 deletions src/i18n/locales/zh-TW/timeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
"autoFocusAllOff": "為所有縮放開啟自動對焦(攝影機跟隨游標)",
"addTrim": "新增剪輯 (T)",
"addAnnotation": "新增標註 (A)",
"addSpeed": "新增速度 (S)",
"addCameraFullscreen": "新增全螢幕攝影機 (C)"
"addSpeed": "新增速度 (S)"
},
"hints": {
"pressZoom": "按 Z 新增縮放",
Expand Down
6 changes: 3 additions & 3 deletions src/lib/ai-edition/store/useTimeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,9 +407,9 @@ export function useTimeline() {
// a missing `webcamRect`) and nothing in the export — it just sits in
// `legacyEditor.cameraFullscreenRegions` forever. The agent's `addCameraFullscreen`
// tool already refuses this and says why (electron/ai-edition/agent-tools.ts,
// `noCameraUnderSpan`); the gate lives HERE rather than at each button so both UI
// entry points — the toolbar and the `C` shortcut and any future one are covered
// by construction. `hasAnyClipWithCamera` is the consolidated answer to "does this
// `noCameraUnderSpan`); the gate lives HERE rather than at each caller so the `C`
// shortcut and any future entry point are covered by construction.
// `hasAnyClipWithCamera` is the consolidated answer to "does this
// project have a camera at all", used the same way by the Layout pane.
const addCameraFullscreen = useCallback(
async (durationSec = DEFAULT_NEW_REGION_SEC) => {
Expand Down
Loading