Redmine テーマ minimalflat2 v1.3.5 リリース

2017年11月6日 0 開発 ,

Redmine テーマ minimalflat2 v1.3.5 をリリースした。

Redmine v3.4.3 対応

以下の問題が報告されたので

Redmine v3.4.2 と v3.4.3 の application.css を比較して変更点を反映してから修正しようとした。結果、CSS の差分はこの問題に対するものだけであることが判明。

よって Redmine v3.4.3 対応ついでに、チケット一覧のバージョン条件リストに名前の長いものがあっても省略されず表示するようになった。

マイページ画面の table が見切れる問題の修正

だいぶ前に報告されて放置してた

When “my project” is displayed in 1 column mode, the table protrudes – Issue #117

を修正した。マイページ画面を表示しながら Web ブラウザーの幅を狭めて 1 カラムにすると table が見切れる。標準テーマでは起きないとのことなので調べたら、

  • 標準テーマは body に font-size: 12px を指定している
  • そのため「たまたま」表示幅を狭めても table のコンテンツが収まる
  • minimalflat2 は特別な箇所をのぞき font-size は指定しない設計方針
  • そのため Web ブラウザーの文字サイズによっては table コンテンツがはみ出る = 見切れる

ということがわかった。短絡的に修正するなら標準テーマ同様に bodyfont-size: 12px を指定すれば済む。しかし現在の設計方針はユーザーの文字サイズを維持するためのものであり、ひとつの問題のため変えたくはない。マイページの table 限定で直すことも可能だが、ここだけ文字サイズが小さくなるのも収まりが悪い。

というわけで修正を断ろうとしたのだが、マイページをよく観察すると「ウォッチしているチケット」などの table は表示幅が狭くなったときに横スクロールバーを提供している。HTML の構造としては

<div class="autoscroll">
  <table class="list issues odd-even sort-by-updated-on sort-desc">
  </table>
</div>

こんな感じになっていて、table の親となる <div class="autoscroll">

.autoscroll {
  overflow-x: auto;
  padding: 1px;
  margin-bottom: 1.2em;
  position: relative;
}

となっているおかげでスクロール可能になっていた。ならば「作業時間」などの table もこれを指定すれば?と思うだろう。しかしわざわざ <div class="autoscroll"> が用意されていることから分かるように table へ指定してもダメだ。可変幅の table にしたければスクロールは外周の div が担当しなければならない。

というわけで theme.js 側に以下の関数を定義して実行するようにした。

// The table can be scrolled when "My Page" is displayed in one column
function setupMyPageAutoScroll () {
  $('#my-page table').each(function () {
    var parent = $(this).parent()
    if (parent.hasClass('autoscroll')) {
      return
    }

    $(this).wrap($('<div class="autoscroll"></div>'))
  })
}

<div class="autoscroll"> で囲われていない table 限定で囲いなおしている。これで無事、他の table も横スクロール可能となった。

設計的に一部の table だけスクロール可能になってるのは統一感がなくておかしいため、将来の Redmine では同様の修正が入るかもしれない。その場合でも処理的に誤動作することはないはず。

Electron を試す 10 – Main プロセスのデバッグ

2017年8月23日 0 開発 ,

このシリーズ第一回node-inspector を利用した Main プロセスのステップ実行を紹介した。しかし node-inspector は現在、非推奨である。Node v6 でサポートされた --inspect を使用することになっている。

Node 同様に Electron も v1.7.2--inspect が追加された。つまり Node/Electron 共に単体でステップ実行を利用できる。現時点の latest version は v1.7.5 なので開発者としても正式にこの機能を採用する時がきた。

というわけで、Electron が提供するデバッグ機能についてまとめる。

サンプル プロジェクトは以下。

Chrome

Electron 公式サイトに Main プロセスをデバッグするための方法が掲載されている。

Debugging the Main Process | Electron

Electron CLI を実行する際に --inspect=[port] オプションを指定することで、Electron の Main プロセスと外部デバッガーを連携できる。例えば

electron --inspect=5858 your/app

のようにする。package.json の npm-scripts に定義する場合は

{
  "scripts": {
    "app": "electron --inspect=5858 src/"
  }
}

こんな感じになる。実際に Terminal から実行すると

$ npm run app

> electron-audio-player@1.4.0 app .../examples-electron/audio-player
> electron --inspect=5858 src/

Debugger listening on port 5858.
Warning: This is an experimental feature and could change at any time.
To start debugging, open the following URL in Chrome:
    chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:5858/ee7b65f0-dc0d-46f7-9d9e-997441981f52

外部デバッガーとして使用する Chrome 用の URL が出力される。これを Chrome のアドレス バーに入力してページを表示すれば DevTools が割り当てられる。

--inspect と Chrome

あとは普段 DevTools を利用しているように Sources タブからブレーク ポイントを貼ってステップ実行したり、変数の内容を参照するなどのデバッグが可能となる。

