Skip to content

Commit 0b2f557

Browse files
authored
feat: show recording (#635)
* feat: show recording * ci: fix chrome executable * fixup * fixup
1 parent 0439727 commit 0b2f557

9 files changed

Lines changed: 69 additions & 27 deletions

File tree

.github/workflows/playwright.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: screenshots
22

33
on:
44
pull_request:
5-
branches: [ main ]
5+
branches: [main]
66

77
jobs:
88
test:
@@ -15,7 +15,8 @@ jobs:
1515
node-version: 22
1616
cache: npm
1717
cache-dependency-path: 2025/package-lock.json
18-
- run: cd 2025 && npm ci && npm run dev &
18+
- run: cd 2025 && npm ci
19+
- run: cd 2025 && npm run dev &
1920
env:
2021
PORT: 3001
2122
- run: cd 2025 && npx playwright install --with-deps

2025/messages/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
"LT": "LT",
5959
"sponsor session": "Sponsor session",
6060
"sponsor LT": "Sponsor LT"
61-
}
61+
},
62+
"recording": "Recording"
6263
},
6364
"venue": {
6465
"placeName": "Gran Tokyo South Tower",

2025/messages/ja.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
"LT": "LT",
5959
"sponsor session": "スポンサーセッション",
6060
"sponsor LT": "スポンサーLT"
61-
}
61+
},
62+
"recording": "録画"
6263
},
6364
"venue": {
6465
"placeName": "グラントウキョウサウスタワー",

2025/src/app/[locale]/talks/[slug]/page.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ export default async function Page({ params }: Props) {
6262
<Chip>{session.talk.language}</Chip>
6363
</p>
6464
</div>
65+
{session.talk.recordingUrl && (
66+
<div className="mt-4 flex flex-col justify-center items-center gap-2">
67+
<h2 className="text-2xl font-bold">{t("recording")}</h2>
68+
<iframe
69+
width="560"
70+
height="315"
71+
src={session.talk.recordingUrl}
72+
title={session.talk.title}
73+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
74+
referrerPolicy="strict-origin-when-cross-origin"
75+
allowFullScreen
76+
/>
77+
</div>
78+
)}
6579
<div className="mt-4">
6680
<Markdown>{session.talk.description}</Markdown>
6781
</div>

2025/src/assets/youtube.png

7.6 KB
Loading

2025/src/components/Footer.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import Image from "next/image";
22
import { useTranslations } from "next-intl";
33
import githubUrl from "@/assets/github.svg";
44
import twitterXUrl from "@/assets/twitter-x.svg";
5+
import youtubeUrl from "@/assets/youtube.png";
56
import {
67
GITHUB_URL,
78
JNA_URL,
89
NODEJS_OFFICIAL_URL,
910
PREVIOUS_URL,
1011
X_URL,
12+
YOUTUBE_URL,
1113
} from "@/constants/external";
1214
import { Link } from "@/i18n/navigation";
1315

@@ -27,6 +29,12 @@ export function Footer() {
2729
href: GITHUB_URL,
2830
target: "_blank",
2931
},
32+
{
33+
icon: youtubeUrl,
34+
alt: "YouTube",
35+
href: YOUTUBE_URL,
36+
target: "_blank",
37+
},
3038
];
3139
const links = [
3240
{

2025/src/components/SessionCard.tsx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import clsx from "clsx";
22
import Image from "next/image";
33
import { useTranslations } from "next-intl";
4+
import youtubeUrl from "@/assets/youtube.png";
45
import { ScheduledSession } from "@/constants/schedule";
56
import { Chip } from "./Chip";
67

@@ -45,18 +46,25 @@ export function SessionCard({ session }: { session: ScheduledSession }) {
4546
>
4647
{session.kind === "talk" || session.kind === "streaming" ? (
4748
<div className="flex flex-col gap-1 items-start">
48-
<div className="flex gap-1">
49-
{session.track !== "all" && (
50-
<div className="visible md:hidden">
51-
<Chip size="sm" track={session.track}>
52-
{tTrack(session.track)}
53-
</Chip>
49+
<div className="flex items-center justify-between w-full">
50+
<div className="flex gap-1">
51+
{session.track !== "all" && (
52+
<div className="visible md:hidden">
53+
<Chip size="sm" track={session.track}>
54+
{tTrack(session.track)}
55+
</Chip>
56+
</div>
57+
)}
58+
<Chip size="sm" track={session.track}>
59+
{tKind(session.talk.kind)}
60+
</Chip>
61+
<Chip size="sm">{session.talk.language}</Chip>
62+
</div>
63+
{session.kind !== "streaming" && session.talk.recordingUrl && (
64+
<div className="flex gap-1 justify-end">
65+
<Image src={youtubeUrl} alt="YouTube" width={24} height={24} />
5466
</div>
5567
)}
56-
<Chip size="sm" track={session.track}>
57-
{tKind(session.talk.kind)}
58-
</Chip>
59-
<Chip size="sm">{session.talk.language}</Chip>
6068
</div>
6169
<div className="font-bold text-md">
6270
{session.kind === "streaming" ? `(${t("streaming")}) ` : ""}

2025/src/constants/external.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ export const X_URL = "https://x.com/jsconfjp";
22

33
export const GITHUB_URL = "https://github.com/jsconfjp/jsconf.jp";
44

5+
export const YOUTUBE_URL = "https://www.youtube.com/@jsconfjp";
6+
57
export const NODEJS_OFFICIAL_URL = "https://nodejs.org/";
68

79
export const JNA_URL = "https://nodejs.jp/";

2025/src/constants/talks.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import baseballyamaAvatar from "../../public/speaker/baseballyama.jpg";
1111
import berlysiaAvatar from "../../public/speaker/berlysia.jpg";
1212
import blagojJovanovAvatar from "../../public/speaker/blagoj-jovanov.jpg";
1313
import canalunAvatar from "../../public/speaker/canalun.png";
14-
import ctcpipAvatar from "../../public/speaker/ctcpip.png"
14+
import ctcpipAvatar from "../../public/speaker/ctcpip.png";
1515
import deanSrebnikAvatar from "../../public/speaker/dean-srebnik.jpg";
16-
import devsnekAvatar from "../../public/speaker/devsnek.jpg"
17-
import DmitryMakhnevAvatar from "../../public/speaker/DmitryMakhnev.jpg"
16+
import devsnekAvatar from "../../public/speaker/devsnek.jpg";
17+
import DmitryMakhnevAvatar from "../../public/speaker/DmitryMakhnev.jpg";
1818
import GiovanniLaquidaraAvatar from "../../public/speaker/Giovanni-Laquidara.png";
1919
import islandryuAvatar from "../../public/speaker/islandryu.jpg";
2020
import itaiSatatiAvatar from "../../public/speaker/itai-satati.jpg";
@@ -24,7 +24,7 @@ import joyeeCheungAvatar from "../../public/speaker/joyee-cheung.jpg";
2424
import jxckAvatar from "../../public/speaker/jxck.svg";
2525
import kazukiImamuraAvatar from "../../public/speaker/kazuki-imamura.jpg";
2626
import kojiKojiAvatar from "../../public/speaker/koji-koji.jpg";
27-
import legendecasAvatar from "../../public/speaker/legendecas.png"
27+
import legendecasAvatar from "../../public/speaker/legendecas.png";
2828
import leoKettmeirAvatar from "../../public/speaker/leo-kettmeir.jpg";
2929
import lucaMugnainiAvatar from "../../public/speaker/luca-mugnaini.jpg";
3030
import manishKumarAvatar from "../../public/speaker/manish-kumar.jpg";
@@ -35,7 +35,7 @@ import olivierFluckigerAvatar from "../../public/speaker/olivier-fluckiger.jpg";
3535
import otaMeshiAvatar from "../../public/speaker/ota-meshi.jpg";
3636
import petamorikenAvatar from "../../public/speaker/petamoriken.jpg";
3737
import progfayAvatar from "../../public/speaker/progfay.png";
38-
import robpalmer2Avatar from "../../public/speaker/robpalmer2.jpg"
38+
import robpalmer2Avatar from "../../public/speaker/robpalmer2.jpg";
3939
import RubenBridgewaterAvatar from "../../public/speaker/RubenBridgewater.jpg";
4040
import sachaGreifAvatar from "../../public/speaker/sacha-greif.jpg";
4141
import shaneCarrAvatar from "../../public/speaker/shane-carr.jpg";
@@ -68,6 +68,7 @@ export type Talk = {
6868
day: Day;
6969
language: Language;
7070
speakers: (Speaker | Sponsor)[];
71+
recordingUrl?: string;
7172
};
7273
export type FlattenedSpeaker = {
7374
talk: Talk;
@@ -509,7 +510,8 @@ Whether you're a web developer looking to expand into TV development or seeking
509510
},
510511
{
511512
slug: "denkiyagi-sponsor-session",
512-
title: "なぜブラウザで帳票を生成したいのか、どのようにブラウザで帳票を生成するのか",
513+
title:
514+
"なぜブラウザで帳票を生成したいのか、どのようにブラウザで帳票を生成するのか",
513515
description: `デンキヤギでは、yagisan-reportsという「ブラウザ単体で動作する帳票エンジン」を開発・販売しています。
514516
帳票エンジンとは、ざっくり言えば「請求書のようなPDFを出力するテンプレートエンジン」です。
515517
@@ -818,7 +820,8 @@ Additionally, I am a maintainer to jsr.io, a modern alternative to npm, where I
818820
},
819821
{
820822
slug: "reiwa-travel-sponsor-session",
821-
title: "AIにNext.js App Router移行を依頼して、失敗した話。 Evolving NEWT’s TypeScript Backend for the AI-Driven",
823+
title:
824+
"AIにNext.js App Router移行を依頼して、失敗した話。 Evolving NEWT’s TypeScript Backend for the AI-Driven",
822825
description: `1. AIにNext.js App Routerの移行を任せた結果、プロンプト調整だけで1ヶ月を費やしてしまい、ログ欠損も発生してしまいました。
823826
この経験を通じて、AI支援の正しい使い方と、人が担うべき判断・理解の順序を見直した話をします。
824827
@@ -905,8 +908,10 @@ In this talk, I’ll walk through how I used the TypeScript tracer to uncover hi
905908
},
906909
{
907910
slug: "aapo-alasuutari",
908-
title: "Out the cave, off the cliff — data-oriented design in Nova JavaScript engine",
909-
description: "In the world of JavaScript, it is easy to forget that our software runs on real hardware, made up of real bits and bytes instead of being intangible shadows of objects on a wall, flickers of code on a screen. JavaScript programs tend to consume a lot of memory, and while much of the blame lies in the developer, a part of that is also the way our JavaScript engines are built. Nova JavaScript engine attempts to find a different path: in this talk we'll look at what JavaScript objects look like in memory, and ponder how much of that shadow of an object we really need or if we're perhaps ready to leave the Programmer's Cave and do away with thinking about shadows? Nova JavaScript engine is also an exploration into what is the price of walking that path: we'll see magnificent performance cliffs, and perhaps plunge off of them if the price is right.",
911+
title:
912+
"Out the cave, off the cliff — data-oriented design in Nova JavaScript engine",
913+
description:
914+
"In the world of JavaScript, it is easy to forget that our software runs on real hardware, made up of real bits and bytes instead of being intangible shadows of objects on a wall, flickers of code on a screen. JavaScript programs tend to consume a lot of memory, and while much of the blame lies in the developer, a part of that is also the way our JavaScript engines are built. Nova JavaScript engine attempts to find a different path: in this talk we'll look at what JavaScript objects look like in memory, and ponder how much of that shadow of an object we really need or if we're perhaps ready to leave the Programmer's Cave and do away with thinking about shadows? Nova JavaScript engine is also an exploration into what is the price of walking that path: we'll see magnificent performance cliffs, and perhaps plunge off of them if the price is right.",
910915
kind: "session",
911916
day: "1",
912917
language: "English",
@@ -915,8 +920,8 @@ In this talk, I’ll walk through how I used the TypeScript tracer to uncover hi
915920
type: "speaker",
916921
name: "Aapo Alasuutari",
917922
avatarUrl: aapoAlasuutariAvatar,
918-
bio: "Aapo Alasuutari is a data-oriented design zealot writing TypeScript by day, with 9 years of experience developing a browser-based automation control system UI at Valmet Automation, but by night he transforms into a Rust developer writing the Nova JavaScript engine."
919-
}
923+
bio: "Aapo Alasuutari is a data-oriented design zealot writing TypeScript by day, with 9 years of experience developing a browser-based automation control system UI at Valmet Automation, but by night he transforms into a Rust developer writing the Nova JavaScript engine.",
924+
},
920925
],
921926
},
922927
{
@@ -1098,7 +1103,8 @@ AI that acts changes the role it plays: from passive responder to proactive team
10981103
{
10991104
slug: "dwango-sponsor-session",
11001105
title: "Media Capture and Streams: W3C仕様と現場での知見",
1101-
description: "Media Capture and Streams API は、Web アプリケーションがカメラやマイクなどのメディアデバイスへアクセスし、MediaStreamTrack/MediaStream といったインターフェースを通じて映像・音声を扱うための仕様です。このセッションでは、標準化仕様に定義されるモデルやライフサイクル、Permissions/Constraints、ImageCapture などの周辺 API を整理しながら、実際にオンライン試験システムの開発で活用した際の体験談・失敗談を合わせて紹介します。",
1106+
description:
1107+
"Media Capture and Streams API は、Web アプリケーションがカメラやマイクなどのメディアデバイスへアクセスし、MediaStreamTrack/MediaStream といったインターフェースを通じて映像・音声を扱うための仕様です。このセッションでは、標準化仕様に定義されるモデルやライフサイクル、Permissions/Constraints、ImageCapture などの周辺 API を整理しながら、実際にオンライン試験システムの開発で活用した際の体験談・失敗談を合わせて紹介します。",
11021108
kind: "sponsor session",
11031109
day: "1",
11041110
language: "Japanese",
@@ -1127,7 +1133,8 @@ In this talk, we’ll take a look at how to use HTML and CSS to build simpler al
11271133
},
11281134
{
11291135
slug: "money-forward-sponsor-session",
1130-
title: "Micro Frontendsで築いた共通基盤の成長と、運用で積み重ねた試行の軌跡",
1136+
title:
1137+
"Micro Frontendsで築いた共通基盤の成長と、運用で積み重ねた試行の軌跡",
11311138
description: `マネーフォワード クラウドでは、複数サービスに共通する承認・ワークフロー機能を共通基盤として切り出し、Web ComponentsをベースにMicro Frontendsを運用しています。
11321139
2023年7月のリリースから現在まで2年以上、実装検証の段階を含めると約3年にわたり、Micro Frontendsを使った設計・統合方式における試行錯誤を積み重ねてきました。
11331140

0 commit comments

Comments
 (0)