Liminalia

制作画面(Liminalia Studio)

作者向けの制作画面。lim studio(または lim dev)の開発サーバーに載り、lim build の書き出しには入らない。

lim studio [プロジェクト] [--port 5180]
# 制作画面: http://localhost:5180/__liminalia/studio
# プレイヤー: http://localhost:5180/

画面(ブラウザ側)は packages/studio/src/client/、開発サーバー側は packages/studio/src/server/。 受け渡しの形(型)は packages/studio/src/protocol.ts が正本。

画面

画面の言葉は日本語と英語。上の帯の右端の「…」のメニューの「English」/「日本語」で切り替える(デスクトップ版はメニューの「表示 → 言語」でも。 アプリのメニュー、はじめの画面、書き出しの画面、ダイアログも替わる)。決めていなければ OS、ブラウザの言語に合わせる。 構文チェックのメッセージと、書き出しの経過は日本語のまま。

上の帯は左から、プロジェクトボタン(表紙の絵とプロジェクト名。押すと「プロジェクト」)、主な画面の切り替え(「編集」⌘1、「フローチャート」、「チェック」⌘2。チェックには問題の数の印)、 そのほかの画面(「キャラクター」「素材」「変数」「ゲームの設定」「プラグイン」「タイムライン」「コメント」。コメントには未解決の数の印)、 検索(⌘K、⌘P。ファイルとシーンを開く)、「書き出し」、「テストプレイ」、「…」のメニュー(更新、配色、言語、欄の表示の切り替え、シーンの見出しの背景)。 ⌘3 からは、キャラクター、素材、フローチャート、変数、ゲームの設定、プロジェクト、プラグインの順(タイムラインとコメントには番号が無い)。「…」の「更新」(デスクトップ版はメニューの「プロジェクト → 更新」⌘⇧R)で、 外で変わったプロジェクトを読み直す(下の「更新」)。「テストプレイ」はゲームをはじめから遊ぶ(デスクトップ版はアプリの遊ぶウィンドウ、ブラウザでは別のタブ)。 「書き出し」はデスクトップ版では書き出しのウィンドウを開き(画面は STUDIO_PLAY、STUDIO_EXPORT へ移ろうとし、アプリがそれを受ける)、 ブラウザでは lim build を使うよう知らせる。お知らせは画面の下の真ん中に重ねて出す。

制作画面の見た目は Radix Colors(灰は mauve、操作と現在地は violet)、書体は同梱の Zen Kaku Gothic New、Zen Old Mincho、JetBrains Mono (client/fonts.css。デスクトップ版には使う太さの woff2 だけを入れる)。人物の色は cast.json の color(ゲームの名前欄の色)をそのまま使い、 色の無い人物は灰にする。顔は立ち絵の標準の差分から切り出し、切り出す位置は制作画面だけの覚え書き(.liminalia/studio.json)に置く。 制作画面の都合(顔の位置、フローチャートで動かしたシーンの位置など)は、ゲームのデータ(.lim、cast.json、liminalia.json)に書かない。

編集は、左にシーン一覧、中にシナリオ、右にプレビューと状態、下にサムネイルを置く。 シーン一覧は、はじめはファイルだけを並べる(名前の右にシーンの数)。ファイル名の左の山形で、そのファイルのシーン(ラベル)を 小さな絵(そのシーンの背景と、出てくる人物の顔)、名前、最初の本文で開き、押すとその行へ(開いたファイルは覚えておく)。 ファイル名の右の「+」でそのファイルの終わりにシーン(ラベル)を足す。全体の検索(⌘⇧F)は一覧の上に欄を出す。 シナリオは「ブロック」(ブロック表示)と「コード」(コード表示)を、シナリオの上の切り替え(⌘E)で行き来できる(どちらも同じ文書を直し、元に戻す、自動保存、プレビュー、構文チェックは共通)。 ブロック表示は、ラベルをシーンの見出し(そのシーンの背景を敷いた帯。「…」のメニューで背景を消して名前と線だけにもできる)、続くコマンドの行を一行の札の並び (種類の色。立ち絵は顔と人物の色。細部の fade、time、volume はコマンドの編集で見る)、台詞を顔、名前、本文、地の文を顔の列を空けた本文、 選択肢をカードで出す。本文の行と、その前に続くコマンドの行はページとしてひとまとめにする(見せ方だけで、コマンドをどこへ動かしても、 ページは本文を区切りに描き直される。切り方は model/blocks.ts)。ラベル、流れのコマンドは一行、 @choice、@define、@script は @end までを一行にまとめる。行の左のつまみ(札は札そのもの)を引くと、その行の写しが指(マウス)について動き、 元の場所には「ここに入る」隙間が残る。隙間は指の高さに合わせて行と行のあいだ(ページの中も)へ移り、周りの行は滑ってよける。 離すと隙間の位置に収まり、Esc で取りやめる。端まで引くと自動でスクロールする。囲みの左上のつまみはページごと動かす (隙間はページとページのあいだにだけ入る)。パーツの欄(シナリオの上のアイコンで開く)のパーツを引くと、パーツの名前の入った隙間が同じように動き、 落とすとそこに足す(コマンドならフォームが開く。ダブルクリックで選んでいる行の後ろに足す)。選んだ行をもう一度押すと、本文はその場で直せ (Enter で確定、Esc でやめる)、コマンドはフォームが開く。Delete か、行に重ねたとき(と選んでいる行)に右端に出る×で消す(⌘Z で戻る)。本文の中の書き方(ルビ、改行、クリック待ち、文中のコマンド)は、ブロックでは見せ方にして出す(字面は変えない)。 Shift+クリックか Shift+上下で行の範囲を選び(⌘A ですべて)、⌘C、⌘X、⌘V、⌘D でコピー、切り取り、貼り付け(選んでいる行の後ろへ)、複製する。 コピーは文字としてほかのアプリにも貼れ、ほかのアプリで写した行も貼れる。行を右クリックすると同じ操作と「お気に入りに追加」「削除」のメニューが出る (ページの左のつまみを右クリックするとページ全体)。お気に入りに追加した行はパーツの欄の先頭の「お気に入り」に並び(プロジェクトをまたいで使える)、 パーツと同じく落とすかダブルクリックで足し、右クリックで外す。

パーツは区切りごとに並ぶ(よく使う、本文、流れ、背景と画面、立ち絵、サウンド、動き、進行とシステム、カスタムコマンド)。区切りは見出しで開閉でき(覚えておく)、 探すと当てはまる区切りだけ開いて出る。カスタムコマンドはプラグインが足したコマンドで、名前の頭(fx- など)が二つ以上で同じならその頭で小分けにする (コマンドの出どころは /project の commandSources)。