なお --inspect=[port]=[port] は省略可能。その場合は自動的にポート 5858 が選択されたものとして扱われる。省略するかはお好みで。開発環境によってはポート番号が競合するかもしれないので、変更可能であることを明示するため規定値でもそのまま指定しておくのもよいだろう。私はそうしている。

Visual Studio Code

Atom と並んで Web アプリ開発者に人気のテキスト エディター Visual Studio Code (以下、vscode) にはデバッグ機能が実装されている。これは vscode の大きな特徴であり、エディターにも関わらず IDE のようなデバッグを可能とする。

これは汎用的に設計してあり、様々な言語へ対応できるようになっている。Microsoft 謹製の TypeScript や C# は当然として Python、Ruby、PHP といった言語から Node (JavaScript) まで幅広くサポート。もちろん Electron からも利用可能。

Main プロセスを素の Node で実装している場合は上記の公式資料にある設定だけで使い始められる。しかし Babel などの Transpiler、Browserifywebpack といった Bundler を利用して JavaScript を変換しているならば対象となるコードを指定するために工夫が要る。

vscode のデバッグ機能と設定ファイルの詳細は以下を参照のこと。

launch.json

はじめに vscode 用の設定ファイルを定義する。vscode で読み込むプロジェクトのルートに .vscode フォルダーを作成し、その中へ launch.json というファイルを作成。内容は以下。

{
  // Use IntelliSense to learn about possible Node.js debug attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Main Process",
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
      "windows": {
        "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
      },
      "program": "${workspaceRoot}/src/js/main/Main.js",
      "outFiles": [
        "${workspaceRoot}/src/assets/main.js"
      ]
    }
  ]
}

configurations 内に設定を定義してゆく。

type はデバッグ実行のプラットフォームを表す。Electron 用の設定ではなく Node として実行する。request は新たにプロセスを起動する launch と起動されているものを利用する attach のいずれかを指定する。基本、launch でいい。name は役割で命名しておく。

${workspaceRoot} は特別な変数で、プロジェクトのルート フォルダーをあらわす。基本的に設定はこのフォルダー内で完結するだろうから、パスはこれで開始しておけばよい。

runtimeExecutable はデバッグ実行で使用する Electron のパスになる。Windows だけ起動方法が変わる (cmd.exe 経由になる) ため windows 内へ個別に定義が必要。

program はデバッグ実行のエントリー ポイントになるコードを指定する。Transpiler を利用しているなら変換元のエントリー ポイントになるファイルを指定する。変換先を指定しても動作するが、Bundler で複数ファイルを単一化しているとブレーク ポイントを貼りにくくなる。

outFilesprogram と関連付けるファイル一覧を定義。Transpiler を利用しているなら変換先のエントリー ポイントになるファイルを指定する。以前は sourceMapstrue に指定する必要もあったが、現在はデフォルトで有効になっている。Transpiler/Bundler と Source Maps を利用した最小の設定としては今回のもので十分。

他にもデフォルトが変更されて省略可能なものがあったり、outDir が deprecated で outFiles が代替になっているなどの変化がある。この記事ですら古びるかもしれないので、自分で設定する際は前述の公式資料を必ず確認すること。

Source Maps の注意点

私の環境は Babel + Browserify になる。元のコードは ES.next (例えば ES2015 の Modules など) で書いて、Babel により Electron 向けに変換している。これらのタスクは package.json の npm-scripts で組んでおり、デバッグに関連する定義は以下となる。

{
  "babel": {
    "presets": [
      ["env", {"targets": {"electron": "1.7"}}],
      "react"
    ]
  },
  "scripts": {
    "watch:js-main": "watchify -v -t [ babelify ] ./src/js/main/Main.js --exclude electron --im --no-detect-globals --node -o \"exorcist --base ./src/assets ./src/assets/main.js.map > ./src/assets/main.js\" -d"
  },
  "devDependencies": {
    "babel-preset-env": "^1.6.0",
    "babel-preset-power-assert": "^1.0.0",
    "babel-preset-react": "^6.24.1",
    "babelify": "^7.3.0",
    "browserify": "^14.4.0",
    "electron": "^1.7.5",
    "exorcist": "^0.4.0",
    "watchify": "^3.9.0"
  },
}

watch:js-main が browserify (watchify) によるファイル監視つきの JavaScript 変換になる。

browserify により変換された内容を exorcist に渡して Source Maps となる main.js.map を生成し、その後に変換結果となる main.js を出力している。

これらのうち、vscode のデバッグで重要なのは Source Maps にあたる map ファイルである。map ファイルは変換元ファイルのパスとコード行などが定義されている。vscode のデバッグ機能で map ファイルを使用する場合、変換元ファイルのパスは map からの相対にしなければならない。

exorcist の README によれば、オプションを何も指定しないと変換元ファイルのパスは absolute path (絶対パス) になる。実際には exorcist を実行したフォルダーをルートにした相対パスになるようだが、どちらにせよ map ファイルから見た変換元ファイルへのパスにはならない。

