anti scroll

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

Angular(<=8)でnehanを使っている方への注意

結論だけ先に述べておきます。

  • Angular8以下では、nehan(<=6.0.38)までしか使えません。
  • ただしTypescript3.6.3以降を使える環境では、なんの問題もなくnehan(>=6.0.40)を使用できます。

以下に理由を書きますが、非常にしょうもないことです。

package.jsonのミス

nehan(<=6.0.38)においては、package.jsontypesという属性の値が./dist/indx.d.tsとなっていました。

しかし実はこれ正しくは./dist/index.d.tsと書かねばならなかったのです(eの字が抜けていた)。

- "types": "./dist/indx.d.ts",  // version 6.0.38
+ "types": "./dist/index.d.ts", // version 6.0.40

先に「Angular8以下では、nehan6.0.38を使ってください」と書きましたが、つまりそれは、このバグったpackage.jsonを使ったversion6.0.38を使ってください、ということです。なぜか?

理由は「そのバグがあるために、Typescript3.5以下でもエラーが出ないから」です。つまりバグのある古いバージョンを使うことで、Typescript3.5以下しか使えないAngular(<=8)でバグがでない、ということです。

実はnehanは各所にgetterと言われる機能を使っているのですが、実はこれ、Typescript3.6.3以降じゃないと次のようなエラーが出て、正しく使えなかった機能だったのです。

error TS1086: An accessor cannot be declared in an ambient context.

でもnehan6.0.38は正しいindex.d.tsが読み込めていない状態だったので、幸運にも? Typescript(<=3.5)でもこのエラーが出なかったのです!

というわけで、Typescript3.5.3までしか使えないAngular8では、nehan(>=6.0.40)を使用できません…

ちなみにAngular9になると、Typescript3.6系列がサポートされるらしいので、そこからはnehanを6.0.40にアップデートできます。

TypeNovelのVSCode拡張を公開しました

VSCodeのマーケットプレースにvscode-typenovelというTypeNovel用のVisual Studio Code拡張を公開しました。

marketplace.visualstudio.com

インストール後は、*.tnファイルを編集するときに有効になります。

実際に動かすと、こんな感じです。

https://raw.githubusercontent.com/tategakibunko/vscode-typenovel/master/images/capture.gif

主な機能

  • ブロック(@で始まる)と注釈($で始まる)のインテリセンス
  • 組み込みマークアップの引数説明を表示($rubyとか@notesとか)
  • 文法のハイライトや、括弧を自動で閉じる設定
  • セーブ時にエラーをチェック(注釈されていない制約、未定義の制約、重複した制約など)

ちなみに$で起動するインテリセンスは、もちろん組み込みタグだけではなく、ブロックで定義した制約の一覧も表示します。

連絡先

バグや機能要望はGithubにて受け付けています。

github.com

余談(開発でハマったところ)

1 .vscodeignoreファイルの挙動

vsce packageコマンドで拡張機能をパッケージ化するときに、.vscodeignoreファイルでパッケージに含めないファイルを定義できるのですが、このファイルに.gitignoreを入れると、.gitignoreの中身を勝手に見て、その中に記述されたファイルもパッケージの対象外にしてしまうみたいです。

つまり.gitignoreファイルに「gitには必要ないがvs拡張のパッケージには含めたい」ファイルなんかを記述してしまったときは、必要なファイルがパッケージ化されなくて、うまく拡張機能が動作しません。

これを回避するのは簡単で、ようするに.vscodeignore.gitignoreを記述しなければよいのです(ちなみに.gitignoreは、.vscodeignoreに記述しなくてもパッケージ対象にはならない模様)。

ちなみにパッケージされるファイルを事前に確認したい場合は、vsce packageをする前にvsce lsとすると、パッケージされるファイル一覧を事前に確認することができます。

2 ローカルの.vsixファイルをVSCodeのUIから直インストール・アンインストールした際の挙動