シーン一覧のいちばん下の「新しいファイル」と、ファイル名の右クリックのメニュー(シーンを追加、新しいファイル、複製、名前を変更、ごみ箱に入れる)でシナリオのファイルを扱う。 名前は英数字、_、- と、フォルダを分ける /。名前を変えると、ほかのファイルからのジャンプ先(@jump、@call、@use、選択肢、ジャンプ先のパラメータ)と 変更履歴も移し、「はじめから」のファイルなら liminalia.json の entry も書き換える(ゲームには再起動すると反映される)。 ごみ箱に入れたファイルはお知らせの「元に戻す」で戻せる(「はじめから」のファイルは削除できない)。

シナリオの上の「テキストをインポート」で、テキストや表(CSV、TSV。表計算から写したものでよい)から台詞と地の文の行を作って入れる。 名前「…」、【名前】…、名前:… は台詞(括弧が無ければ「」で囲む)、ほかは地の文(頭に全角スペースを足すかを選べる)。 表は見出しの「名前、話者」「本文、セリフ」の列を使う。本文の { } \ と行頭の @ # // は逃がし、すでに .lim の書き方の行は そのまま通す。話者に無い名前は話者に足すかを選べ、カーソルの行の後ろに入れるか、新しいファイルにする(⌘Z で戻せる)。 形式で Ink か Yarn Spinner を選ぶと(.ink、.yarn のファイルを落とすか、自動で見分ける)、lim import-ink / lim import-yarn と同じ変換で ラベル、選択肢、条件分岐、変数のある .lim にする。変数の初期値は先頭の @set に書き、変換できない箇所はコメントにして数と理由を横に出す (ink-yarn-import.md)。

シーン一覧、右の欄、サムネイルは「…」のメニューで、パーツの欄はシナリオの上のアイコンで表示を切り替え、タイムラインは見出しの山形で折りたたむ(どれも覚えておく。 タイムラインとパーツの欄は、はじめは閉じている)。シーン一覧と右の欄の幅は、境目をドラッグして変えられる(覚えておく。ダブルクリックで元の幅。 「拡大」のときの右の欄の幅は別に覚える)。

右の欄は上から、プレビュー(16:9)、その下の操作の行(ここから再生、一時停止、ミュート、カーソル連動、端末、別ウィンドウ、全画面)、行の表示とルート、コマンドの編集(コマンドの札を もう一度押したとき)、今のプレビューの状態(背景、BGM、立ち絵、ループの効果音を一行ずつ、その様子を決めた行と。押すとその行へ)、 区切りの下に変数(値と、それを最後に決めた行)、タイムライン。「ここから再生」はカーソルの行の画面からプレビューを組み立て直し(止めていれば動かし)、 プレビューに入力を移す(押すと進む)。

サムネイルは、開いているファイルの本文と選択肢のページを、そのページの時点の背景と立ち絵の小さなプレビューで横に並べる(材料は持続の帯)。 下に話し手の色の丸(地の文は灰、選択肢は violet)と行番号、シーンの切れ目にラベル名。カーソルの行のページ(その行か、その次のページ)に印が付き、 押すとその行へ。