この状態で vscode のデバッグを開始すると変換元を見つけられず program に指定されたファイルをそのまま解釈してしまう。デバッガーは Node として動作するため、現時点で未対応の import 構文などを見つけると Syntax Error 例外でプロセスが中断される。

これを防ぐため exorcist の --base オプションに map と変換されたファイルの出力先フォルダーを指定。変換元ファイルへのパスがそこから見た相対となるようにした。

exorcist --base ./src/assets ./src/assets/main.js.map > ./src/assets/main.js

--base [PATH] の有無で map ファイルの sources が変化することを確認できる。

デバッグ実行

すべての設定が正しく定義されていれば vscode のデバッグ機能を利用できる。JavaScript 変換 & map ファイル生成された状態で vscode のメニューから「デバッグ」、「デバッグの開始」を選ぶとアプリが起動されるはず。

次に vscode でブレーク ポイントを貼りたいファイルを開き、対象行の行番号の左側をクリックすると赤い丸印アイコンが表示される。

ブレーク ポイント

この状態でその場所を通るような操作をアプリから実行すると、その箇所でプロセスが一時停止 (ブレーク) する。このとき vscode のサイドバーからデバッグを開いていれば、変数なども確認できる。

デバッグ実行

コード編集に使用しているエディターとデバッガーがひとつになっていることの便利さ (IDE 感) を実感。これはいい。

userData のパス問題

Electron でアプリ固有のデータを保存する場合は app.getPath('userData') で得られたフォルダーを利用することが多いだろう。このパスは Electron の Web Storage や IndexedDB などを保存する場所で、OS のユーザー単位かつアプリ単位で作成される。

userData には

The directory for storing your app’s configuration files, which by default it is the appData directory appended with your app’s name.

とある。この説明なら package.jsonname が参照されることを期待するけど、実際にはアプリの実行ファイル名が使用されるようだ。例えば AudioPlayer.appAudioPlayer.exe
であれば AudioPlayer というフォルダーになる。

Chrome によるデバッグでは Electron プロセスを単体実行して Chrome と関連付けているだけなので、userData は通常のアプリと同じ場所になる。しかし vscode のデバッグ実行だとフォルダー名は Electron 一択となる。

そのため複数のアプリでデバッグ実行している場合、データが競合するかもしれない。これに気付いたのは、これまで --inspect を利用して IndexedDB などに保存していたデータが vscode のデバッグだと読み込めていなかったから。userData のパスを調べたら、どのプロジェクトでも Electron になっていた。app.getName()Electron を返す。

かなり致命的な問題と思うのだけど、ざっと調べたかぎり vscode 公式のデバッグ関連資料や GitHub issues にはこれについての言及が見つからなかった。後で issue あげるかも。

いちおう app.setPath('userData', path) でパスの上書きは可能だが app.getName()Electron なのでアプリ名を抽象化しきれず、ハード コードが発生する。そもそも開発環境の都合でアプリ側のコードが左右されるのも好ましくない。

この件について情報をお持ちの方は、コメント欄や Twitter などで知らせていただきたい。

Atom v1.19.0 の IME 問題とダウングレード方法

2017年8月9日 0 開発

Atom v1.19.0 の更新があったので反映したら、IME 入力がおかしい。例えば「にほんご」と入力した場合、変換中は一文字目だけしか表示されず、順に「に」、「ほ」、「ん」、「ご」と切り替わってゆく。変換前の文字がひとつしか見えないので、まともに入力できない状態。あまりに致命的な問題なので issue があるだろうと探したら以下をみつけた (ついでにコメントしておいた)。

beta2 で見つかっていた問題だが、残念ながら安定版でも再現する。コメントを読むと影響があるようならダウングレードしてほしいとのこと。Atom の標準機能としてはダウングレードを提供していないので手動実行する必要あり。手順は以下。

  1. Atom が起動しているなら終了しておく
  2. Web ブラウザーでダウングレードしたい旧バージョンのリリースページにアクセスする
    • リリースされたバージョンは Releases に一覧あり
    • 例えば Release 1.18.0 にアクセスする
  3. リリース用イメージを入手する
    • Windows は AtomSetup.exe
    • macOS は atom-mac.zip
  4. 旧バージョンをインストールする
    • Windows は AtomSetup.exe を実行
    • macOS は atom-mac.zip を展開した中身の Atom.app をアプリケーション フォルダーに上書き
  5. Atom を起動してバージョンを確認

この手順で v1.18.0 にダウングレードして IME 問題が再現しないことを確認できた。

ダウングレードにおける懸念としてプラグインの互換性がある。例えばプラグインが最新の Atom に依存している場合、ダウングレードによって不具合を生じるかもしれない。今回のように minor レベルの更新なら大丈夫そうだけど、major の場合は要注意である。

安定版とはいえ致命的なデグレードが発生することは今後もありえるだろうから、今回の件はダウングレード手順を調べるよい契機だと考ることにした。

  • 2017/8/15: Atom v1.19.1 にて本記事の問題が修正されていることを確認