あと、ローカルでインストールした.vsixファイルを、VSCodeのUIからアンインストールした場合、.vscode/extensions/<拡張機能のフォルダ>が削除されないバグがあるみたいです。

で、わかりにくいことに、この状態でVSCodeのUIから新しい.vsixファイルを読み込ませて上書きインストールさせようとすると、インストールは成功と表示されるのですが、実際には何も新しいファイルが展開されず、古いディレクトリだけが残っている状態になってしまいます。

つまりバグを修正して再インストールしても、修正されていない古い拡張が残り続けるので、まったく修正されていない状態になっているわけです。

この状態について、最初は「修正が正しくない」と判断して、ハマってしまいました。そこから「実は再インストール自体がなされておらず、古い拡張が残り続けているだけ」と気付くのに小一時間ぐらいかかりました。

ようするに「ローカルでインストール・アンインストールのテストするときは、UIを介さずに手動で拡張機能ディレクトリを削除する必要がある」ということになります。

jingoo v1.3.1をリリース

久しぶりになりますが、jingooのv1.3.1をリリースしました。

Release v1.3.1 · tategakibunko/jingoo · GitHub

変更点

  • 演算子として、新たに+=, -=,*=, /=,%= がサポートされました。
  • 匿名関数がサポートされました。
  • 条件分岐の構文にて、elseif だけではなく、else ifを使うこともできるようになりました。
  • macro ~ endmacro構文にて、endmacroの後に、macro名を付け足すことができるようになりました。

匿名関数はこんなふうに使います。

