Skip to content
17 changes: 9 additions & 8 deletions Tools/check-metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,14 @@ def read_text(path)

# Control characters and unvetted glyphs. Tabs are included: they survive into
# the listing as literal whitespace nobody sees in the editor.
def check_characters(path, body)
# `locale` widens the allowlist to that locale's scripts (MetadataLimits::
# SCRIPT_RANGES); the TestFlight changelog and en-US stay glyph-strict.
def check_characters(path, body, locale = nil)
body.each_char.with_index do |char, index|
next if char == "\n"
ord = char.ord
next if ord >= 0x20 && ord < 0x7F
next if MetadataLimits::ALLOWED_GLYPHS.key?(char)
next if MetadataLimits.allowed_character?(char, locale)

ord = char.ord
line = body[0, index].count("\n") + 1
name = ord < 0x20 || ord == 0x7F ? "control character" : "character #{char}"
error(
Expand All @@ -96,15 +97,15 @@ def check_length(path, body, limit)
error(path, "is #{length} characters; App Store Connect allows #{limit}")
end

def check_file(path, limit)
def check_file(path, limit, locale = nil)
body = read_text(path)
return if body.nil?

if body.strip.empty?
error(path, "is empty")
return
end
check_characters(path, body)
check_characters(path, body, locale)
check_length(path, body, limit) if limit
body
end
Expand Down Expand Up @@ -137,7 +138,7 @@ def check_whats_new
def locale_directories
Dir.children(METADATA)
.select { |name| File.directory?(File.join(METADATA, name)) }
.grep(/\A[a-z]{2}(-[A-Z]{2})?\z/)
.grep(/\A[a-z]{2}(-[A-Z][a-z]{3})?(-[A-Z]{2})?\z/) # en-US, ja, zh-Hant
.sort
end

Expand Down Expand Up @@ -166,7 +167,7 @@ def check_locales
next
end

body = check_file(path, MetadataLimits::LOCALIZED[field_for(basename)])
body = check_file(path, MetadataLimits::LOCALIZED[field_for(basename)], locale)
check_url(path, body) if URL_FILES.include?(basename)
check_keywords(path, body) if basename == "keywords"
end
Expand Down
31 changes: 31 additions & 0 deletions Tools/metadata_limits.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,35 @@ module MetadataLimits
"▸" => "forward glyph (File Viewer history)",
"◂" => "back glyph (File Viewer history)",
}.freeze

# Localized listings need whole scripts, not glyphs: a Traditional Chinese
# or Japanese description is thousands of distinct characters no allowlist
# could name. Each locale gets the Unicode blocks its copy is written in —
# the ideographs, kana, the CJK punctuation block (、。「」…) and the
# full-width forms (,:?!()) — on top of ASCII and ALLOWED_GLYPHS. A
# character outside those blocks (an emoji, a stray Cyrillic look-alike)
# is still refused, and en-US stays glyph-strict.
CJK_RANGES = [
0x3000..0x303F, # CJK Symbols and Punctuation
0x3040..0x309F, # Hiragana
0x30A0..0x30FF, # Katakana
0x3400..0x4DBF, # CJK Unified Ideographs Extension A
0x4E00..0x9FFF, # CJK Unified Ideographs
0xF900..0xFAFF, # CJK Compatibility Ideographs
0xFF00..0xFFEF, # Halfwidth and Fullwidth Forms
].freeze

SCRIPT_RANGES = {
"zh-Hant" => CJK_RANGES + [0x3100..0x312F], # + Bopomofo
"ja" => CJK_RANGES,
}.freeze

# The single character test both the Fastfile and the checker apply.
def self.allowed_character?(char, locale)
ord = char.ord
return true if ord >= 0x20 && ord < 0x7F
return true if ALLOWED_GLYPHS.key?(char)

SCRIPT_RANGES.fetch(locale, []).any? { |range| range.cover?(ord) }
end
end
8 changes: 7 additions & 1 deletion docs/agents/release-and-metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ changelog looked right in every editor and App Store Connect refused the
character. The caps live in that one file, required by `fastlane/Fastfile`
too, so the lane that fails an archive and the job that fails a pull request
can never disagree. Add a glyph to the allowlist only after an upload has
actually accepted it.
actually accepted it. Localized listings (`fastlane/metadata/zh-Hant`, `ja`
— every en-US file mirrored, same caps) are allowed their whole scripts by
Unicode block (`MetadataLimits::SCRIPT_RANGES`: ideographs, kana, bopomofo,
CJK punctuation, full-width forms) instead of glyph-by-glyph; en-US and
the TestFlight changelog stay glyph-strict, and the changelog stays
English. When en-US copy changes, change the two mirrors in the same PR
(glossary in `i18n.md`; run `zhtw-mcp lint` over the zh-Hant files).

