anti scroll

ブラウザと小説の新しい関係を模索する

縦書き文庫のビューアーをリニューアルしました

縦書き文庫のビューアーを久しぶりにリニューアルしました。

いくつかあった不満点を、それなりに改善できたと思います。

旧ビューアーの不満点

  • 目次、登場人物、コメントなどの各種情報が確認しにくかった(下方向へのスクロールが必要だった)
  • 解像度の大きいPCで横幅が余ってしまっていた
  • 横書きが読みにくかった(縦書きを初期設定にしているので、横幅がでかくて読みにくかった)
  • 表示設定を変えると、先頭ページに戻されてしまっていた

新しいビューアーの改善点

  • 目次、登場人物、読者分析、コメント、お気に入りユーザー、しおりなどの情報が、上部のタブから簡単に確認できるようになりました。

f:id:convertical:20210328222945p:plain
上部のタブから各種情報が確認できるように

  • 見開きに対応したので、横書きが読みやすくなりました。

f:id:convertical:20210329063153p:plain
見開きで横書きも読みやすく

  • 目次の情報が作品の左上に表示されるようになりました。

f:id:convertical:20210328223030p:plain
目次の情報が左上に表示されるように

  • 画面サイズに合わせて、ビューアーの幅が勝手に伸張するようになった(レスポンシブになった)ので、解像度の大きいPCでも横幅が有効に使えるようになりました。
  • 表示設定を変えても、先に見ていた内容のページ(になるべく近いところ)に戻れるようになりました。
  • ページ送りボタンのUIを削除したので、縦幅を増やすことができました。
  • UIをマテリアルデザインに統一したので、スマホでの操作がより直感的になりました。

とまあ、こんな感じです。

その他、注意点

  • 新しいビューアーの表示設定は、右上の歯車アイコンから行います。

f:id:convertical:20210329092856p:plain
表示設定の変更

  • ビューアーを新しくした影響で、エディタ画面での「プレビュー」機能が(現在のところ)使えなくなっています。

プレビューは、いずれ復活させたいのですが、対応時期は未定です。

数式の表示とプログラムの色付け表示に対応しました

縦書き文庫で数式とプログラムの自動色付け表示ができるようになりました。

数式

数式は文の途中に差し込む場合(インライン表示)と、行全体を使って表示する場合(ブロック形式)のそれぞれで書き方が異なります。

文の途中に差し込む場合は、次のように書きます。

[math $sin$theta]は、日本語で「正弦」といいます。

書き方は[math 数式]です。

実際に表示するとこんな感じになります。

f:id:convertical:20210226170956p:plain

行全体を使って表示する場合はmathタグを使います。

<math>
c = $pm$sqrt{a^2 + b^2}
</math>

実際に表示するとこんな感じになります。

f:id:convertical:20210226171033p:plain

色々な数式の記述についてはKaTexのサイトを参照してください。

ちなみにKaTexでは\(バックスラッシュ)で書く記号を、縦書き文庫では$(ドルマーク)で書くことに注意してください。

プログラムの色付け表示

こんな感じでプログラムを書くと、言語(下の場合は javascript)に応じた色付けがされます。

<pre><code class="lang-javascript">
let a = "foo";
console.log("a is %s", a);
</code</pre>

もちろん javascript の部分は、プログラムの中身がhtmlなら htmlcssなら css などと書き換えてください。

実際に表示させると、こんな感じになります。

f:id:convertical:20210226171256p:plain

ちなみに投稿フォームにて、入力フォーマットの項目を「マークダウン記法」にすると、もう少し簡単に書けます。

```javascript
let a = "foo";
console.log("a is %s", a);
```

またプログラムについて別のページから参照させたい場合は、id属性をつけて、別ページからアンカーリンクを貼ると便利ですが、次のように書くとプログラムにid属性を与えることができます。

```javascript#program1
let a = "foo";
console.log("a is %s", a);
```

上の例ではprogram1というid属性を与えています。

まとめ

小説投稿サイトで数式やプログラムを記述することは多分ないでしょうが、技術的な内容や学術的な内容も書けるようになると面白いかなあと思って、対応してみました。

なお数式についてはKaTex、プログラムの色付けについてはhighlight.jsを利用しました。

