Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
058792c
test(whats-new): a Ledger row shows its whole description with its pi…
teamleaderleo Oct 21, 2026
e5d163f
What's New: the Ledger uses the pane's width, pictures beside their c…
teamleaderleo Oct 21, 2026
2799856
test(whats-new): type the Ledger row test's notes like the other chan…
teamleaderleo Oct 21, 2026
97621a8
What's New Ledger: a row's actions end its last line, never a line of…
teamleaderleo Oct 21, 2026
502f59b
What's New: row actions are a span, valid inside the Ledger's paragraph
teamleaderleo Oct 21, 2026
818ce5b
test(whats-new): the Ledger row test waits for its row, not 20 ms
teamleaderleo Oct 21, 2026
92f2afd
Merge remote-tracking branch 'origin/feat-cmux-next' into wn/whats-ne…
teamleaderleo Oct 21, 2026
e151590
What's New Ledger: a row's picture stands next to its text; the lead …
teamleaderleo Oct 21, 2026
bed59b3
What's New Ledger: a row's text keeps at least 36ch beside its picture
teamleaderleo Oct 21, 2026
7981359
test(whats-new): the Ledger row test checks for its row between acts,…
teamleaderleo Oct 21, 2026
f4af624
test(whats-new): a change's picture opens enlarged, and a digest entr…
teamleaderleo Oct 21, 2026
4809da7
What's New: click a change's picture to enlarge it; digest entries sh…
teamleaderleo Oct 21, 2026
6f694cc
Merge commit '59965a4a4b3019ae335171a32119a44d87f6336e' into wn/whats…
teamleaderleo Oct 21, 2026
becf144
What's New: the enlarged picture opens over the page; a highlight and…
teamleaderleo Oct 22, 2026
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
264 changes: 264 additions & 0 deletions webviews/src/pages/changelog/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -6460,6 +6460,270 @@
}
}
}
},
"changelog.page.close": {
"comment": "Changelog page: closes the enlarged picture.",
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Close"
}
},
"ar": {
"stringUnit": {
"state": "translated",
"value": "إغلاق"
}
},
"bs": {
"stringUnit": {
"state": "translated",
"value": "Zatvori"
}
},
"da": {
"stringUnit": {
"state": "translated",
"value": "Luk"
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "Schließen"
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Cerrar"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Fermer"
}
},
"it": {
"stringUnit": {
"state": "translated",
"value": "Chiudi"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "閉じる"
}
},
"km": {
"stringUnit": {
"state": "translated",
"value": "បិទ"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "닫기"
}
},
"nb": {
"stringUnit": {
"state": "translated",
"value": "Lukk"
}
},
"pl": {
"stringUnit": {
"state": "translated",
"value": "Zamknij"
}
},
"pt-BR": {
"stringUnit": {
"state": "translated",
"value": "Fechar"
}
},
"ru": {
"stringUnit": {
"state": "translated",
"value": "Закрыть"
}
},
"th": {
"stringUnit": {
"state": "translated",
"value": "ปิด"
}
},
"tr": {
"stringUnit": {
"state": "translated",
"value": "Kapat"
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": "Закрити"
}
},
"vi": {
"stringUnit": {
"state": "translated",
"value": "Đóng"
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "关闭"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "關閉"
}
}
}
},
"changelog.page.enlarge": {
"comment": "Changelog page: label of a change's picture, which opens it enlarged; the picture's description follows.",
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Enlarge picture"
}
},
"ar": {
"stringUnit": {
"state": "translated",
"value": "تكبير الصورة"
}
},
"bs": {
"stringUnit": {
"state": "translated",
"value": "Uvećaj sliku"
}
},
"da": {
"stringUnit": {
"state": "translated",
"value": "Forstør billedet"
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "Bild vergrößern"
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Ampliar imagen"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Agrandir l’image"
}
},
"it": {
"stringUnit": {
"state": "translated",
"value": "Ingrandisci immagine"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "画像を拡大"
}
},
"km": {
"stringUnit": {
"state": "translated",
"value": "ពង្រីករូបភាព"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "이미지 확대"
}
},
"nb": {
"stringUnit": {
"state": "translated",
"value": "Forstørr bildet"
}
},
"pl": {
"stringUnit": {
"state": "translated",
"value": "Powiększ obraz"
}
},
"pt-BR": {
"stringUnit": {
"state": "translated",
"value": "Ampliar imagem"
}
},
"ru": {
"stringUnit": {
"state": "translated",
"value": "Увеличить изображение"
}
},
"th": {
"stringUnit": {
"state": "translated",
"value": "ขยายภาพ"
}
},
"tr": {
"stringUnit": {
"state": "translated",
"value": "Resmi büyüt"
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": "Збільшити зображення"
}
},
"vi": {
"stringUnit": {
"state": "translated",
"value": "Phóng to hình ảnh"
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "放大图片"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "放大圖片"
}
}
}
}
},
"version": "1.0"
Expand Down
74 changes: 66 additions & 8 deletions webviews/src/pages/changelog/ReleaseParts.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// The pieces every changelog design shares (cx-urd.61): release meta, the hero, the grouped
// change rows and the folded commit list by area. Colors are theme tokens, sizes the type scale
// (styles.css); nothing here knows which design it sits in.
import { useState, useSyncExternalStore } from "react";
import { useRef, useState, useSyncExternalStore } from "react";
import { Dialog } from "../../ui/Dialog";
import { DialogButton } from "../../ui/DialogButton";
import type { Strings } from "../shared/i18n";
import { getColorScheme, subscribeColorScheme } from "./colorScheme";
import { byArea } from "./areas";
Expand Down Expand Up @@ -31,21 +33,77 @@ export function ReleaseMeta({ date, version, strings }: { date: string; version:
);
}

