diff --git a/src/assets/hero/tama-dark.webp b/src/assets/hero/tama-dark.webp
new file mode 100644
index 0000000..26c59d8
Binary files /dev/null and b/src/assets/hero/tama-dark.webp differ
diff --git a/src/assets/hero/tama-light.webp b/src/assets/hero/tama-light.webp
new file mode 100644
index 0000000..47494a3
Binary files /dev/null and b/src/assets/hero/tama-light.webp differ
diff --git a/src/assets/logos/sonae.png b/src/assets/logos/sonae.png
new file mode 100644
index 0000000..1114a2e
Binary files /dev/null and b/src/assets/logos/sonae.png differ
diff --git a/src/assets/works/sonae.webp b/src/assets/works/sonae.webp
new file mode 100644
index 0000000..82f7ec9
Binary files /dev/null and b/src/assets/works/sonae.webp differ
diff --git a/src/components/Footer.astro b/src/components/Footer.astro
index 830a171..6f93211 100644
--- a/src/components/Footer.astro
+++ b/src/components/Footer.astro
@@ -23,7 +23,7 @@ const text = t(lang);
{text.nav.company} ·
{text.footer.privacy} ·
{text.footer.tokushoho}:
- {services.map((s, i) => (<>{i > 0 && "・"}{text.services[s.id].name}>))} ·
+ {services.filter((s) => s.legal).map((s, i) => (<>{i > 0 && "・"}{text.services[s.id].name}>))} ·
{text.footer.copyright}
diff --git a/src/components/ServiceTour.astro b/src/components/ServiceTour.astro
index beae3e0..bd94a24 100644
--- a/src/components/ServiceTour.astro
+++ b/src/components/ServiceTour.astro
@@ -1,6 +1,8 @@
---
import BrowserFrame from "@components/BrowserFrame.astro";
import { type Lang, t } from "@i18n/translation";
+import tamaDark from "@/assets/hero/tama-dark.webp";
+import tamaLight from "@/assets/hero/tama-light.webp";
import todoroku from "@/assets/hero/todoroku.webp";
import worklogDark from "@/assets/hero/worklog-dark.webp";
import worklogLight from "@/assets/hero/worklog-light.webp";
@@ -10,7 +12,7 @@ import { services } from "@/config";
* トップの自社サービス。左に 1 件ずつ説明を縦に並べ、右の画面は貼り付けて重ねる。
* 巻いて次の説明に入ると、次の画面が下から来て前の画面に重なり、前の画面は少し沈む。
* 重ねるのは position: sticky、沈むのは次の説明の view-timeline で、JS は使わない。
- * 狭い画面では説明の下に画面を置くだけ。worklog は選んだ明暗で画面を出し分ける
+ * 狭い画面では説明の下に画面を置くだけ。worklog と tama は選んだ明暗で画面を出し分ける
*/
interface Props {
lang: Lang;
@@ -28,6 +30,7 @@ const scope = services
const shots = {
todoroku: { src: todoroku, dark: undefined },
worklog: { src: worklogLight, dark: worklogDark },
+ tama: { src: tamaLight, dark: tamaDark },
};
---
diff --git a/src/components/WorksSummary.astro b/src/components/WorksSummary.astro
index 498c3bd..019fa1e 100644
--- a/src/components/WorksSummary.astro
+++ b/src/components/WorksSummary.astro
@@ -11,6 +11,7 @@ import kubernetes from "@/assets/logos/kubernetes.svg";
import lgtm from "@/assets/logos/lgtm.svg";
import mado from "@/assets/logos/mado.svg";
import noren from "@/assets/logos/noren.svg";
+import sonae from "@/assets/logos/sonae.png";
import talos from "@/assets/logos/talos.svg";
import xool from "@/assets/logos/xool.svg";
import yuzuriha from "@/assets/logos/yuzuriha.svg";
@@ -44,6 +45,7 @@ const values: Record = {
const logos = [
{ id: "denpa", name: text.works.denpa.name, logo: { src: denpa }, href: "/works/#works" },
+ { id: "sonae", name: text.works.sonae.name, logo: { src: sonae }, href: "/works/#works" },
{ id: "noren", name: text.works.noren.name, logo: { src: noren }, href: "/works/#works" },
{ id: "mado", name: text.works.mado.name, logo: { src: mado }, href: "/works/#works" },
{ id: "aizuchi", name: text.works.aizuchi.name, logo: { src: aizuchi }, href: "/works/#works" },
@@ -53,6 +55,7 @@ const logos = [
{ id: "helmMosp", name: text.works.helmMosp.name, logo: { src: helm, plate: true }, href: "/works/#works" },
{ id: "gitops", name: text.works.gitops.name, logo: { src: kubernetes, plate: true }, href: "/works/#works" },
{ id: "ffmpeg", name: text.oss.ffmpeg.name, logo: { src: ffmpeg, plate: true }, href: "/works/#oss" },
+ { id: "libaribcaption", name: text.oss.libaribcaption.name, logo: undefined, href: "/works/#oss" },
{ id: "blades", name: text.oss.blades.name, logo: { src: blades, plate: true }, href: "/works/#oss" },
{ id: "talos", name: text.oss.talos.name, logo: { src: talos, plate: true }, href: "/works/#oss" },
{ id: "epgstation", name: text.oss.epgstation.name, logo: { src: epgstation, plate: true }, href: "/works/#oss" },
@@ -71,7 +74,7 @@ const logos = [
diff --git a/src/config.ts b/src/config.ts
index dd52ecd..2e7d656 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -45,10 +45,14 @@ export const business = [
"services",
] as const;
-/** 自社サービス(運用中のもの)。文言は languages の services */
+/**
+ * 自社サービス(運用中のもの)。文言は languages の services。
+ * legal は特定商取引法に基づく表記 (サービスの /legal) があるもの (足のリンクに出す。売っていない tama には無い)
+ */
export const services = [
- { id: "todoroku", url: "https://tk.doany.io/" },
- { id: "worklog", url: "https://w.doany.io/" },
+ { id: "todoroku", url: "https://tk.doany.io/", legal: true },
+ { id: "worklog", url: "https://w.doany.io/", legal: true },
+ { id: "tama", url: "https://ts.doany.io/", legal: false },
] as const;
/** 制作実績。文言は languages の works */
@@ -57,6 +61,7 @@ export const works = [
id: "denpa",
url: "https://github.com/DAnything/denpa",
},
+ { id: "sonae", url: "https://github.com/danything/sonae" },
{ id: "noren", url: "https://github.com/5ym/noren" },
{ id: "mado", url: "https://github.com/danything/mado" },
{ id: "aizuchi", url: "https://github.com/danything/aizuchi" },
@@ -109,6 +114,20 @@ export const oss = [
},
],
},
+ {
+ id: "libaribcaption",
+ merged: 2,
+ links: [
+ {
+ label: "xqq/libaribcaption#30",
+ url: "https://github.com/xqq/libaribcaption/pull/30",
+ },
+ {
+ label: "xqq/libaribcaption#31",
+ url: "https://github.com/xqq/libaribcaption/pull/31",
+ },
+ ],
+ },
{
id: "blades",
merged: 25,
diff --git a/src/i18n/languages/en.ts b/src/i18n/languages/en.ts
index 66291cc..93f8084 100644
--- a/src/i18n/languages/en.ts
+++ b/src/i18n/languages/en.ts
@@ -40,6 +40,7 @@ export const en: Translation = {
"The Todoroku home page, showing the vehicle ledger form with the make, model and engine type filled in",
worklog:
"The worklog home page, with a sample timesheet built from Slack and GitHub logs",
+ tama: "A search for “Prius” in the tama vehicle catalogue: a table of 21 type designations with model name, category, years listed, displacement and fuel economy",
},
businessTitle: "What we do",
@@ -147,7 +148,8 @@ export const en: Translation = {
},
services: {
title: "Own services",
- summary: "Todoroku for vehicle ledgers, and worklog for timesheets.",
+ summary:
+ "Todoroku for vehicle ledgers, worklog for timesheets, and tama, a vehicle catalogue.",
},
},
@@ -160,6 +162,10 @@ export const en: Translation = {
name: "worklog",
body: "Works out each day's start, end, breaks and working time from the API logs of Slack, GitHub, GitLab, Backlog, Jira, OpenProject and Redmine. There's no agent to install, and it can build timesheets for past months.",
},
+ tama: {
+ name: "tama vehicle catalogue",
+ body: "A catalogue of more than 20,000 vehicle type designations approved in Japan. Search by type, model name or type designation number across specifications, fuel economy, years on sale, type designations in the official gazette and recall filings. Todoroku takes its vehicle data from here too.",
+ },
},
worksPage: {
@@ -168,13 +174,15 @@ export const en: Translation = {
worksTitle: "Work",
source: "Source",
others:
- "Smaller tools such as svelte-slider, fixeed and cash-tabelog are on GitHub ({user}, {org}).",
+ "Smaller tools such as wslc-compose, genkan, seisho and svelte-slider are on GitHub ({user}, {org}).",
ossTitle: "Open source contributions",
ossLead:
"Fixes for bugs found in software Doa uses, sent upstream as pull requests.",
shots: {
denpa:
"The denpa recordings list, with recordings and reservations of made-up programmes, and a month calendar and filters on the right",
+ sonae:
+ "sonae with test data: a satellite map of Japan showing coasts under a tsunami advisory and areas under weather warnings, with a list of tsunami, earthquake, earthquake early warning and weather warning reports on the right",
noren:
"The noren storefront: cards with prices for a sample download, a T-shirt picked up at the venue and an admission ticket, with the three steps of buying below",
yuzuriha:
@@ -185,7 +193,11 @@ export const en: Translation = {
works: {
denpa: {
name: "denpa",
- body: "A system for recording and watching TV. It has two parts: an agent that drives the tuner and streams the raw TS, and the main app for the programme guide, reservations, recording, encoding, streaming and live viewing. No separate media server. Written in TypeScript and Svelte, with C# on the tuner side.",
+ body: "A home TV recording server that works as soon as you plug in a tuner and start it. There are no config files: reserve from the programme guide, CMs are skipped automatically, and live TV and recordings play in the browser with captions and data broadcasting. It has two parts, an agent that drives the tuner and streams the raw TS and the main app that does everything else, so it needs neither Mirakurun nor a media server. Runs on Linux, Mac, Windows and Kubernetes. Doa also makes an Android TV app for watching on a TV (denpa-tv) and a rounded typeface for captions and programme guides (Denpa Font).",
+ },
+ sonae: {
+ name: "sonae (備え)",
+ body: "An app that receives earthquake, tsunami, volcano and weather information on a home server, to view in the browser and get as Web Push notifications. It takes earthquake early warnings, earthquake and tsunami reports, eruption notices and weather warnings from the Japan Meteorological Agency and others, shows them on a map and in a list, and reads them out with a short chime and a VOICEVOX voice on open screens. It installs with one line on Windows, Mac and Linux, and on Kubernetes with a Helm chart.",
},
noren: {
name: "noren (暖簾)",
@@ -193,7 +205,7 @@ export const en: Translation = {
},
mado: {
name: "mado (窓)",
- body: "A gateway for connecting to Windows over Remote Desktop from the browser, with OIDC sign-in. A TypeScript client in the browser speaks RDP, and the server only checks sign-in and relays, so it never draws the screen. The relay tries WebTransport (HTTP/3) first and falls back to WebSocket. The server is .NET 10 Native AOT, installed with an MSI and run as a Windows service.",
+ body: "A gateway for connecting to Windows over Remote Desktop from the browser, with OIDC sign-in. A TypeScript client in the browser speaks RDP, and the server only checks sign-in and relays, so it never draws the screen. The relay tries WebTransport (HTTP/3) first and falls back to WebSocket. The server is .NET 10 Native AOT, installed with an MSI and run as a Windows service. Intel AMT machines can also be powered on and off, reached over a serial console and booted from an ISO, all from the browser.",
},
aizuchi: {
name: "aizuchi (相槌)",
@@ -217,7 +229,7 @@ export const en: Translation = {
},
gitops: {
name: "gitops",
- body: "The Kubernetes platform Doa's services run on. A Talos Linux cluster managed with Argo CD. Traffic comes in through Cilium's Gateway API, secrets are in Infisical, and k8up backs up to Cloudflare R2. Sign-in is Entra ID single sign-on, and the Cloudflare and Entra settings are in Pulumi. Matrix and Forgejo are self-hosted here too.",
+ body: "The Kubernetes platform Doa's services run on. A Talos Linux cluster managed with Argo CD. Traffic comes in through Cilium's Gateway API, secrets are encrypted with SOPS and kept in git, and VolSync backs up to Cloudflare R2. Sign-in is Entra ID single sign-on, and the Cloudflare and Entra settings are in Pulumi. Matrix and Forgejo are self-hosted here too.",
},
},
@@ -226,6 +238,10 @@ export const en: Translation = {
name: "FFmpeg",
body: "Fixes for the ARIB caption decoder that uses libaribcaption (caption clearing and memory errors), and a change that enables the loop filter and CDEF in the VAAPI AV1 encoder in CQP mode, have been merged.",
},
+ libaribcaption: {
+ name: "libaribcaption",
+ body: "A library that renders ARIB captions. Merged changes add characters found in terrestrial broadcasts, and phone, TV, radio and speaker symbols, to its table that replaces caption DRCS glyphs with ordinary characters and emoji.",
+ },
blades: {
name: "Blades CSS",
body: "The successor to Pico CSS. Merged changes include light-dark() colours, contrast fixes for focus rings and other parts, an opt-in compact mode, dialog and popover parts, a build on lightningcss alone that cut the dev dependencies from nine to two, layered per-part builds that are now the default exports, and examples for using it in frameworks.",
diff --git a/src/i18n/languages/ja.ts b/src/i18n/languages/ja.ts
index 02893f8..6c54987 100644
--- a/src/i18n/languages/ja.ts
+++ b/src/i18n/languages/ja.ts
@@ -40,6 +40,7 @@ export const ja = {
"トドロクのトップページ。車両台帳に車名・型式などを入れている画面が映っている",
worklog:
"worklog のトップページ。Slack や GitHub のログから作った稼働表のサンプルが出ている",
+ tama: "tama 車両カタログで「プリウス」を探した画面。型式・通称名・区分・掲載年・排気量・燃費の表に 21 型式が並んでいる",
},
businessTitle: "事業内容",
@@ -149,7 +150,8 @@ export const ja = {
},
services: {
title: "自社サービスの開発・運営",
- summary: "車両台帳のトドロクと、稼働表の worklog を運営しています。",
+ summary:
+ "車両台帳のトドロク、稼働表の worklog、車両カタログの tama を運営しています。",
},
},
@@ -162,6 +164,10 @@ export const ja = {
name: "worklog",
body: "Slack・GitHub・GitLab・Backlog・Jira・OpenProject・Redmine の API のログを横断して、日ごとの稼働の開始・終了・休憩・実働を推定するサービスです。常駐エージェントを入れずに、過去の月にさかのぼって稼働表を作れます。",
},
+ tama: {
+ name: "tama 車両カタログ",
+ body: "日本で型式指定を受けた自動車 2 万型式超のカタログです。型式・通称名・型式指定番号から、諸元・燃費・販売期間・官報の型式指定・リコール届出を横断して探せます。トドロクの車両の情報もここから引いています。",
+ },
},
worksPage: {
@@ -170,7 +176,7 @@ export const ja = {
worksTitle: "制作実績",
source: "ソース",
others:
- "ほかにも svelte-slider・fixeed・cash-tabelog などの小さなツールを GitHub({user}・{org})に置いています。",
+ "ほかにも wslc-compose・genkan・seisho・svelte-slider などの小さなツールを GitHub({user}・{org})に置いています。",
ossTitle: "OSS への貢献",
ossLead:
"使っているソフトウェアで不具合を見つけたら、直して上流に送っています。",
@@ -178,6 +184,8 @@ export const ja = {
shots: {
denpa:
"denpa の録画の一覧。架空の番組の録画と予約が並び、右に月のカレンダーと絞り込みがある",
+ sonae:
+ "sonae の画面。試験用のデータで、日本の衛星写真の地図に津波注意報の海岸と気象の警報の地域が出ていて、右に津波・地震・緊急地震速報・警報の一覧がある",
noren:
"noren の商品一覧。見本のダウンロード商品・T シャツ (会場で受け取り)・入場チケットのカードが価格つきで並び、下に購入の流れの 3 段階がある",
yuzuriha:
@@ -188,7 +196,11 @@ export const ja = {
works: {
denpa: {
name: "denpa",
- body: "テレビを録って観るためのシステムです。チューナーを扱って素の TS を流すエージェントと、番組表・予約・録画・エンコード・配信・ライブ視聴を受け持つ本体の 2 つで完結していて、別にメディアサーバを置きません。TypeScript と Svelte、チューナー側は C# で書いています。",
+ body: "チューナーを挿して起動すれば使える、自宅用のテレビ録画サーバです。設定ファイルは書かず、番組表から予約し、CM は自動で飛ばし、ライブも録画もブラウザで字幕・データ放送つきで観られます。チューナーを扱って素の TS を流すエージェントと、それ以外の全部を受け持つ本体の 2 つで完結していて、Mirakurun やメディアサーバは要りません。Linux・Mac・Windows・Kubernetes で動きます。テレビで観る Android TV のアプリ(denpa-tv)と、字幕・番組表のための丸ゴシックの書体(Denpa Font)も作っています。",
+ },
+ sonae: {
+ name: "sonae(備え)",
+ body: "地震・津波・火山・気象の情報を家のサーバーで受けて、ブラウザで見たり、Web Push で知らせを受けたりするアプリです。緊急地震速報・地震・津波・噴火・気象の警報を気象庁などから受け、地図と一覧に出し、開いている画面では短い音と VOICEVOX の声で読み上げます。Windows・Mac・Linux には 1 行で入り、Kubernetes には Helm chart で置けます。",
},
noren: {
name: "noren(暖簾)",
@@ -196,7 +208,7 @@ export const ja = {
},
mado: {
name: "mado(窓)",
- body: "OIDC でログインして、ブラウザから Windows にリモートデスクトップでつなぐゲートウェイです。RDP はブラウザの中の TypeScript の実装が話し、サーバーはログインの確認と中継だけをするので、画面を描きません。中継はまず WebTransport(HTTP/3)で試し、開けなければ WebSocket に移ります。サーバーは .NET 10 の Native AOT で、MSI で入れて Windows のサービスとして動きます。",
+ body: "OIDC でログインして、ブラウザから Windows にリモートデスクトップでつなぐゲートウェイです。RDP はブラウザの中の TypeScript の実装が話し、サーバーはログインの確認と中継だけをするので、画面を描きません。中継はまず WebTransport(HTTP/3)で試し、開けなければ WebSocket に移ります。サーバーは .NET 10 の Native AOT で、MSI で入れて Windows のサービスとして動きます。Intel AMT の機械の電源の操作・シリアルの端末・ISO からの起動もブラウザからできます。",
},
aizuchi: {
name: "aizuchi(相槌)",
@@ -220,7 +232,7 @@ export const ja = {
},
gitops: {
name: "gitops",
- body: "自社のサービスを動かしている Kubernetes 基盤のリポジトリです。Talos Linux のクラスタを Argo CD で GitOps 管理しています。公開は Cilium の Gateway API、秘密は Infisical、バックアップは k8up で Cloudflare R2 へ。ログインは Entra ID のシングルサインオンで、Cloudflare と Entra の設定は Pulumi で持っています。Matrix や Forgejo もここで動かしています。",
+ body: "自社のサービスを動かしている Kubernetes 基盤のリポジトリです。Talos Linux のクラスタを Argo CD で GitOps 管理しています。公開は Cilium の Gateway API、秘密は SOPS で暗号化して git に置き、バックアップは VolSync で Cloudflare R2 へ。ログインは Entra ID のシングルサインオンで、Cloudflare と Entra の設定は Pulumi で持っています。Matrix や Forgejo もここで動かしています。",
},
},
@@ -229,6 +241,10 @@ export const ja = {
name: "FFmpeg",
body: "libaribcaption を使う ARIB 字幕デコーダの修正 (字幕を消す処理とメモリの不具合) と、VAAPI の AV1 エンコーダで CQP のときにループフィルタと CDEF を有効にする修正が取り込まれました。",
},
+ libaribcaption: {
+ name: "libaribcaption",
+ body: "ARIB 字幕を描くライブラリです。字幕の外字(DRCS)を普通の文字や絵文字に置き換える対応表に、地上波で見つけた字と、電話・テレビ・ラジオ・スピーカーの記号を足す変更が取り込まれました。",
+ },
blades: {
name: "Blades CSS",
body: "Pico CSS の後継の CSS フレームワークです。light-dark() での配色、フォーカスリングなどのコントラストの修正、オプトインのコンパクト表示、dialog と popover の部品、ビルドを lightningcss だけにして開発用の依存を 9 個から 2 個に減らす変更、部品ごとの層付きビルドとそれを既定の読み込み先にする変更、フレームワークでの使い方の例などが取り込まれました。",
diff --git a/src/pages/[...lang]/works.astro b/src/pages/[...lang]/works.astro
index 66dffdd..89aece1 100644
--- a/src/pages/[...lang]/works.astro
+++ b/src/pages/[...lang]/works.astro
@@ -13,12 +13,14 @@ import kubernetes from "@/assets/logos/kubernetes.svg";
import lgtmLogo from "@/assets/logos/lgtm.svg";
import madoLogo from "@/assets/logos/mado.svg";
import norenLogo from "@/assets/logos/noren.svg";
+import sonaeLogo from "@/assets/logos/sonae.png";
import talos from "@/assets/logos/talos.svg";
import xoolLogo from "@/assets/logos/xool.svg";
import yuzurihaLogo from "@/assets/logos/yuzuriha.svg";
import denpaShot from "@/assets/works/denpa.webp";
import norenDark from "@/assets/works/noren-dark.webp";
import norenLight from "@/assets/works/noren-light.webp";
+import sonaeShot from "@/assets/works/sonae.webp";
import yuzurihaShot from "@/assets/works/yuzuriha.webp";
import { github, oss, works } from "@/config";
@@ -39,6 +41,7 @@ const [between, afterOrg] = rest.split("{org}");
type Logo = { src: ImageMetadata; plate?: boolean };
const logos: Record = {
denpa: { src: denpaLogo },
+ sonae: { src: sonaeLogo },
noren: { src: norenLogo },
mado: { src: madoLogo },
aizuchi: { src: aizuchiLogo },
@@ -60,6 +63,7 @@ const shots: Record<
{ src: ImageMetadata; dark?: ImageMetadata; label: string; alt: string }
> = {
denpa: { src: denpaShot, label: "denpa", alt: page.shots.denpa },
+ sonae: { src: sonaeShot, label: "sonae", alt: page.shots.sonae },
noren: {
src: norenLight,
dark: norenDark,