どちらも素晴らしく使いやすいライブラリで、作者様には感謝の気持ちでいっぱいです。

アンカーリンクと、リンク先のプレビューに対応しました

アンカーリンクと、リンク先のプレビューに対応しました。

アンカーリンクとは

id属性を付けた要素に対して、<a href="#そのidの値">...</a>のように宣言したリンクのことを、アンカーリンクといいます。

リンクのhref属性の値が #(シャープ)から始まっていることに注意してください。

こうして作成したリンクは、

  1. クリックすると、リンク先の要素が記述されたページに移動できます。
  2. マウスを乗せると、リンク先の要素がプレビューで確認できます。

記述例1

<blockquote id="bq1">「強い者が勝つのではない。勝った者が強いのだ」</blockquote>
[page-break]
この<a href="#bq1">発言</a>は、ドイツの皇帝と呼ばれた「ベッケンバウアー」によるものです。

f:id:convertical:20210220100100p:plain
マウスを乗せるとアンカーリンク先のプレビューが表示される

ここではblockquoteタグに使っていますが、imgタグなどに使っても便利だと思います。

記述例2

他にも、先頭ページに空のaタグをid="top"のような属性で宣言しておいて、最終ページから「先頭ページに戻る」みたいなリンクを貼ることもできます。

<a id="top"></a>
冒頭の文章
[page-break]
最終ページの文章
<a href="#top">トップページに戻る</a>

この場合、リンク先の中身は空なので、マウスを乗せてもプレビューは表示されません。

記述例3

アンカーリンクを利用して、ちょっとしたゲームブックを作成することもできます。

その他

各種機能を改善しました

縦書き文庫の各種機能を改善しました。

作品一覧にてタグが表示されるように

作品の一覧にて、タグが表示されるようになりました。

f:id:convertical:20201113125300p:plain
タグの表示

これによって、同じ系統の作品にアクセスしやすくなりました。

シリーズに追加できる作品数を20から50に増やしました

大菩薩峠(41作品)のように大きなシリーズもあるので、シリーズあたりの作品数を20から50に拡張しました。

先頭ページに「登場人物の一覧」が表示されるようになりました

作品に登場人物を登録した場合、先頭ページに「登場人物の一覧」が表示されるようになりました。

f:id:convertical:20200705102918p:plain
登場人物の一覧

市販の書籍でも、だいたいこんな感じで冒頭に表示されてますよね。

登場人物の挿絵については、小説本文の下側に「登場人物」のタブメニューがあって、そこをクリックすると挿絵付きで一覧が表示されます。

f:id:convertical:20200705102945p:plain
挿絵付きの登場人物一覧

人物をクリックすると、こんな感じで詳細情報が表示されます。

f:id:convertical:20200705103223p:plain
人物の詳細

シリーズ作品において続きの作品へのリンクが本文の最後に表示されるようになりました

Togetterの次の記事に「探す作業が嫌だ」みたいなことが書かれていて「そりゃごもっとも…」と思ったので、表題の件を実装しました。

少年ジャンプ+副編集長が大学1年生から「漫画アプリのUIについて物申したい!」というDMが来て実際に会って考えが整理されて意義深い時間になったという話 - Togetter

これまでは、シリーズを設定している作品について、続きを読む際に、いったんシリーズ作品のリンクへ飛んでから、自分で続きを探さないといけませんでした。

これからは続きがある場合は、本文の最後にリンクが表示されます。

f:id:convertical:20200615125638p:plain
続きがある場合は、リンクを表示する

当たり前にそうあるべきことが、これまで出来ていなかったことが、ちょっと恥ずかしいです。

nehan7をリリース

nehan6からnehan7にバージョンアップしました。

インストール

npm install --save nehan

変更点

  • 組版速度が約20%向上しました。
  • コードサイズが約10%削減されました。
  • nehan.cssが不要になりました。
  • 行内に置換要素、画像、ルビ、圏点傍点、複数サイズの文字などを同時に含むような場合でも、ベースラインが正確に計算されるようになりました。
  • テーブルセルに対してvertical-align: middleが効くようになりました。
  • いくつかのパターンにおける文字詰めの表示崩れを修正しました。
  • 行末揃えの処理を指定しても組版速度が落ちなくなりました。
  • PageReaderは廃止されました。今後はPagedHtmlDocumentを使用してください(後述)。