- **App icon is a hand-authored Icon Composer package** (`AppIcon.icon`;
spec + bake-off in `DESIGN.md`); icon.json lists groups
Expand Down
28 changes: 28 additions & 0 deletions fastlane/metadata/ja/beta_app_description.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Multiplexはリモートのtmuxを中心に作られた空間SSHターミナルで、ホストごとにherdrも選べます — Apple Vision Pro、iPad、iPhoneのネイティブAppです。

Deckはライブのウォールです。すべてのホストが同時にプローブされ、各セッションはそのセッションの実際の最後の画面行を示すタイルとして描かれます。タイルをタップするとアタッチし、セッションは独自のウインドウで開きます — Vision Proでは空間ウインドウ、iPadではステージマネージャのシーン、iPhoneではフルスクリーンのペイン。ウインドウはタブにまとめたり分割し直したりでき、シェルは落ちません。HERDRモードでは1タイルが1つのherdrセッションで、そのワークスペースがタイルのスパインになり、エージェントの状態はherdr自身から得ます。

Multiplexはペインで動いているClaude Code、Codex、Pi、Grok Buildも見つけ出し、ターミナルの下にコマンドストリップを置きます。Multiplex Proは、見ていないエージェントがターンを終えたり質問したりしたときの通知、どのメッセージにもワンタップでジャンプできるClaude Codeのプロンプト履歴、mosh、無制限のホスト、カスタムターミナルテーマのエディタを追加します。

必要なもの
- SSHで到達できるホスト(パスワード、またはOpenSSHのed25519/RSA鍵)。
- ウォールと永続セッションのために、そのホスト上のtmux 1.8以降またはherdr 0.7.5以降。通常のSSHシェルはどちらもなくても動きますが、タイルは出ません。
- moshトランスポート(Pro)を試すなら、ホスト上のmosh-server。

どこに何があるか
- Deckの + HOSTでホストを追加。プローブはすぐに始まります。
- 設定 → 外観でシャーシをライブに切り替え。Vision ProにはSYSTEM、LIGHT、DARKに加えてGLASSがあります。
- ホスト設定 → BACKENDでホストをTMUXとHERDRの間で切り替え。→ MONITORINGには「バックグラウンドで接続を維持」もあり、デフォルトはオフです。
- + NEW SESSIONで永続セッションを開始するか、任意のセットアップスクリプト、モデル、最初のプロンプト付きでエージェントを起動。ウィジェットとショートカットからの起動はどちらのバックエンドでも動き、既存のセッションの中にも入れます。
- TMUX(Vision Proでは下部バー、iPad/iPhoneではキーバー、幅の狭いiPhoneでは右上)で日常のtmuxコマンドを送信。herdrタブではこのキーはHRDRと表示され、herdr自身のコマンドを送ります。
- ターミナルを長押し、ダブルタップ、または副ボタンクリックすると、どちらのバックエンドでもSELECT/SELECT ALL/PASTE。選択はそのペインの中に留まります。herdrタブにはMENUも加わり、そのセルでherdr自身のペインメニューを開きます。
- FILEは写真、ファイル、カメラからアクティブなペインの作業ディレクトリへアップロードし、Returnを押さずにリモートのパスを入力します。ファイルをドラッグしても同じです。
- + TAB → ファイルビューアでホストのファイルとgitの差分を閲覧。ターミナル出力のパスを長押しすると、そのファイルがセッションの横に開きます。レンダリングされたMarkdownのWebリンクは、ドックされたビューポートかシステムのブラウザで開けます。
- ターミナルのGUIDEチップは、そのタブのジェスチャを図解したフィールドマニュアルです。
- DETACHでウインドウを閉じます。リモートのセッションは動き続け、タイルはウォールに残ります。
- ホーム画面ウィジェットは、ホストで最後に開いたセッション — または選んだセッション — とSHELL/AGENTのキーを表示。ショートカットとSiriでシェルを開く、リモートファイルを行または行範囲で表示する、エージェントを起動することができます。

ホスト、エージェントコマンドの設定、シークレットはiCloudキーチェーンを通じてデバイス間で同期され、エンドツーエンドで暗号化されます。Multiplexのサーバはなく、アカウントも分析もありません。

報告してほしいこと
接続が切れる、キーボードのフォーカスが失われる、ウインドウの位置やサイズがおかしくなる、ターミナルの描画が乱れる — こうしたことがあれば、デバイス、OSのバージョン、そのとき何をしていたかを添えてお知らせください。TestFlight内のスクリーンショットによるフィードバックは開発者に直接届きます。
28 changes: 28 additions & 0 deletions fastlane/metadata/ja/description_ios.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Multiplexは、リモートの永続セッションの中で暮らす人のためのターミナルです — iPhoneとiPadのためにネイティブで作られました。