{# 2と表示される #}
{{ ((i) => i + 1)(1) }}

最後のmacro~endmacroについては、こんな風に使います。

{% macro li (content) %}<li>{{content}}</li>{% endmacro %}

{# v1.3.1からは、こういうふうにも書ける #}
{% macro li (content) %}<li>{{content}}</li>{% endmacro li %}

マクロの中身が長くなったときに「あれ、これってなんのマクロの宣言だっけ?」とかならないように、endmacroの部分で、メモ代わりみたいな感覚でマクロ名を付け足すこともできるようになった、ということです。

ただし次のように、宣言したマクロ名と違う名前を付け足すと、構文エラーになります。

{# エラー(マクロ名はliであって、fooではない) #}
{% macro li (content) %}<li>{{content}}</li>{% endmacro foo %}

アプリケーションにTypeNovelのコンパイラを組み込む

この記事は、TypeNovelコンパイラをアプリケーションから利用したい開発者向けのものです。

導入

npm install --save typenovel

コンパイラの呼び出し

ソーステキストからコンパイルするときは、Tnc.fromStringです。

import { Tnc } from 'typenovel';

const result = Tnc.fromString('@scene(){ foo }', {
  format: 'html', // もしくは 'text'
  minify: false // trueだと圧縮表示
});

console.error(result.errors); // エラー
console.log(result.output); // コンパイル結果

ソースファイルからコンパイルするときは、Tnc.fromFileです。

import { Tnc } from 'typenovel';

const result = Tnc.fromFile('sample.tn', {
  format: 'html',
  minify: false
});

console.error(result.errors); // エラー
console.log(result.output); // コンパイル結果

コンパイラを拡張する

Compileクラスを使うと、コンパイルの各段階におけるVisitorを自前のものに置き換えることができます。

例えば、TypeNovelのノードツリー(TnNode)から出力文字列に変換するVisitor(NodeFormatter)を自前のものにするときは、こんな風にします。

import { Compile } from 'typenovel';

const myFormatter = new MyNodeFormatter(); // 自前で実装したFormatter
const result = Compile.fromString('@scene(){ foo }', {
  nodeFormatter: myFormatter // 自前のFormatterを設定
});

console.log(result.output); // コンパイル結果

ここで、MyNodeFormatterは、NodeFormatter interfaceを実装したクラスです。

NodeFormatter interfaceはnode-formatter.tsにて、次のように定義されています。

export interface NodeFormatter {
  visitTextNode: (args: {
    content: string;
    isWhiteSpacePre: boolean;
    isFirstChild: boolean;
    isLastChild: boolean;
    prev?: TnNode;
    next?: TnNode;
    indent: number;
  }) => string;

  visitAnnotNode: (args: {
    name: string;
    tagName: string;
    id: string;
    className: string;
    attrs: any;
    content: string;
    selfClosing: boolean;
    prev?: TnNode;
    next?: TnNode;
    indent: number;
  }) => string;

  visitBlockNode: (args: {
    name: string;
    tagName: string;
    id: string;
    className: string;
    attrs: any;
    content?: string;
    children: TnNode[];
    prev?: TnNode;
    next?: TnNode;
    indent: number;
  }) => string;
}

それぞれの関数の引数に、各ノードの情報が入っています。

ようするに、これらの情報を元にして、テキストノード(TextNode)、注釈タグ(AnnotNode)、制約ブロック(BlockNode)のそれぞれを、なんらかのテキストに変換して返せばよいわけです。

例えば注釈タグではクラス属性やid属性など無視して、タグ名だけあれば十分!などと思うなら、次のようにvisitAnnotNodeを実装すればいいわけです。

class MyNodeFormatter implements NodeFormatter {

  (省略)

  visitAnnotNode: (args: {
    name: string;
    tagName: string;
    id: string;
    className: string;
    attrs: any;
    content: string;
    selfClosing: boolean;
    prev?: TnNode;
    next?: TnNode;
    indent: number;
  }){
    return `<${args.tagName}>${args.content}</${args.tagName}>`;
  }
}

拡張できるinterfaceはノードから出力テキストの変換処理だけではありません。

次の部分をユーザーが自由に拡張できます。

TypeNovelParser(String -> Ast)

TypeNovelParserは、TypeNovelのソースからAstを作成するインターフェイスです。

AstMapper(Ast -> Ast')

AstMapperは、Astを別のAstに入れ替えるインターフェイスです。

AstConverter(Ast -> TnNode)

AstConverterは、AstTnNodeに変換するインターフェイスです。

NodeMapper(TnNode -> TnNode')

NodeMapperは、TnNodeを別のTnNodeに入れ替えるインターフェイスです。

NodeValidator(TnNode -> ValidationError[])

NodeValidatorは、TnNodeを検査してValidationErrorを出力するインターフェイスです。

NodeFormatter(TnNode -> String)

NodeFormatterは、TnNodeから出力テキストを生成するインターフェイスです。

拡張例

例えばTypeNovelにおいて、別のTypeNovelソースを展開する構文である

$include('別ソース')

などは、Ast -> Ast'な処理なので、IncludeExpanderクラスとしてAstMapperインターフェイスで実装されています。

ast-mapper.ts

あるいは、ノードから無駄なホワイトスペースを削除するNodeWhitespaceCleanerクラスは、TnNode -> TnNode' な処理なので、NodeMapperインターフェイスで実装されています。

node-mapper.ts

こうやって、コンパイラの各段階を自分好みのものに置き換えることで、各自でコンパイラが拡張できるような作りになっています。

TypeNovelをTypeScriptで書き直しました

TypeNovelは、制約と注釈の組み合わせによって、型付きの小説を記述するための言語です。

参考:プロとアマの小説の特徴を数値化して比較してみたらやっぱり差があったので、それを埋めるための型付き小説記述用言語 TypeNovel を公開した件について

これまでF#で開発してきたのですが、今後はTypeScriptで開発することになりました。

これによって、コンパイラのインストールは

npm install -g typenovel

で、完了します。

インストールが成功すると、/usr/local/bin/tncが使えるようになるはずです。

[foo@local] tnc --version
v1.0.0

書き直した理由

一言でいうと「TypeNovelReaderのファイルサイズが大きくなってしまうから」です。

F#のアプリケーションを配布するときには、.NETCoreのランタイムも一緒に配布しないといけないのですが、これが80Mbyte近くあります。

これを除外して、ファイルサイズを削減したかった、というのが主な理由です。

しかしMac/Linux用のバイナリを100Mb以下にする、という当初の目標は達成できませんでした…つまりGithub(100MBの制約がある)にはMac/Linux版をアップロードできません。

その他にも「コンパイラの配布が簡単になる」とか「メジャーな言語なので開発者を募りやすくなる」とかもありますが…

Fsharpの良かった点

  • パーサーが書きやすい

Fsharpの辛かった点

  • 開発者が少ない(ように感じる)
  • ドキュメントが少ない
  • paket周りの運用が少し面倒に感じた
  • ランタイムがでかい(80M前後)
  • プログラムの立ち上がりが少し遅い

TypeScriptの良い点

  • TypeNovelReaderのファイルサイズが減る(20Mbyte近く削減)
  • npmで簡単に配布できる(インストーラーを配布する必要がない)
  • 書ける人がたくさんいる

TypeScriptにして辛い点

  • Nearley(パーサー生成系)やMoo(字句解析機の生成系)などの優れたライブラリを使うことで軽減されるが、F#に比べるとパーサーを書くのは面倒くさい

変わった仕様について

  • コンパイルオプションの--release--minifyに変更されました。
  • --formatオプションを指定することで、出力フォーマットとして、htmlだけではなくtextも選べるようになりました。
  • tnconfig.jsonにおいて、warnXXX系のフラグは、compilerOptionsというフィールドで記述する仕様に変更されました。
// 変更前
{
  warnUndefinedConstraint: true,
  ...
}

// 変更後
{
  "compilerOptions": {
    warnUndefinedConstraint: true,
    ...
  }
}

TypeNovelで電子書籍を公開する方法

TypeNovelで記述した原稿を公開する時、そのファイルをそのままTypeNovelReaderで開いても、それなりには表示されます。

しかし、どうせなら作品タイトルや、作者の情報や、作中キャラクターの情報などが表示されるように公開したいものです。

ここではそうした情報を盛り込んだ上で作品を公開する方法を簡単に説明します。

ちなみにTypeNovelReaderは以下のURLからダウンロードできます。

Windows版は.exeで、Mac版は.dmgで、Linux版は.AppImageです。TypeNovelコンパイラも付属しているので、別途TypeNovelをダウンロードする必要もありません。

メイン原稿はindex.tn

まずはindex.tnというファイルを作って、次のように記述します。

@scene(){
  あいうえお。
}

作品情報はdata.json

次にdata.jsonというファイルを作って、こんな感じで記述します。コピペして必要な箇所だけ書き換えたらいいでしょう。

ちなみにwritingModeの部分をhorizontal-tbとすると、横書きになります。

その他の項目についての詳細はSemanticNovelというページを参照して下さい。

{
  "title": "作品のタイトル",
  "theme": "default",
  "author": "あなたのお名前",
  "email": "あなたのメールアドレス",
  "homepage": "あなたのホームページ",
  "writingMode": "vertical-rl",
  "displayTypeNovelError": true,
  "enableSemanticUI": true,
  "speechAvatarSize": 50,
  "characters": {
    "hanako": {
      "names": ["田中", "花子"],
      "images": {
        "normal": "images/tanaka-hanako.png"
      },
      "description": "田中花子の詳細をここに書く"
    },
    "taro: {
      "names": ["山田", "太郎"],
      "images": {
        "normal": "images/yamada-tarou.png"
      },
      "description": "山田太郎の詳細をここに書く"
    }
  }
}

必要に応じて画像ファイルを追加

上のdata.jsonでは、キャラクターの画像としてimages/tanaka-hanako.pngなどのようなファイルが入っているので、キャラクター画像が用意できるなら、imagesフォルダーを作って、そこにtanaka-hanako.pngのようなファイルを放り込んで下さい。

画像がなければ、data.jsonからimagesの項目そのものを削除しても構いません。

ファイルをzipでまとめる

あとは上記のファイルすべてを一つのフォルダーに入れて、zipで圧縮してまとめます。

zipにするのが面倒なら、index.tnと同じディレクトリにdata.jsonをおいておくだけでも構いません。

ファイルをTypeNovelReaderで開く

上で作ったzipファイル(もしくはdata.jsonと同じディレクトリにあるindex.tnファイル)をTypeNovelReaderで開きます。

基本的にはこれだけですが、原稿の中身がちょっと寂しいので、少し台詞などを足してみましょう。

原稿に台詞を足してみる

@scene(){
  @speak("taro"){ 「ぼくは太郎さ」 }
  @speak("hanako"){ 「わたしは花子よ」 }
}

原稿を修正したらリロード

TypeNovelReaderの上部メニューに「Rebuild」というボタンがあるので、押すと変更が反映されます(ファイルを開き直す必要はありません)。

リビルドすると、台詞が表示されるだけじゃなく、それぞれの台詞の上に人物アイコンのようなものが表示されます。

アイコンにカーソルを乗せると、人物名がポップアップし、さらにそのアイコンをクリックすると、data.jsonで記述したキャラクターの詳細文がダイアログで表示されます。

その他の表現

よく使いそうなものを紹介します。

ルビ

$ruby("漢字", "かんじ")にルビをふる。

圏点・傍点

日本語で$fdot("圏点")を打つ。
日本語で$odot("圏点")を打つ。
日本語で$ftriangle("圏点")を打つ。
日本語で$otriangle("圏点")を打つ。
日本語で$fsesame("傍点")を打つ。
日本語で$osesame("傍点")を打つ。

縦中横

これは事件だ$tcy("!!")

チップ・リンク

チップ・リンクが最初に登場したのは、@tip("街"){
 @p(){ 1998年に現在のSpikeChunsoftの前身であるChunsoftが発売したゲーム。 }
 @p(){ ザッピング等のシステムが斬新で、今も不朽の名作として知られるアドベンチャーゲームの最高峰。}
}というゲームだったように思う。

脚注リンク

メッシはクラッキ@notes(){
  ポルトガル語で「名手」を意味する言葉
}である。

画像

$img("images/tb.png", 100, 100)

画像(行頭方向に寄せる)

$img("images/tb.png", 100, 100, "float start")

画像(行末方向に寄せる)

$img("images/tb.png", 100, 100, "float end")

シーンの制約(時間)

season(季節)とtime(時刻)とdate(日付)をシーンの制約に含めると、その結果がUIのテーマに反映されます。

「読者に読解力と記憶力を求めない小説」は可能か? TypeNovel用の電子書籍リーダー「TypeNovelReader」を公開しました

松本○志 「言語だけあっても、ビューアーないと不安よな。TypeNovelReader、動きます」

github.com

というわけで、TypeNovelの原稿を放り込んだら、コンパイルされてページ送りの組版がズバッ!と表示されるTypeNovelReaderを公開しました。

TypeNovelで記載した「時間」とか「人物」などといった情報も、ちゃんとページに反映されます。

ちなみにTypeNovelコンパイラも一緒に入っているので、別途ダウンロードする必要はありません。

Windows用のインストーラーは.exe Mac用のインストーラーは.dmg Linux用は.AppImage

使い方

TypeNovelで書かれた原稿を、ドラッグ・アンド・ドロップで放り込むだけです。

FileメニューからOpenを選んでもいいです。

設定ファイル(この記事の下で解説)や、複数ファイルをまとめてzip化したものを開くこともできます。あと*.html, *.txt, *.mdも可。

コンパイルエラーは(もしあれば)画面下のほうに表示されます。

特徴

TypeNovelで執筆した原稿をTypeNovelReaderで表示させることで、以下のような「読者を補助する機能」が、簡単に利用できます。

誰のセリフなのかが分かり、かつその人物が何者なのかを思い出せるUI

@speakタグを使って台詞を記述すると、各セリフの上に人物アイコンが出ます。

f:id:convertical:20190727091724p:plain
通常の台詞

カーソルを上に乗せると人物名がポップアップします。

f:id:convertical:20190727091808p:plain
名前がポップアップ

また@sb-start@sb-endを使うと、画像つきの吹き出しセリフを表示します。

f:id:convertical:20190727091946p:plain
吹き出しを使った台詞

@sb-startを使うとアバター画像が先に表示され、台詞が次に表示されます。@sb-endはその逆で、台詞が先でアバターが後です。

人物アイコンやアバター画像をクリックすると、詳細なプロフィールが表示されます。

f:id:convertical:20190727092017p:plain
キャラクターの詳細を表示

一般に登場人物の多い作品は、それぞれの名前を覚えるのに難儀しますので、こういうUIは有効なのではないでしょうか。

シーンの切り替わりで改ページが入る

一般にシーンの切り替えは、場所、時間などの切り替えを伴うはずです。

よって、そのタイミングで読者の視覚に全く変化がないのは直感的におかしいと考え、シーンの切り替えで改ページが入るようにしました。

シーンに記述された季節や時刻がUIのテーマに反映される

シーンにseason(季節)とdate(日付)とtime(時刻)の記述があった場合、その情報がUIに反映されます。

例えば季節が秋だと、メニューと本文の間にある補助線が紅葉に代わり、時刻が夕方だとオレンジ色の空の画像が左上に表示されます。

f:id:convertical:20190727092044p:plain
秋の装い

「逆に情緒が削がれる!」という場合は、画面上部のメニューボタンから無効にすることも出来ます。あるいは後ほど解説する設定ファイル(data.json)で無効にすることも可能です。

チップ機能と脚注機能

チップ機能は、文章の中の特定の単語に補足の説明を付け足したいときに使います。

f:id:convertical:20190727092125p:plain
チップリンク

クリックすると、補足情報が表示されます。

f:id:convertical:20190727092144p:plain
クリックで補足情報が表示される

部分的にちょっとした情報を付け足したいときには、脚注が便利です。

f:id:convertical:20190727092206p:plain
脚注リンク

これも同じく、クリックすると補足情報が表示されます。

f:id:convertical:20190727092224p:plain
クリックすると補足情報が表示される

目次項目を追従

作中に目次が設定されていれば、ステータス部分で現在読んでいる章が表示されます。

縦書きにも横書きにも対応

nehanを使っているので当たり前なのですが、縦書き横書き(のページ送り)に対応しています。

ルビ、圏点・傍点、ドロップキャプス、画像などにも対応

基本的に縦書き文庫で使える組版は全て使用可能です。

f:id:convertical:20190727093941p:plain
ルビ・圏点・傍点

ルビの書き方とかは、公式のサンプルを見て下さい。

執筆する人たち向けの情報

以下は執筆者向けの情報です。

まずはサンプルを動かす

先の表示例は公式のサンプルによるものです。

公式サンプルのsemantic-novel-jp.zipをTypeNovelReaderにドラッグ・アンド・ドロップしてください。

あるいは解凍して、中身のindex.tnを放り込んでも同じ結果になります。

次にサンプルを解凍して、中にあるindex.tnとかdata.jsonを眺め、表示結果と比べてみてください。

なんとなく書き方がわかると思います。

ちなみに解凍した先のディレクトリからindex.tnだけをTypeNovelReaderに放り込んでも、同じように作品を表示させることができます。

なお、原稿の中身を書き換えた場合は、画面上部のメニューに再構築(Rebuild)用のボタンが便利です。押すと作品が再コンパイルされ、変更が反映されます。

f:id:convertical:20190727092250p:plain
リビルドボタン

注意点

執筆するにあたり、いくつかの注意点があります。

注意1 メインの原稿ファイルはindex.tn

TypeNovelでは、各ソースの中で外部の原稿を$include("chapter1.tn")のようにして取り込むことができます。

なので、複数のファイルをzip化して開くときなどに、どのファイルが原稿の入り口なのかがわからないと、困ったことになります。

というわけで、TypeNovelReaderでは、その入り口に該当するファイルをindex.tnである、と規定しています。

とはいえzip化せず、直に原稿ファイル(*.tn)を放り込むのであれば、どんなファイル名でも問題ないのですが、いちおう「そういうルールがあるんだな」ぐらいに覚えておいてください。

注意2 作品情報はdata.jsonに記述

中身はこんな感じのファイルです。なんとなくですが、見たらわかるのではないでしょうか。

作品のタイトル、作者情報、初期の書式モード(縦書きか、横書きか)、登場人物など、色々なことについて設定できます。

内容については、各自で自分の作品用に直して使ってください。

{
  "title": "サンプル作品",
  "author": "縦書き文庫",
  "email": "lambda.watanabe@gmail.com",
  "homepage": "https://tb.antiscroll.com",
  "writingMode": "vertical-rl",
  "speechAvatarSize": 50,
  "enableSemanticUI": true,
  "displayTypeNovelError": true,
  "characters": {
    "john": {
      "names": ["John", "Adams"],
      "images": {
        "normal": "images/avatar2.svg"
      },
      "description": "Desription of John Adams"
    },
    "taro": {
      "names": ["山田", "太郎"],
      "images": {
        "normal": "images/avatar1.svg"
      },
      "description": "山田太郎の詳細をここに書く"
    }
  }
}

注意3 tnconfig.jsonはなくても大丈夫

サンプルにも含まれていませんが、tnconfig.jsonがない場合は公式の初期設定ファイルが勝手に使用されます。

公式の設定に加えて独自のマークアップなどを追加したい場合は、このファイルを編集して、index.tnと同じ場所に置くことになります。

しかし不注意に弄ると公式のマークアップと矛盾したり、公式の更新と衝突したりするので、あまり手を出さないほうが無難だと思います…

注意4 SemanticNovelについて

サンプルのファイル名がsemantic-novel-jp.zipとあるのですが、実はこのサンプルはSemanticNovelというTypeNovelのマークアップ仕様に沿って記述されています。

で、TypeNovelReaderも、この仕様に沿ったものを表示するという前提で開発されています。

つまり、この仕様に則っていない場合は、せっかくTypeNovelで型を書いても、それを十分に活かせないということになります。

ただこの仕様、まだまだ検討中のもので、今後大きく変わることもあり得るので、ご注意下さい。

SemanticNovelについて、何かしら新しいアイデアや要望があるかたは、Githubの上のページでご提案いただけたらと思います。

オープンソースという文化の性質上、やむを得ずページは英語で公開していますが、もちろん日本語で投稿していただいても全く問題ないです。

ライセンス

ライセンスはGPLv3です。

つまり無償で利用できるし、無償で改変できるけれども、改変したソースもまたGPLv3であることが求められます。

ということで、ソフトウェアを改変した後に、ソースを秘密にしたまま別アプリとかウェブサービスとして再配布、みたいなことはできません。

利用するにしても、ソースはGPLv3で公開して下さい、ということになります。

不具合について

不具合の報告はGithubや、Twitterまでどうぞ。

TypeNovelでルビをふる

ルビタグをそのまま記述しても良いのですが、こういう感じの設定をtnconfig.jsonに加えると(気持ち?)楽になります。

{
  "markupMap":{
    "$ruby":{
      "validate": false,
      "content": "<arg1><rt><arg2></rt>"
    }
  }
}

本文中はこんな感じでマークアップします。

$ruby("漢字", "かんじ")にふりがなをふる。

コンパイルすると、こうなります。

<ruby>漢字<rt>かんじ</rt></ruby>にふりがなをふる

ちなみに$rubyのmarkupMapで"validate":falseとなっているので、制約としての警告は出ないことに注意して下さい。

このように、純粋にインラインタグとして使いたいだけの場合は、markupMapで"validate":falseと設定しておくのが便利です。

ちなみに、この$rubyについてはv0.9.2より、最初から設定に含まれるようになる予定です。