縦書き文庫のビューアーの刷新

縦書き文庫のビューアーも、新バージョンのエンジンに差し替えました。

nehanのコードサイズが減ったぶん、ページの読み込みも(少しだけ)速くなったと思います。

500KByteほど削減されているので、モバイル環境なんかでは、そこそこ効果が大きいのではないでしょうか。

UIについても、ちょっとだけ刷新しました。

これまでは「本文」と「目次・登場人物」の2列で表示していたのですが、今後は本文を一列(ワンカラム)で表示するようにして、「目次・登場人物」は本文下のメニューに移動させました。

これにより、本文は表示領域が広くなり見やすくなった一方、目次や登場人物に対する操作性は悪くなってしまったので、これに関しては今後の課題としておきます。

(本文の横に目次やら人物やらのタブを表示したらいいのかなあ、と考えています)

nehan7のざっくりとした使い方(開発者向け)

だいたいこんな感じで使います。

import { PagedHtmlDocument, CssStyleSheet } from 'nehan';

const src = "<h1>hello, nehan!</h1>";
const doc = new PagedHtmlDocument(src, {
  styleSheets: [
    new CssStyleSheet({
      "body": {
        "writing-mode": "horizontal-tb", // or "vertical-rl"
        // インライン方向のサイズ(横書きならwidth、縦書きならheightに相当)
        "measure": "450px", 
        // ブロック方向のサイズ(横書きならheight、縦書きならwidthに相当)
        "extent": "500px",
        "font-size": "16px",
        "padding": "1em"
      }
    })
  ]
});
const $dst = document.querySelector("#dst"); // 結果を格納するDOM

// 描画を開始(非同期処理)
doc.render({
  onPage: (ctx){
    const page = ctx.caller.getPage(ctx.page.index); // ページを評価
    $dst.appendChild(page.dom); // 評価したページのDOMを注入
    $dst.appendChild(document.createElement("hr")); // 一応、分割線
  },
  onComplete: (ctx){
    console.log(`終わりました(${ctx.time}msec)`);
  }
});

注意事項1(論理プロパティについて)

nehanは論理組版エンジンなので、領域サイズの指定にwidthとかheightなどは使えません。

代わりにmeasureとかextentという論理プロパティで指定します。

measureは横書き(writing-mode:"horizontal-tb")ではwidthのことです。

縦書き(writing-mode:"vertical-rl")ならheightになります。

extentは横書き(writing-mode:"horizontal-tb")ではheightのことです。

縦書き(writing-mode:"vertical-rl")ならwidthになります。

同じく、top, right, bottom, leftといった名前は、nehanではそれぞれbefore, end, after, startと指定します。

例えば横書きのmargin-leftは、nehanではmargin-startです。

before/afterはブロック方向の前後で、start/endはインライン方向の前後、と覚えてください。

注意事項2(段組とページ送りについて)

上のサンプルでは、appendChildを使って、組版結果をどんどん追記する「段組み方式」で表示していますが、ページ送りで表示したいときは、自前で現在表示するページやページ番号などを管理する必要があります。

その場合、起動時には先頭ページだけを表示し、それ以降のページはページ送りをしたタイミングでdoc.getPageを使って動的に対象ページを取得し、画面上のDOMを差し替える、という実装になるでしょう。

ページ送りの場合の実装については、サンプルのbook.tsなどが参考になるかもしれません。

TypeNovelで出力したhtmlを縦書き文庫のビューアーで開く方法

最初に

Windows用とLinux用のexeのzipができましたので、ご利用下さい。

まずは適当な文章を書く

適当にこんな感じの文章を書いたとします。

@scene({season:"summer"}){
  @scene({time:"morning"}){
    遅刻したので、$time("朝食")はたべません!
  }
  @scene({time:"noon"}){
    $time("ランチでも")いかが?
  }
  @scene({time:"night"}){
    $time("日も暮れたし")もうそろそろ帰らない?
  }
}

リリースモードでコンパイル

これをリリースモードコンパイルします(リリースモードは、途中の余計な空白を取り除くモードです。出力結果がコンパクトになります)。