ウォール
ホストは同時にプローブされます。どのセッションもライブモニタ:実際の最後の画面行、ウインドウごとのスパイン、アタッチ中は赤いLIVE。アタッチする前にフリート全体を見渡せます。NO SIGNALはワンタップで再接続。tmuxを動かすか、ホストをherdrに切り替えるか — セッションごとに1タイル、ワークスペースはスパインに、エージェントの状態はherdr自身から読み取ります。

セッションを、必要な場所に
iPadでは、アタッチすると各セッションがステージマネージャ、Split View、外部ディスプレイのための本物のシーンで開きます。ウインドウをタブにまとめ、また分割できます — SSH接続、バッファ、スクロールバックはタブと一緒に移動するので、シェルが落ちることはありません。iPhoneでは、ウォールとターミナルがひとつの可変フルスクリーンシェルを共有します。

AIエージェントセッションのために
Claude Code、Codex、Pi、Grok Buildを、任意のセットアップスクリプト、モデル、最初のプロンプト付きで1ステップで起動。Multiplexは4つすべてをどのペインでも見つけ出し、ヘルパーチップは前面のエージェントに追従するので、コマンドはあなたのキー入力を受け取っているプロセスに届きます — 並べ替え、自分のチップを追加でき、各ホストがその設定をすべてのデバイスで覚えています。Proは1日のチップ上限を取り払い、見ていないターンが終わったり入力を必要としたりすると通知し(tmux上のClaude Code、Codex、Grok Build。herdr上ではherdr自身が追跡するすべてのエージェント)、Claude Codeのプロンプト履歴をワンタップのトランスクリプトジャンプ付きで開きます。tmuxとherdrのタブにファイルを添付またはドロップ:Multiplexはペインの作業ディレクトリへアップロードし、リモートのパスを入力します。

ウィジェットとショートカット
ホストをホーム画面に置けます — 最後に確認したセッションのフレームと正直なSEENスタンプ、そして接続へ直行するSHELLとAGENTのキー — または全ホストをひと目で見渡すフリートウィジェット。ショートカットとSiriでシェルを開く、リモートファイルを任意の行または行範囲で表示する、選んだディレクトリでエージェントを起動する(新しいセッションとして、または既存のセッションの中で)ことができます。multiplex://URLスキームで独自の自動化も。

mosh内蔵
Proならホストをmoshに切り替えるだけで、セッションはスリープ、ローミング、IPの変更を越えて生き残ります — ネイティブSwiftのmoshクライアントで、コンパニオンAppは不要です。

本物のターミナル
完全なxterm-256colorエミュレーション、多言語のシステムキーボード入力、マウス対応TUIのためのタッチとポインタによるクリック、ハードウェアキーボード、ライブのPTYリサイズ、日常のtmuxコマンドを集めた内蔵メニュー — herdrタブにはherdr自身のセットを。CTRLを押し続けるとキーコマンド:ソフトウェアキーボードでは押せない保存済みのコード(Shift+Enter、Ctrl-Cを2回、単語を削除するOption+Backspace)と1行のテキストマクロを、独自の繰り返し回数と開いたままにするルール付きで、デバイス間で同期。長押し、ダブルタップ、または副ボタンクリックで、どちらのバックエンドでもペイン内に収まるテキスト選択。システム、ライト、ダークがシャーシ全体を切り替え、それぞれ独自のターミナルテーマを保持します — 10を内蔵し、Proは背景、テキスト、カーソル、16のANSIカラーすべてのエディタを追加します。

設計からプライベート
ホスト、エージェントコマンドの設定、シークレットはiCloudキーチェーンを通じてデバイス間で同期され、エンドツーエンドで暗号化されます。私たちのサーバに触れるものはありません — サーバ自体が存在しないからです。アカウントなし、分析なし、トラッキングなし。

MULTIPLEX PRO
1回の購入、サブスクリプションなし:無制限のホスト、mosh、エージェント通知、上限のないコマンドストリップ、カスタムテーマ、そしてすべてのデバイスで12のキーコマンド。無料版には2台のホスト、完全なターミナル、5つのキーコマンド、1日10回のエージェントコマンドチップタップが含まれます。

必要なもの
SSHで到達できるリモートホスト(パスワード、またはOpenSSHのed25519/RSA鍵)。ライブウォールと永続セッションにはtmux 1.8以降またはherdr 0.7.5以降が必要です。通常のSSHシェルはどちらもなくても動作します。moshにはホスト上のmosh-serverが必要です。
Loading