フローチャートは、シナリオから作る分岐の俯瞰。シーン(ファイルの頭と、ラベルから次のラベルまで)をカード(そのシーンの背景と出てくる人物の絵、 「#ラベル」、最初の本文)、移り方(そのまま次へ、@jump、@call、選択肢、ジャンプ先を持つコマンド。@if の中なら条件付き)を線にし、 ファイルごとに物語の順に上から積む(作り方は GET /map、並べ方は model/map-layout.ts)。選択肢の線には選択肢の文の札、 @call は点線。調べ直し、選び直しのように前のシーンへ戻る線は、カードの右を回る輪にする。開くたびにシナリオのカーソルがあるシーンを 照らして真ん中へ寄せ、選んできたルートのシーンと線を強める。全ルートテスト(チェック)を実行していれば、到達しなかったシーンを薄くする。シーンを押すと そのシーンのシナリオへ移る。地を引くと全体が動き、カードを引くとそのシーンだけを動かす(動かした位置は .liminalia/studio.json に覚え、 右クリックの「位置を元に戻す」で自動の位置へ戻す)。⌘ / Ctrl+ホイールか + − で拡大、縮小し、「シーンを検索」でラベル名や本文の頭から探す。 カードの絵の材料は持続の帯(GET /sweep/stage)で、カードが見えたファイルの分だけ取る。選択肢を出すシーンのカードは、シーンの頭ではなく 選択肢を出す時点の背景と人物を描く。カードの絵は一つの時点の背景と、そのとき出ている人物(シーンの中で後から出る人物は重ねない)。 プレイの終わるシーン(ファイルの終わり、@stop、プレイを終えるコマンド。コマンドの ends)には「終了」の印(コマンドの ending パラメータを書けばその値。 「BAD END」など)を付け、次のシーンへは線を引かない。 データでジャンプ先が決まるプラグインのコマンドは、コマンドの targets が教える見込みで矢印を引く(plugins.md)。

キャラクターは、キャラクター(cast.json のキャストと台詞の名前)を扱う標準の画面。「設定」と「位置」を切り替える。 「設定」は左にキャラクターの一覧(cast.json の cast。顔、名前、台詞の数とシナリオの行数。登場の多い順、少ない順、名前順、名前の逆順で並べ替え、選んだものを覚える)、右に選んだ人物の大きな枠 (人物の色の地に、立ち絵と、キャラクター ID、表示名、名前の色、既定の立ち絵セット、表情の一覧)を出す。 表情の一覧の上の「標準の差分」で、差分を書かずに出したときの差分(立ち絵セットの defaultVariant)を選ぶ(差分が二つ以上のとき)。 「顔の位置」で、シーン一覧、シナリオ、フローチャートなどに出す顔を立ち絵のどこから切り出すかを決める(ドラッグで動かし、ホイールかスライダーでサイズを変える。 .liminalia/studio.json の faces に覚え、cast.json には書かない)。キャラクター ID、表示名を変えると、シナリオの参照(キャラクター型のパラメータ、台詞の行の頭)も 書き換える(シナリオの変更履歴は残る)。画像を差分の並びにドロップすると、標準の差分(normal、default、最初のもの)と透明でない部分を 比べた枠で差分を足す(画像は fgimage/ の、そのキャラクターの画像と同じフォルダに置く)。差分を選ぶと、画像の差し替え、名前の変更、 口パクとまばたきの重ね画像、削除と「位置を調整」がある。名前の色はゲームの名前欄(NVL の名前も)に反映される。 差分の操作の「パーツ合成」で、選んでいる立ち絵セットのパーツ合成(cast.json の立ち絵セットの layers と、差分の parts。形と規則は @liminalia/lang の cast.ts)を編集するダイアログを開く(parts-editor.ts。操作は model/parts.ts)。左にレイヤー(手前を上に並べる。 「+」で追加し、行を引いて並べ替え、「前面へ」「背面へ」でも動かせる)と、選んだレイヤーの画像(「画像を追加」かドロップで fgimage/<キャラクターのフォルダ>/<立ち絵セット>/<レイヤー>/ に置く。 透明でない部分を切り抜いた小さな絵で並べる)、既定の画像、名前の変更、削除、真ん中に選んだ差分を重ねた見本、右に差分の一覧と、選んだ差分のレイヤーごとの画像 (「既定(…)」は parts に書かずにレイヤーの既定を使い、「なし」はそのレイヤーを出さない)。画像の絵を押しても、選んでいる差分のそのレイヤーの画像になる。 差分の「+」で、選んでいる差分の parts と枠を写した新しい差分を作る(一枚絵の差分を選んでいれば空の parts から)。どの操作もすぐ cast.json に保存する。 レイヤーを外しても画像のファイルは残る。PSD を差分の並びにドロップしたとき(PSD の取り込み)も、このレイヤーと差分になる。 パーツ合成の差分は、立ち絵、差分の一覧、顔の位置でもレイヤーを重ねて出す。 「位置」は立ち絵の位置調整画面(開発サーバーの /__liminalia/cast、?embed=1)をそのまま入れたもの。cast.json の枠を直して保存する。 タブを開くと、シナリオのカーソルの行で出ている立ち絵(いちばん前のもの)を選ぶ。位置の画面の保存していない変更は、ウィンドウを閉じる前、⌘S、 「更新」の前と「設定」へ戻るときに保存する。どちらの書き込みも位置調整画面と同じ口を通るので、変更前の内容は .liminalia/backups/ に残り、 インポートで作るプロジェクトではインポート元へ書き戻す。デスクトップ版のメニュー「プロジェクト → 立ち絵の位置調整」もこのタブを開く。

素材は、素材のフォルダ(背景、立ち絵、画像、BGM、効果音、ボイス、動画と、素材のフォルダにプロジェクトが足したもの)の中身を並べる標準の画面。 左で種類を選び、中でフォルダをたどる(検索の欄に書くと、その種類のすべてから検索する)。押すと右に、画像、音、動画の確かめと大きさ、 使用箇所(シナリオの行、キャラクター、プロジェクトの設定)を出し、行を押すとそのシナリオへ、キャラクターを押すとキャラクターのタブへ移る。 名前を変更する(”/” でフォルダへ動かせる)と、参照しているシナリオの行とキャラクターの立ち絵も書き換える(プロジェクトの設定の参照は書き換えずに知らせる)。 「ごみ箱に入れる」は .liminalia/trash/ へ動かし、その場で元に戻せる。ファイルをドロップするか「ファイルを追加」で、開いているフォルダへ追加する。

タイムラインは、シナリオのカーソルのページ(本文の一行)の演出を、時間軸の上の帯として編集する標準の画面(timeline-view.ts。 材料と書き換えは model/timeline.ts)。右の欄のタイムラインの見出しの「開く」でも開く。上の帯に前後のページへの移動、ファイルと行、 拡大、縮小、全体を表示、「再生」、その下にプレビューと選んだ帯の詳細、下に帯を出す。帯はそのページの前に並ぶ演出のコマンド (@bg、@chara、@camera、@quake、@mask、@wait、プラグインのコマンドなど)と、本文の出方、クリック待ち、本文の中のコマンドで、 右の欄のタイムラインと同じ計算(長さは fade、time、待つかどうかは書いた wait、無ければコマンドの仕様の waits)。 画面の操作はすべて .lim のその行を書き換える(エディタの変更として入るので ⌘Z で元に戻り、自動保存に乗る)。

  • 帯の右端を引くと長さ(fade= か time=)を 50ms 刻みで変える。既定と同じ長さになればパラメータを消す
  • 帯を引くと開始を変える。直前の演出のコマンドが @wait ならその長さを変え(0 になれば行を消す)、無ければ後ろへずらすときだけ 前に @wait の行を足す。@wait はそれより後のコマンドもまとめて遅らせる(.lim の意味のまま)。前へは直前の @wait の分までしか動かない
  • 帯を押すと選び、プレビューにそのコマンドを実行した直後の画面を出す。詳細で長さ(1.5s、800ms)と「終わるまで待つ」 (wait のパラメータを持つコマンドだけ。既定と同じなら書かない)を変えられる。選んだ帯は ←→ で開始、Shift+←→ で長さを 50ms ずつ変え、 Enter かダブルクリックでシナリオのその行へ
  • 本文の中のコマンド({@quake} など)は表示だけ(シナリオで編集する)。読み取り専用のファイルでは変えられない
  • 「再生」は、保存してから、そのページの最初の演出のコマンドの直前の画面(その前の行の状態)から、本番のプレイヤーでコマンドを実行してページを出す (帯の上を赤い線が進む)。プレビューは編集のプレビューとは別のプレイヤーで、ミュートの設定は同じ

コメントは、シナリオの行に付けたレビューのコメント(返信と解決ができる)を扱う標準の画面(reviews-view.ts)。

  • 付け方: コード表示でカーソルの行に、シナリオの上の吹き出しのボタンか ⌘⌥M で、右の欄の「コメント」に書く欄を出す(⌘Enter で保存、Esc でやめる)。 コメントの付いた行は行番号の左に吹き出しの印が付き(未解決は violet、すべて解決済みなら灰)、押すとその行のスレッドを右の欄に出す。 右の欄にはカーソルの行のスレッドを出し、返信、解決(再度開く)、コメントの削除ができる
  • 画面: ファイルごとに、行(今の行と、その行の中身)、コメント、返信の欄を並べる。「未解決」「解決済み」「すべて」で絞り、行を押すとシナリオのその行へ。 上の帯の「コメント」のタブの丸印は未解決の数。右上の「名前」でコメントに付ける名前を決める(.liminalia/studio.json の reviewer。 空なら git の user.name、それも無ければ OS のユーザー名)
  • 保存先: プロジェクトの reviews/<id>.json(スレッドごとに一つ。git で共有でき、別々の人が別のスレッドを足しても衝突しない)。 書き出し(lim build)はシナリオ、データ、素材のフォルダしか読まないので、ゲームには入らない。reviews/ が外で変わったら (git で取り込んだときなど)、開発サーバーが reviews-changed を送り、画面が読み直す
  • 行の追い方: スレッドは行番号と、その行の中身(前後の空白を除いたもの)と、そのハッシュ(@liminalia/lang の textHash)を持つ。 開いたとき、保存したときに、同じ中身の行(いくつもあれば元の行に近いもの)を探し、無ければ中身の似ている行(文字の二つ組の重なりが 0.6 以上)を探す (model/reviews.ts)。似ている行で見つけたら、保存のたびにその行へ付け直す(次からは中身で探せる)。打っている間は印が行と一緒に動く。 見つからない行のスレッドは、画面で「行が見つかりません」と出す。印はコード表示だけに出る(ブロック表示には出ない)

変数は、シナリオで使っている変数(game.、system.、temp.)と、その初期値(liminalia.json の variables)を一つの表にする標準の画面。 初期値の欄を編集するとすぐ保存し(空にすると削除。文字は "…"、数、true、false はそのまま)、プレビュー、現在地も初期値から流し直す。 行を押すと代入する行、参照する行が開き、押すとそのシナリオへ移る。名前を変更するとシナリオの参照、代入の場所と初期値の名前も書き換える。 参照するのに代入する行も初期値も無い変数と、代入するのにどこからも参照しない変数には印が付く。「変数を追加」で初期値だけ先に決めておける。

ゲームの設定は、liminalia.json の ui(docs/player.md の「ゲームの設定」)を直す標準の画面。左に項目(文字、メッセージウィンドウ、名前欄、 クリック待ちアイコン、選択肢、システム画面、タイトル画面、カーソル、セーブとバックログ、コンフィグの初期値、クイックメニュー、キー割り当て)、 右に実際のプレイヤーで出したプレビュー(本文、選択肢、タイトル、メニュー、セーブ、コンフィグ、バックログを切り替え、項目に触れるとそのプレビューになる)。 編集するとすぐ保存し、プレビューと開いているプレイヤーに再起動せずに反映される。空にした項目は外す(既定に戻る)。キーは「+」を押してからキーを押して足す。 プロジェクトのプラグインがその画面を差し替えていれば(タイトル、コンフィグ、セーブとロード、バックログ、クイックメニュー、メニュー、確認、おまけ、 フローチャート、クリック待ちアイコン。プラグインの宣言から分かる)、まとまりの見出しに「<プラグイン> の画面」と出す(ここの設定が使われるかはプラグインによる)。 本文、選択肢のプレビューでは、項目ごとに設定が渡す値を二つに替えてみて、実際の見た目が変わらない項目に「反映されません」と出す (プロジェクトの CSS、プラグイン、キャラクターの名前の色など、ほかで見た目を決めているとき。替えた値は描く前に戻す)。

プロジェクトは、プロジェクト全体にかかわる設定(ゲームの設定のほか)を上の切り替えで扱う標準の画面。

  • おまけ: liminalia.json の extra(CG、回想、音楽、実績)の一覧を作る。画像、曲は素材の一覧から、回想の開始位置はシナリオのファイルとラベルから選べる
  • 翻訳: 遊べる言語(languages。先頭が原文の言語)を追加、外し、言語ごとに本文、選択肢、話者名の訳を書く(lang/<言語>.json)。 開くと今のシナリオに合わせ(新しい行は訳を空で足し、使われなくなった訳は引き継ぐか取っておく。lim i18n と同じ)、訳していないものを 100 件ずつ並べる。原文を書き換えた行には以前の訳文を添え、押せば訳文の欄に入れる。場所を押すとそのシナリオへ移る。言語ごとの訳した数を出す。 シナリオの名前を変えると、訳の鍵も一緒に書き換える おまけと言語は、開いているプレイヤーに再起動せずに反映される。

プラグインは、左の欄に「一覧」と、プロジェクトのプラグインが足した画面(./studio。プラグインごとにまとめる。plugins.md)を並べ、 選んだものを右に開く(最後に開いたものを覚える)。

  • 一覧: plugins の並び。名前、バージョン、入口(カスタムコマンド、描画、インポート、Studio)、追加するカスタムコマンドを出し、並べ替える、外す(フォルダは残す)。 プロジェクトのフォルダの中のプラグインは、名前と説明(package.json)を編集し、フォルダを OS の画面で開ける(入れたパッケージは書き換えない)

  • 新規作成: 名前(英小文字、数字、-)と入口(コマンドは必ず、描画、Studio は選ぶ)を決めると、plugins/<名前> に動く小さな例 (@<名前>-hello。描画があれば画面に文を出す。Studio があれば一つ画面を追加する)を作って plugins の最後に追加する

  • インストール済みのプラグインを追加: プロジェクトのフォルダからの相対パスかパッケージ名。見つからないプラグインは追加しない プラグインの並びを変えたら、再起動すると反映される(「再起動」でデスクトップ版は開発サーバーを起動し直す)。

  • コード表示の行番号の右の持続の帯(演出の帯)は、背景、BGM、ループの効果音、立ち絵が何行目から何行目まで続くかを、3px の縦線でファイル全体について描く (GET /sweep/stage)。右端の縮図もファイル全体。帯を押すとその始まりの行へ。帯は列ごとに同じ幅の箱を並べてその中に描くので、 ほかの列の帯の有無、始まりと終わりの印、折り返した行によらず、同じ列の帯はどの行でも同じ横位置、同じ幅でつながる

  • 帯、縮図、札(背景と立ち絵のコマンド)にポインタを乗せると、覗き窓に範囲と、背景ならその背景の画像、立ち絵ならその行の時点の 差分、立ち絵セットの画像(キャラクターから引く)を出す。大きさは背景が 16:9 で幅 480px、立ち絵が高さ 420px(枠は 3:4)で、 画面に収まらなければ比を保って画面の高さ、幅に合わせて縮める。画像が読めるまでは同じ大きさの枠を先に出す(窓が途中で跳ねない)。 窓はポインタの右下に置き、はみ出すなら左、上へ、どちらにも収まらなければ空きの広い側に寄せて、画面の端から 8px の内に収める。 BGM、効果音は名前と範囲だけ。読み込み中、読めない画像、キャラクターに無い差分は、そう書く

  • 補完(コマンド名、パラメータ、値)と、札のフォームの入力はコマンドの仕様から作る。素材のパラメータは、このファイルで使っているものを先に、 続けて素材のフォルダのものを候補に出す(画像は縮小して見せる。音は名前だけ)。素材のフォルダに無い素材を書くと、 保存を待たずに打鍵中の構文チェックで知らせる。選択肢の行は、ジャンプ先と、そのあとのパラメータ(if= と見た目。仕様は CHOICE_OPTION)を補う。 "…" と (…) の中を書いている間はパラメータ名を出さない

  • 条件(if=)付きの選択肢の行には、右の余白に条件の札(「条件 式」、破線の枠)を出す。押してもフォームは開かない

  • タイムラインは、このページの演出を、コマンドの長さと「終わるまで待つか」(コマンドの仕様の waits。書いた wait が優先)から描く。 コマンドの行にカーソルがあるときは、そのコマンドを実行し終えた時点(待つコマンドは終わったとき、待たないコマンドは始めたとき)に印を置き、 あとのコマンドと本文を薄くする

  • プレビューの下の操作の行の「端末」で、スマホ(横、縦)の見え方を真似る。iPhone のおおよその画面の大きさで描いてノッチ、ホームバーの余白を空け、 タッチ端末の操作 UI(右上のメニューボタン、下のクイックメニューを隠す、横長の作品を縦に持ったときの知らせ)を出す(プレイヤーの ?device=phone)

  • プレビューは、本文、選択肢の行ではそのページ(帯に「N 行目」)、コマンドの行(演出のコマンド、@set、ラベルなど)では その行を実行した直後の、本文の前の画面(「N 行目の直後」)を出す。状態の欄もその直後の変数。ルートはプレビューの下に出す。 空行、注釈の行では、それより前の状態を持つ一番近い行のもの

  • プレビューの下の行に、プレビューの操作のボタンを置く。プレビューが入力を持っている(書き手がプレビューを押した)ときも、キーは効く

    • 一時停止(⌘⇧P、もう一度で再開): プレビューを今の画面のまま止める。音(BGM、効果音、動画)と動いている演出(アニメーション、 文字送り、オート、ウェイト)が止まり、プレビューを押しても進まない。プレビューの右上に「一時停止中」と出る。止めている間はカーソルを 動かしてもプレビューを作り直さず、現在地の状態(GET /sweep)の取り直しも控える(状態、タイムラインは取ってある状態で描く)。 再開すると、そのときのカーソルの行を映し直し、控えていた取り直しをする。重いプロジェクトで打鍵のたびにプレビューが動くのを止めたいときにも使う。 止めた状態は覚えない(開き直すと動いている)
    • ミュート(⌘⇧M): プレビューの音を、止めずに聞こえなくする。覚えておくので、プレビューを作り直しても、開き直しても消えたまま
    • 拡大(⌘⇧B。ボタンは無い): 右の欄を広げ、プレビューを大きくする(タイムライン、この行の状態はその下。シナリオは狭くなる)。覚えておく
    • 別ウィンドウ(操作の行のアイコン): プレビューを別のウィンドウに出す。ウィンドウの大きさに合わせて広がり、カーソルの行、一時停止、ミュート、カーソル連動はそのまま効く。欄には「ここに戻す」が出て、ウィンドウを閉じても欄に戻る。別ウィンドウの間は全画面のボタンを隠す(ウィンドウ自体を全画面にする)
    • 全画面(⌘⇧Enter): プレビュー(操作の行ごと)だけを全画面にする。16:9 のまま画面に収まる大きさで真ん中に置く。Esc で戻る
    • カーソル連動(⌘⇧J、もう一度でオフ): プレビューが自分で進んだ(書き手がプレビューを押した、オート、スキップ、選んだ)とき、シナリオのカーソルを プレビューが出し始めたページ(選択肢ならその選択肢)の行へ合わせる。@jump などでほかのファイルへ進んだら、そのファイルを開く。 合わせたカーソルの行ではプレビューを作り直さない(進めたプレビューが頭からやり直しにならない)。シナリオに入力は移さないので、続けてプレビューを押して進められる。 シナリオで打っている間(シナリオが入力を持っている)、止めている間、チェックの画面、変更履歴や競合した変更を選ぶ欄を開いている間は動かさない。 スキップで続けて進んだときは、最後の行だけに合わせる。シナリオからカーソルを動かすと、いつもどおりプレビューがその行を出し直し、そこからカーソル連動が続く。 合わせた行では、状態の欄、ルート、タイムラインを、プレビューで実際に遊んで着いた状態で描く(ルートどおりの現在地の状態ではない)。 プレビューの下の行の表示は「N 行目(プレビューの状態)」になり、状態の欄に「この行の状態(プレビュー)」の札が付く。 ルートは「ルート(プレビュー)」として、プレビューに出させたページまでのルートにプレビューで選んだ選択肢を足し、プレビューで選んだものに印を付ける。 変数の出どころ(「← start 133」)は、プレビューで値を入れた行があればその行、値がプレビューに出させたページのときと同じならそのページの出どころ。 値を入れた行をシステム画面が知らせない(SystemEnv.onAssign を実行器に渡さない)ときは、変わった変数に「← プレビュー」とだけ出す。 シナリオからカーソルを動かすと、いつもどおり現在地の状態に戻る。入れた状態は覚えておく
    • 画面での確かめ(カーソル連動も)は packages/studio/dev/verify.ts、デスクトップ版(Electron)での全画面とキーの確かめは npx tsx packages/studio-desktop/scripts/stage-check.ts [--shots フォルダ]
  • ブレークポイント: 行番号の左を押すと赤い印が付き(もう一度押すと外れる)、プレビューで遊んでいて(押す、オート、スキップ、選ぶ) その行のコマンドに来たら、実行する前でプレビューを止める。シナリオのカーソルはその行へ移り、状態の欄はそこまで遊んだ状態を出す。 「一時停止」をもう一度押す(⌘⇧P)とそのコマンドから続ける。印はプロジェクトごとに覚え、上に行を足すと同じ行に付いたまま動く。頼んだ行をプレビューに出す ための組み立て直しでは止めない(プレビューの口は stage-breakpoints と stage-break、実行器は RuntimeOptions.beforeStep)

  • 変更履歴(シナリオの上のボタン)で、保存のたびに残している前の内容を見られる。一覧から選ぶと今の内容(保存前の編集も含む)との 違いが行ごとに出て、「この内容に戻す」で今の内容をそれに置き換えて保存する。戻す前の内容も変更履歴に残るので、 戻したことも取り消せる(⌘Z でも元に戻せる)。読み取り専用のファイルでは戻せない

チェックは、上に三枚のカード(「全ルートテスト」(POST /coverage)で到達した行の割合と「再実行」、「問題」の数とエラー、警告の内訳、 「未到達」の最初の範囲)、その下に分類(構文、コマンドとパラメータ、キャラクター、ジャンプ先、素材、プラグイン)の丸いボタンと件数、問題の一覧を出す。 構文チェック(POST /check)は保存のたびに自動。分類を押すとその分類の問題だけに絞る(いくつでも選べる。もう一度押すと外す)。 どの問題も押せば編集のその行へ(cast.json への指摘はキャラクターの画面のその人物、立ち絵セットへ。修正案も画面の言葉で出す)。上の帯の「チェック」の丸印は問題の数(エラーと警告。全ルートテストで見つけたものも)。 問題の一覧の下には、文字数(台詞、地の文、選択肢)、ボイスの付いた台詞、表記ゆれの組の数のカードと、ファイル、シーン、話者ごとの内訳の表、表記ゆれの組の一覧を出す (GET /stats。lim stats と同じ集計で、チェックの画面を開いたときと構文チェックを取り直したときに数え直す)。表の行と表記ゆれの場所は押すとその行へ、 表記ゆれの組の「除外」は liminalia.json の stats.variants.ignore に足す(text-stats.md)。

プロジェクトのデータ(paths.data の .json)やプラグインのコードが変わったとき、それを読むのがプレイヤーだけなら、開発サーバーは制作画面ごと読み直さず、 プレイヤー(プレビュー)にだけ読み直しを知らせ(liminalia:player-reload)、プレビューはつながり直したら同じ行を出し直す。制作画面の入口から読まれるコード (プラグインの ./studio など)が変わったときは、Vite の既定どおり制作画面も読み直す。

更新は、外(ほかのエディタ、AI、git など)で変わったプロジェクトを読み直す。保存待ちの変更を先に書いてから、POST /reload で 開発サーバーにシナリオ、キャスト、素材を読み直させ、目次、開いているファイル(元に戻す履歴は残す)、現在地、持続の帯、プレビュー、構文チェックを取り直す。 シナリオ、cast.json、素材のフォルダの変更はふだんから見張って自動で読み直すので、更新は見張りが拾わない変更(大量の書き換えのあとなど)や、 確実に最新にしたいときに使う。プロジェクトの設定(liminalia.json)かコマンドのプラグインのコード(./commands のフォルダの .ts、.js)が変わっていたら、 Node が読み込んだプラグインは取り替えられないので、POST /reload は { ok: false, restart } を返す。デスクトップ版の画面(?desktop=1 で開いたもの)は STUDIO_RESTART(?file=&line=)へ移ろうとし、アプリがそれを受けて開発サーバーを起動し直し、同じファイルの同じ行を開き直す (遊ぶウィンドウは閉じる)。ブラウザ(lim studio)では起動し直すよう知らせる。

原則

  • 正本は .lim のテキスト一つ。画面は読んで描くだけで、書き込みはすべて PUT /file(書き込み口)を通る
  • 読んだあとにディスクの内容が変わっていたら書かない(409 と今の内容を返す)。古い内容で上書きしない
  • 書く前の内容(変更履歴)を <プロジェクト>/.liminalia/history/<ファイル>/ に日時付きで残す(ファイルごとに最新 50 件まで)。 書くときは一時ファイルに書いてから置き換える(途中で止まっても半端なファイルを残さない)
  • プレビュー用の生成物はプロジェクトに作らない。読むだけの口(GET、POST /check、POST /coverage)は何も書かない
  • インポートで作るプロジェクト(liminalia.json に import.tyrano がある)は importManaged: true。直しても次の lim import で上書きされる

口

すべて /__liminalia/studio/api の下。ファイルはシナリオのフォルダからの相対パスで、拡張子を除いた名前(start、part1/chapter2)。

口返すもの
GET /projectプロジェクトの情報、ファイルの一覧(行数、hash、ラベル)、話者、キャスト、コマンドの仕様、素材の一覧(下)
GET /file?file=内容と hash(SHA-256 の先頭 16 文字)
PUT /file{ file, text, baseHash } を書く。baseHash がディスクと違えば 409(今の内容つき)。新しいファイルは baseHash を null
GET /history?file= / GET /history/item?file=&id=変更履歴の一覧(新しい順)と、その内容
POST /checklim check と同じ構文チェックに、素材の実在の確認(lim build と同じ参照の集め方)を足したもの。診断ごとの分類と、分類ごとの件数つき
POST /coveragelim coverage と同じ、画面なしで流した全ルートテストと実行時の診断(本文 { runs } で回数)
GET /reviews / POST /reviewsレビューのコメント(ReviewList。スレッドと書くときの名前)/ ReviewAction(create、reply、resolve、delete、anchor)を受けて reviews/ を書き換え、一覧を返す
GET /stats / POST /stats/ignore文字数と表記ゆれの候補(TextReport。lim stats と同じ)/ { id } の組を stats.variants.ignore に足す
POST /reload外で変わったプロジェクトを読み直す。ReloadResult(設定かプラグインが変わっていたら { ok: false, restart })
GET /data?name=プロジェクトのデータ(paths.data の .json)。DataText(プラグインの画面が使う)
PUT /dataDataWriteRequest → 200 { ok: true, hash }、409 { ok: false, current }。変更履歴は .liminalia/history/@data/
POST /asset?kind=&path=[&overwrite=1]素材を足す(本文はファイルそのもの。画像、音、動画、書体だけ)。同じ名前は 409
GET /assets/list / GET /assets/usage素材のフォルダごとのファイル(大きさつき)と中のフォルダ / 素材を使っている場所("種類/パス" → シナリオの行、キャスト、プロジェクトの設定)
POST /assets/rename{ kind, from, to } で名前を変える(フォルダも)。参照しているシナリオの行(書き込み口を通し、履歴に残る)とキャストも書き換え、書き換えられなかった参照を返す
POST /assets/trash / POST /assets/restore / POST /assets/folder.liminalia/trash/<id>/ へ動かす(TRASH_KEEP 件まで)/ { id } で戻す(戻す先が埋まっていれば 409)/ フォルダを作る
POST /files/create / duplicate / rename / trashシナリオのファイルを作る({ file, text? })、写す、名前を変える({ from, to }。ジャンプ先、履歴、entry も)、ごみ箱へ({ file }。戻すのは /assets/restore)
GET /extra / POST /extraおまけの一覧(liminalia.json の extra)を読む / 書く(空なら外す)
GET /i18n / GET /i18n/file?lang= / PUT /i18n/file / POST /i18n/languages言語と訳の進み具合 / 今のシナリオに合わせた訳とシナリオの場所 / 訳を書く / 遊べる言語を書く
GET /plugins / POST /pluginsプロジェクトのプラグインとその中身 / プラグインの並びを書く(見つからなければ 400。再起動すると反映される)
POST /plugins/create{ name, parts } → { spec }。plugins/<name> に例の入ったプラグインを作って足す(あれば 409)
POST /plugins/update{ spec, name?, description? }。プロジェクトのフォルダの中のプラグインの package.json を書き換える
POST /plugins/reveal{ spec }。プロジェクトのフォルダの中のプラグインのフォルダを OS のファイルの画面で開く
GET /ui / POST /uiゲームの設定(liminalia.json の ui)を読む / 書く(形が誤っていれば 400。空なら外す)
GET /variables / POST /variables/set / POST /variables/rename変数の一覧(初期値と読む、書く行)/ 初期値を決める、外す / 名前を変える
GET /cast/usage / POST /cast/renameキャラクター ID ごとのシナリオの行と話者名ごとの台詞の数 / { kind: "cast" | "speaker", from, to } で名前を変え、シナリオの参照と cast.json を書き換える
GET /studio-state / PUT /studio-state制作画面だけの覚え書き(.liminalia/studio.json。顔の切り出し faces、フローチャートで動かしたシーンの位置 mapPositions)/ { state } で全体を置き換える(形の合わない項目は捨てる)。ゲームのデータには書かない
GET /sweep?file=[&from=&to=]そのファイルの各行の本番どおりの状態(下)。from、to で行の範囲を絞れる(コマンドの行の状態も絞り、画面の様子は範囲で使うものだけにして番号を付け直す)
GET /sweep/stage?file=持続の帯の材料。行ごとの背景、BGM(ファイル、音量、ループ)、ループの効果音(チャンネルごと)、立ち絵(名前、差分、立ち絵セット、位置、反転)を、ファイル全体について返す(同じ様子は一つにまとめ、行はその番号を指す)

書き込み口は同じオリジンからの要求だけを受ける。シナリオのフォルダと cast.json を見張り、外の変更(ほかのエディタ、AI、インポート)を Vite の WebSocket のカスタムイベント liminalia:studio で送る(file-changed、file-added、file-removed、cast-changed)。 画面自身の書き込みでは送らない。素材のフォルダも見張り、ファイルが増えた、減ったら assets-changed を送る(続けて増えたときはまとめて一度)。 大きな応答(16 KB 以上)は、要求が受け付けるなら gzip で返す。

素材の一覧(ProjectInfo.assets)は、素材のフォルダ(bgimage、fgimage、image、bgm、sound、video と、 コマンドの仕様が探し先に挙げるフォルダ)ごとの、フォルダからの相対パス(名前順。. で始まるものは入れない)。

チェックの分類(CheckResult.diagnostics の各項目の category と、counts): syntax(構文のエラー)、command(コマンドとパラメータ)、 cast(キャスト、立ち絵セット、差分、立ち位置、cast.json の形)、target(ジャンプ先のファイルとラベル)、asset(素材の欠け)、 plugin(プロジェクトの決まり。まだプラグインからチェックを足す口が無いので 0)。

現在地(各行の状態)

カーソルの行の「本番どおりの状態」(はじめからルートどおりに流した結果)を、プレビュー、事実、持続の帯が共有する。

  1. ルート: 「はじめから」から画面なしで流し、そのファイルに最初に入る(そのファイルのコマンドを最初に実行する)ルートを探す。 入った時点は、そのファイルの最初のコマンドを実行する直前(@define の中をマクロとして実行しただけのときは入ったとしない)。 探し方は lim build --seed と同じ後戻りの探索(@liminalia/runtime の findRoute)。選択肢では、同じ選択肢に戻るたびに次を選び、 着かずに終わったら最後の選択を次の番号に変えてやり直す。探す途中で通ったほかのファイルの入り口も控える (探索の順は探す先によらないので、どのファイルから先に頼んでも同じルートになる)。 2 秒で見つからなければ、そのファイルの頭から空の状態で流す(how: "file-start")
  2. ファイルの中: 入った時点から、選び方をずらして何回か流し(選択肢の数まで、8 回まで)、本文の行と選択肢の行を初めて通ったときの状態を控える。 それ以外の行(演出のコマンド、@set、@if、ラベルなど)は、その行を初めて実行し終えた直後の状態を控える(Sweep.after)。 実行し終えたことは、次のコマンドを実行する直前(RuntimeOptions.onStep)で、呼び出しの深さがその行と同じか浅いときに知る。 なので @call、@define の呼び出しの行は、呼んだ先から戻ってきた時点の状態になる。 そのファイルから出たら(@call から戻ってくる途中を除く)その回を打ち切る
  3. 本文、選択肢の行の状態(Sweep.lines)は、その行を出す直前から再開するセーブ(画面の様子は save.stage、ゲーム変数は save.game)、temp、 変数ごとにその値を決めた行(RuntimeOptions.onAssign で集める)、そのファイルの中で選んだ選択肢。 コマンドの行の状態(Sweep.after)はセーブを持たず、画面の様子(Sweep.stages の番号。コマンドの行は多いので、同じ様子は一つにまとめる)、 ゲーム変数、temp、決めた行、選んだ選択肢

行の状態は重ねて持たない。大きなプロジェクトでは変数が数百あり、毎行に全部を持つと 1 ファイルで数十 MB になるため:

  • ゲーム変数と、その値を決めた行は、ファイルに入った時点(Sweep.base)から変わったものだけを持つ(消えた変数は unset)。 プレイヤーに渡すセーブは lineSave(sweep, state)、コマンドの行のゲーム変数は afterGame(sweep, state)、 決めた行は lineAssigned(sweep, state) で全体に戻す
  • ファイルの中で選んだ選択肢は、流した回ごとの並び(Sweep.runs)の先頭何個かを指す(lineChoices(sweep, state))
  1. 届かない行: 1 のルートはそのファイルに最初に入るルートなので、ほかのファイルで決めた変数で分かれる @if の先など、 そのルートでは通らない行がある(演出の帯の斜線)。その行にカーソルを置くと、その行を通るルートを探して 2 をやり直し (GET /sweep/reach、Sweeper.entryReaching)、プレビューの下に「ルート(N 行目へ)」と出す。探しているあいだと、届かない、時間内に 見つからないときは、プレビューの下の行の表示にそう出す(手前の状態を持つ行の画面を出したまま)。探し方:
    • 選択肢では、フローチャートの上で目標のシーンに近いシーンへ移るものから試す(選択肢のジャンプ先は実行器が描画側へ渡す targets)
    • その行へ入る条件(その行を囲む @if と、その行のシーンへ移る矢印の条件)の変数を、条件に合う値に決める @set の行を 候補にし、候補ごとに「その行を先に通ってから目標の行へ」の順に探す
    • 一度入ったファイルをその行を通らずに出た回は、そこで打ち切って次の選び方へ(ゲームの終わりまで流さない)
    • 15 秒まで。見つかったルートは、ファイルが変わるか、ほかの届かない行のために探し直すまで、そのファイルの現在地に使う

ルートと各行の状態は、どれかの .lim が変わるまで覚えておく。2 回目からはすぐ返る。 画面は、プレビューと事実に使う行の状態を見えている範囲の前後だけ取り(from、to)、持続の帯は GET /sweep/stage でファイル全体を取る。 packages/studio/dev/measure.ts <プロジェクト> [ファイル…] で、ファイルごとの大きさ(gzip の前後)と作る時間を測れる。

プレビュー(プレイヤーの制作画面用の口)

制作画面は本番のプレイヤーを /?studio=1 で iframe に開き、postMessage で行の状態を渡す(開発サーバーのときだけ有効)。

  • プレイヤーは「クリックで開始」もタイトルも出さず、開いたら { type: "liminalia:stage-ready" } を返す

  • { type: "liminalia:stage-show", save } を受けたら、そのセーブから始めてページ(か選択肢)を出し、 { type: "liminalia:stage-shown", at } を返す(at はそのページか選択肢の位置。着く前に終わったら null)。 loadSave がページを出す前に解決するシステム画面でも、ページが出るか選択肢で止まるまで待ってから返す (上限 3 秒。過ぎたら実行器の今の位置)。画面は返事の位置を表示には使わない

  • プレビューが入力(focus)を持っていない間は、要素の focus() を止める。選択肢のボタン、入力の枠、システム画面、プラグインの画面が focus を移そうとしても、制作画面で打っている文字はプレビューに吸われない。書き手がプレビューを押せば、ふだんどおり移る

  • { type: "liminalia:stage-still", stage } を受けたら、今のプレイ(実行器)を止めて進めず、描画側の組み立て直し (ロードと同じ restore と、プラグインの restorers)だけでその様子(stage)の画面を組み立て、メッセージウィンドウの本文と名前欄を空にして、 { type: "liminalia:stage-still-shown" } を返す。システム画面には触れないので、システム画面を差し替えたプロジェクトでも同じように動く。 プレビューを押しても先へは進まない

  • プレビューへの頼み(stage-show、stage-still)は順に行い、前の頼みの組み立て直しの途中で次を始めない。待っている間に次が来たら古いものは飛ばす

  • { type: "liminalia:stage-cast", cast } を受けたら、キャストを差し替えて表示中の立ち絵を描き直す

  • stage-show で頼まれたページを出して stage-shown を返したあと、プレビューが自分で進んで次のページ(か選択肢)を出し始めるたびに { type: "liminalia:stage-progress", at: { file, line }, played } を返す(書き手がプレビューを押した、オート、スキップ、選んだ)。 頼まれたページそのものと stage-still の画面では返さず、次の頼みを受けたらその頼みの stage-shown まで止める。画面のカーソル連動が使う。 played(StagePlayed。実行器が無ければ null)は、そのページの時点のプレビューで遊んだ状態:

    • save: そのページ(選択肢)から再開するセーブ(runtime.save()。現在地の Sweep.lines のセーブと同じ時点)。temp: そのときの temp
    • assigned: 頼まれたページ(最後の stage-show)から先で値を入れた変数のパス → その行(SystemEnv.onAssign で集める。後に入れたものほど後ろ)
    • choices: 頼まれたページから先で選んだ選択肢(RouteStep。選んだ順)

    出どころと選択肢は頼まれたページから先の分だけなので、画面はそのページの現在地の状態(変数の出どころと、そのページまでのルート)に足して使う

  • { type: "liminalia:stage-pause" }、{ type: "liminalia:stage-resume" } でプレビューを止める、続きから動かす。 { type: "liminalia:stage-mute", muted } で音を消す、戻す。どれも頼みの順番を待たずにすぐ行い、 { type: "liminalia:stage-controlled", paused, muted } を返す。画面は、止めている間は stage-show、stage-still を送らず、 プレイヤーが開き直したとき(stage-ready)は音を消す、止めるを送り直す。止め方(packages/web/src/studio-stage.ts):

    • 音: 描画側の AudioPlayer の AudioContext を止める。<audio>、<video> は pause()(止めた後に鳴り始めたものも)
    • 演出: document.getAnimations() を pause()。止めた後に始まった Web Animations、CSS のアニメーション、トランジションも止める
    • 文字送り、オート、@wait、システム画面やプラグインのタイマー: プレイヤーは ?studio=1 のとき、ほかより先に window の setTimeout、requestAnimationFrame を止められるもの(TimerGate)に包む。止めている間に時刻の来た呼び出しは控え、 再開で来た順に呼ぶ(setInterval は止めている間の回を飛ばす)。プレイヤー自身の頼みの待ちは包む前のタイマーで数える
    • 入力: 止めている間はプレビューへの押す、キー、ホイールを受けない(制作画面のキー ⌘⇧…、Esc は通す)
    • 音を消すは AudioPlayer.setMuted(コンフィグの音量とは別の出口の音量を 0 にする)と、<audio>、<video> の muted (こちらで消したものだけを戻す)

    システム画面に手を入れないので、プラグインがシステム画面を差し替えたプロジェクトでも同じように効く。止められないのは、 タイマーを使わずに進むもの(Web Worker、MessageChannel、performance.now() を回し続ける同期の処理など)と、 AudioContext を自分で作って鳴らすプラグインの音、文書に入れずに鳴らす new Audio()(音を消すも同じ)

セーブから始めるのはシステム画面の loadSave(save)(plugins.md)。システム画面を差し替えるプロジェクトは、これを実装するとプレビューが使える。

制作の受け渡し

  • 収録用の台本(プロジェクト → 書き出し → 収録用の台本、または lim voice-script): 台詞ごとにボイスのファイル(台詞の前の @voice か、 番号の固定と @voice-auto の pattern から)、話者、台詞(ルビは「漢字《かんじ》」)、演技のメモ(台詞のすぐ上の // の行)、前の行、 シーン、位置を、exports/voice-script/ に全体.csv、話者ごとの CSV、集計.csv(台詞の数、文字数、ボイスが未設定の台詞)で書き出す
  • 訳の受け渡し(プロジェクト → 翻訳 → 入出力、または lim i18n <言語> --export csv|xliff --out … / --import … / --count): 訳のファイルを表(CSV)か XLIFF 1.2 で exports/translation/ に書き出し、訳を書き込んだものを読み戻す(鍵で引き、原文が今のシナリオと 違う行は書かない)。--count は見積もり用の文字数(原文の全体と、まだ訳していないもの)
  • 全分岐の見た目の確かめ(lim regress [--update] [--threshold 0.05] [--pages 1] [--only 文字] [--dir フォルダ]): フローチャートの シーンごとに、そのシーンの頭から始めて頭から --pages ページを撮り(演出を弱め、動きを止めるので、同じシナリオなら同じ画面になる)、 基準(.liminalia/regress/baseline)と比べる。違う画素が --threshold % を超えたものを知らせ、違うところを赤くした画像を .liminalia/regress/diff に置く。基準の無いものは基準にし、--update で今のものを基準にする。変わったものがあれば終了コード 1