/** The release's picture in the theme's scheme; the box keeps its ratio before the file arrives. */
export function Media({ media, className }: { media: MediaRef; className?: string }) {
/** The release's picture in the theme's scheme; the box keeps its ratio before the file arrives. It
loads as it nears the viewport. Given `strings`, a click (or Return) opens it enlarged. */
export function Media({ media, className, strings }: { media: MediaRef; className?: string; strings?: Strings }) {
const scheme = useSyncExternalStore(subscribeColorScheme, getColorScheme, () => "light" as const);
const src = scheme === "dark" ? media.dark : media.light;
const [open, setOpen] = useState(false);
const picture = media.video ? (
<video key={src} src={src} aria-label={media.alt} muted loop playsInline autoPlay preload="none" />
) : (
<img key={src} src={src} alt={media.alt} loading="lazy" decoding="async" />
);
return (
<figure className={`cl-media${className ? ` ${className}` : ""}`}>
{media.video ? (
<video key={src} src={src} aria-label={media.alt} muted loop playsInline autoPlay preload="none" />
{strings ? (
<button
type="button"
className="cl-media-open"
aria-label={
media.alt ? `${strings.t("changelog.page.enlarge")}: ${media.alt}` : strings.t("changelog.page.enlarge")
}
onClick={() => setOpen(true)}
>
{picture}
</button>
) : (
<img key={src} src={src} alt={media.alt} loading="lazy" decoding="async" />
picture
)}
{strings && open && <MediaLightbox media={media} src={src} strings={strings} onClose={() => setOpen(false)} />}
</figure>
);
}

/** A picture enlarged to the window, on glass over the page; Escape, Close or a press outside closes it. */
function MediaLightbox({
media,
src,
strings,
onClose,
}: {
media: MediaRef;
src: string;
strings: Strings;
onClose(): void;
}) {
const close = useRef<HTMLButtonElement | null>(null);
return (
<Dialog
open
onOpenChange={(open) => !open && onClose()}
label={media.alt}
className="cl-lightbox"
backdropClassName="cl-lightbox-backdrop"
initialFocus={close}
>
<header className="cl-lightbox-bar">
<span className="cl-lightbox-alt">{media.alt}</span>
<DialogButton ref={close} dialogKey="escape" className="cl-button" onClick={onClose}>
{strings.t("changelog.page.close")}
</DialogButton>
</header>
<div className="cl-lightbox-stage">
{media.video ? (
<video src={src} aria-label={media.alt} muted loop playsInline autoPlay controls />
) : (
<img src={src} alt={media.alt} />
)}
</div>
</Dialog>
);
}

export function ItemActions({ item, ctx }: { item: ReleaseItem; ctx: PartsContext }) {
if (!item.action && !item.docs) return null;
return (
Expand Down Expand Up @@ -123,7 +181,7 @@ export function Hero({
{p}
</p>
))}
{item.media && <Media media={item.media} />}
{item.media && <Media media={item.media} strings={ctx.strings} />}
<ItemActions item={item} ctx={ctx} />
</>
);
Expand Down Expand Up @@ -191,7 +249,7 @@ function WideRow({ item, ctx }: { item: ReleaseItem; ctx: PartsContext }) {
</p>
))}
</div>
{item.media && <Media media={item.media} className="cl-row-media" />}
{item.media && <Media media={item.media} className="cl-row-media" strings={ctx.strings} />}
</li>
);
}
Expand Down
2 changes: 2 additions & 0 deletions webviews/src/pages/changelog/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { MockChangelogProvider } from "./mockProvider";
import { ChangelogStore, parseDesign, parseSpan } from "./store";
import { mountUpdateSurface, parseSurface } from "./surface";
import "../shared/pageBase.css";
// The enlarged picture is a ui/Dialog: this sheet places a dialog and its backdrop over the page.
import "../../ui/popupSurface.css";
import "./styles.css";

export function mountChangelogPage(root: HTMLElement, client: PageClient | null = defaultClient()): ChangelogStore {
Expand Down
Loading