[foo@localhost] tnc --release sample.tn > sample.html

コンパイルのexeがMacでは小文字のtncですが、Windowsでは大文字のTnc.exeだと思います。各自ここは書き換えて使って下さい。

できあがったsample.htmlを見ると、こんな感じになっています。

/Users/u1/Downloads/sample.tn(line:1) 'season' is not annotated in this block!
error count = 1

<scene data-season='summer'><scene data-time='morning'>遅刻したので、<time>朝食</time>はたべません!</scene><scene data-time='noon'><time>ランチでも</time>いかが?</scene><scene data-time='night'><time>日も暮れたし</time>もうそろそろ帰らない?</scene></scene>

冒頭に「season制約(summer)を注釈してないよ!」というエラーが出ていますが、ここでは気にしないことにします。

縦書き文庫のビューアーにドラッグ・アンド・ドロップ

とりあえずhtmlファイルができたので、縦書き文庫に行って、適当な作品を開きます。

開いたら、作品の本文部分にsample.htmlをドラッグ・アンド・ドロップしてみましょう。するとダイアログが開くので、一般には次のように設定して「読み込む」を押します。

f:id:convertical:20190709172558p:plain
確認ダイアログ

すると次のように内容が表示されます。

f:id:convertical:20190709181258p:plain
表示結果(おかしい)

しかしちょっと変だということに気付いたでしょうか。冒頭のエラーのことではありません。別々の@sceneで分けたブロックが、すべてつながって一行の中にまとめられてしまっているのが問題です。

なぜこんなことになるかというと「本来HTMLに<scene>なんていうタグはないから」ということになります。

そして、一般に登録されていないタグは、インライン・タグと認識されるのがお約束です。

つまり<scene><b>とか<span>とかと同じようなタグと認識されたので、全ての文章が一行につながってしまったわけです。

設定ファイルを作成する

これをどうにかするために、まずは次のコマンドを打って、tnconfig.jsonを作成します。

[foo@localhost] tnc --init

上のコマンドをうつと、作業ディレクトリにtnconfig.jsonというファイルができるはずです。

さっそくこのtnconfig.jsonをエディタで開いてみると、途中に@sceneタグを<scene>ではなく<div>タグにマッピングする処理が書かれています。次がそれに該当する箇所です。

{
  "markupMap":{
    (中略)
    "@scene":{
      "tagName": "div",
      "className": "<name> <arg2>"
    },
    (中略)
  }
}

上の抜粋した部分では、初期状態のtnconfig.json@scene<div class='scene'>に変える、と書いてあります。

さて、tncは作業ディレクトリにtnconfig.jsonがあれば、それを設定ファイルとして利用する仕様なので、このファイルがある状態でコンパイルし直したら、@scene<div class="scene">として出力されそうです。

もういっかい先程と同じようにリリースモードでビルドし、出来上がったhtmlを見てみると、今度は次のようになっています。

/Users/u1/Downloads/sample.tn(line:1) 'season' is not annotated in this block!
error count = 1

<div class='scene' data-season='summer'><div class='scene' data-time='morning'>遅刻したので、<time>朝食</time>はたべません!</div><div class='scene' data-time='noon'><time>ランチでも</time>いかが?</div><div class='scene' data-time='night'><time>日も暮れたし</time>もうそろそろ帰らない?</div></div>

今度は<scene>タグではなく、<div>タグで出力されていまるのがわかりますでしょうか。<div>というのはもちろん通常のHTMLに用意されているタグで、ブロックタグを表すタグですから、今度は別々のシーンが別々の段落になっているはずです。

ではもう一度、同じファイルを縦書き文庫のビューアーの本文部分にドラッグ・アンド・ドロップしてみると、、、

f:id:convertical:20190709181337p:plain
表示結果(正常)

やりました! だいたい望み通りの出力です。

というわけで、こうやって縦書き文庫を利用することで、一応は出力結果を本のようにして読むことが可能になっています。

ちなみに「縦書きは嫌だ!」とか「文字が小さい!」とか不満がある方は「表示設定」というボタンから変更できます。

いずれ専用のビューアーは別に作るつもりですが、しばらくはこれでご容赦いただきたく。