mac 環境構築メモ
OS
環境設定
- トラックパッド
- ポイントとクリック
- 調べる&データ検出:OFF
- 副ボタンのクリック:2本指でクリックまたはタップ
- タップでクリック:1本指でタップ
- スクロールとズーム
- スクロールの方向:ナチュラル:OFF
- その他のジェスチャ
- ページ間をスワイプ:3本でスワイプ
- フルスクリーンアプリケーション間をスワイプ:4本指で左右にスワイプ
- ポイントとクリック
- アクセシビリティ
- キーボード
- キーボード
- キーのリピート:速い
- リピート入力認識までの時間:短い
- ショートカット
- キーボード
- 次のウィンドウを操作対象にする:Option+Tab
- 入力ソース
- 前の入力ソースを選択:command+space
- キーボード
- 入力ソース
- キーボード
アプリ
- Chrome Web Browser
- Dropbox
- Google Drive - Cloud Storage & File Backup for Photos, Docs & More
- iTerm2 - macOS Terminal Replacement
- Visual Studio Code - Code Editing. Redefined
- Xcode - Apple Developer
- MiddleClick
- Spectacle
- Google 日本語入力 – Google
- Docker For Mac | Docker
- Karabiner - Software for macOS
- Simple Modifications
- left_option -> left_command
- left_command -> left_option
- right_option -> right_command
- Complex Modifications
- コマンドキーを単体で押したときに、英数・かなキーを送信する
- Simple Modifications
- Vagrant by HashiCorp
- Oracle VM VirtualBox
- Discord - Free Voice and Text Chat for Gamers
画像からQuineを生成するGemを作った
AAQ - Ascii Art Quine
できること
画像からRubyのQuineをつくれます。たとえば、Octocatの画像をQuineにしてみます。
出力されるアスキーアートは自分自身を出力するRubyのプログラムになっています。
aaq Octocat.png | tee OctocatQuine.rb ruby OctocatQuine.rb
--color
オプションを付けるとカラフルになります。これもQuineになっているので、エスケープシーケンスを取り除けばRubyで実行できます。
ruby OctocatQuine.rb --color ruby OctocatQuine.rb --color | ruby -ne 'puts $_.gsub(/\e.*?m/, "")' | ruby # エスケープシーケンスを除いて実行
インストール
以下の環境で動作確認しています。
rmagickを使っているので、 ImageMagickを先にインストールしておきます。 ImageMagickのバージョン7を入れてしまうと、rmagickのインストール時にエラー1が出てしまうので、バージョン6をインストールします。
brew install imagemagick@6 echo 'export PATH="/usr/local/opt/imagemagick@6/bin:${PATH}"' >> ~/.bash_profile echo 'export PKG_CONFIG_PATH="/usr/local/opt/imagemagick@6/lib/pkgconfig:${PKG_CONFIG_PATH}"' >> ~/.bash_profile
gemでaaqをインストールします。
gem install aaq
動作確認します。
$ aaq -v
aaq 0.1.2
Enjoy!
参考
Quineの作り方
gifはasciinemaとasciicast2gifを使いました。
asciinema rec screen.cast docker run --rm -v $PWD:/data asciinema/asciicast2gif -s 1.8 -t monokai screen.cast screen.gif
Gemの公開方法
色のエスケープシーケンス
Ubuntu 18.04 環境構築メモ
インストールを3回やるはめになったのでメモ。
インストール
設定
- 背景
- Dock
- アイコンサイズ
- 地域と言語
- English
- ユニバーサルアクセス
- カーソル点滅OFF
- 電源
- ブランクスクリーン
CapsLockをCtrlにする
gsettings set org.gnome.desktop.input-sources xkb-options "['ctrl:nocaps']"
linuxbrew
Linuxbrew | The Homebrew package manager for Linux
sudo gem install activesupport plist backports sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)" test -d ~/.linuxbrew && eval $(~/.linuxbrew/bin/brew shellenv) test -d /home/linuxbrew/.linuxbrew && eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) test -r ~/.bash_profile && echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.bash_profile echo "eval \$($(brew --prefix)/bin/brew shellenv)" >>~/.profile
apt
sudo apt install -y vim emacs tmux screen git curl sudo apt install -y build-essential gcc make openssl libssl-dev libbz2-dev libreadline-dev libsqlite3-dev zlib1g-dev
deb
sudo dpkg -i *.deb
Dotfiles
git clone https://github.com/yskoht/dotfiles.git ~/dotfiles
cd dotfiles
make install setup
Python
git clone https://github.com/pyenv/pyenv.git ~/.pyenv git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv pyenv install 3.6.5 pyenv global 3.6.5 pip install --upgrade pip
Ruby
sudo apt install -y rbenv ruby-build git clone https://github.com/jf/rbenv-gemset.git $HOME/.rbenv/plugins/rbenv-gemset rbenv install 2.4.1 rbenv global 2.4.1
Node.js
sudo apt install -y nodejs npm
fish
sudo apt-add-repository ppa:fish-shell/release-2 sudo apt-get update sudo apt-get -y install fish sudo apt install -y peco curl -Lo ~/.config/fish/functions/fisher.fish --create-dirs git.io/fisherman chsh fisher z fisher 0rax/fish-bd fisher oh-my-fish/plugin-peco fisher simnalamburt/shellder
powerline font
git clone https://github.com/powerline/fonts.git ./fonts/install.sh
fcitx
sudo apt install -y fcitx-mozc sudo apt remove -y ibus
Javascriptを調べたときのメモ
ES2015, ES2016, ES2017
- ES2015~2017チートシート - Qiita
- ES2016 / ES2017の最新動向を追ってみた - Qiita
- 正式リリースされたES8の主な新機能 ???? | POSTD
- ES2015(ES6)な時代だからこそ、ES5を改めて調べたJavaScript初級者のメモ - Qiita
- JavaScriptのclass - Qiita
Promise, async/await
Tutorial: Intro To Reactをやったメモ
追加課題のメモです。初心者です。
1. Display the location for each move in the format (col, row) in the move history list.
@@ -49,6 +49,8 @@ class Game extends React.Component { this.state = { history: [{ squares: Array(9).fill(null), + col: null, + row: null, }], stepNumber: 0, xIsNext: true, @@ -66,6 +68,8 @@ class Game extends React.Component { this.setState({ history: history.concat([{ squares: squares, + col: (i % 3) + 1, + row: Math.floor(i / 3) + 1, }]), stepNumber: history.length, xIsNext: !this.state.xIsNext, @@ -86,7 +90,7 @@ class Game extends React.Component { const moves = history.map((step, move) => { const desc = move ? - 'Go to move #' + move : + 'Go to move #' + move + ' (' + step.col + ',' + step.row + ')' : 'Go to game start'; return ( <li key={move}>
状態にcol
とrow
を持つように変更。
2. Bold the currently selected item in the move list.
index.js
@@ -92,9 +92,10 @@ class Game extends React.Component { const desc = move ? 'Go to move #' + move + ' (' + step.col + ',' + step.row + ')' : 'Go to game start'; + const textStyle = (move === this.state.stepNumber) ? 'bold' : null; return ( <li key={move}> - <button onClick={() => this.jumpTo(move)}>{desc}</button> + <button className={textStyle} onClick={() => this.jumpTo(move)}>{desc}</button> </li> ); });
style.css
@@ -48,3 +48,7 @@ ol, ul { .game-info { margin-left: 20px; } + +.bold { + font-weight: bold; +}
className
を切り替え。
3. Rewrite Board to use two loops to make the squares instead of hardcoding them.
@@ -16,30 +16,21 @@ class Board extends React.Component { <Square value={this.props.squares[i]} onClick={() => this.props.onClick(i)} + key={i} /> ); } render() { - return ( - <div> - <div className="board-row"> - {this.renderSquare(0)} - {this.renderSquare(1)} - {this.renderSquare(2)} - </div> - <div className="board-row"> - {this.renderSquare(3)} - {this.renderSquare(4)} - {this.renderSquare(5)} - </div> - <div className="board-row"> - {this.renderSquare(6)} - {this.renderSquare(7)} - {this.renderSquare(8)} - </div> - </div> - ); + let squares = []; + for(let row = 0; row < 3; row++) { + let rows = []; + for(let col = 0; col < 3; col++) { + rows.push(this.renderSquare(row*3 + col)); + } + squares.push(<div className="board-row" key={row}>{rows}</div>); + } + return (<div>{squares}</div>); } }
key
を追加。
4. Add a toggle button that lets you sort the moves in either ascending or descending order.
@@ -45,6 +45,7 @@ class Game extends React.Component { }], stepNumber: 0, xIsNext: true, + isDescendingOrder: false, }; } @@ -74,12 +75,18 @@ class Game extends React.Component { }); } + toggleOrder() { + this.setState({ + isDescendingOrder: !this.state.isDescendingOrder, + }); + } + render() { const history = this.state.history; const current = history[this.state.stepNumber]; const winner = calculateWinner(current.squares); - const moves = history.map((step, move) => { + let moves = history.map((step, move) => { const desc = move ? 'Go to move #' + move + ' (' + step.col + ',' + step.row + ')' : 'Go to game start'; @@ -90,6 +97,14 @@ class Game extends React.Component { </li> ); }); + let movesList; + if(this.state.isDescendingOrder) { + moves.reverse(); + movesList = <ol reversed>{moves}</ol>; + } + else { + movesList = <ol>{moves}</ol>; + } let status; if(winner) { @@ -108,7 +123,8 @@ class Game extends React.Component { </div> <div className="game-info"> <div>{status}</div> - <ol>{moves}</ol> + <button onClick={() => this.toggleOrder()}>toggle</button> + {movesList} </div> </div> );
状態にisDescendingOrder
を追加。<ol>
まで逆順にする必要はなかったかもしれない。
5. When someone wins, highlight the three squares that caused the win.
index.js
@@ -3,8 +3,9 @@ import ReactDOM from 'react-dom'; import './style.css'; function Square(props) { + const buttonClass = props.isHighlight ? "square highlight" : "square"; return ( - <button className="square" onClick={props.onClick}> + <button className={buttonClass} onClick={props.onClick}> {props.value} </button> ); @@ -17,6 +18,7 @@ class Board extends React.Component { value={this.props.squares[i]} onClick={() => this.props.onClick(i)} key={i} + isHighlight={this.props.highlightLine.includes(i)} /> ); } @@ -84,7 +86,8 @@ class Game extends React.Component { render() { const history = this.state.history; const current = history[this.state.stepNumber]; - const winner = calculateWinner(current.squares); + const w = calculateWinner(current.squares); + const [winner, line] = (w) ? w : [null, [null]]; let moves = history.map((step, move) => { const desc = move ? @@ -119,6 +122,7 @@ class Game extends React.Component { <Board squares={current.squares} onClick={(i) => this.handleClick(i)} + highlightLine={line} /> </div> <div className="game-info"> @@ -152,7 +156,7 @@ function calculateWinner(squares) { for(let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if(squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { - return squares[a]; + return [squares[a], lines[i]]; } } return null;
style.css
@@ -40,6 +40,10 @@ ol, ul { background: #ddd; } +.highlight { + color: #ff0000; +} + .game { display: flex; flex-direction: row;
calculateWinner
でハイライトするラインを返すように修正。そのラインをBoard
のprops
に渡す。square
の添字がラインに含まれていたらハイライトするようにhighlight
をclassName
に追加する。
6. When no one wins, display a message about the result being a draw.
@@ -113,7 +113,11 @@ class Game extends React.Component { if(winner) { status = 'Winner: ' + winner; } else { - status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O'); + if(this.state.stepNumber === 9) { + status = 'Draw'; + } else { + status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O'); + } } return (
あれ、なんかこれ簡単じゃね?と思って書いたけど、これだと全てのマスが埋まった時にDraw
と表示されるだけなので、"When no one wins,"じゃない。
@@ -55,7 +55,9 @@ class Game extends React.Component { const history = this.state.history.slice(0, this.state.stepNumber + 1); const current = history[history.length - 1]; const squares = current.squares.slice(); - if(calculateWinner(squares) || squares[i]) { + if(calculateWinner(squares) || + squares[i] || + isDraw(squares, this.state.xIsNext)) { return; } squares[i] = this.state.xIsNext ? 'X' : 'O'; @@ -113,7 +115,11 @@ class Game extends React.Component { if(winner) { status = 'Winner: ' + winner; } else { - status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O'); + if(isDraw(current.squares, this.state.xIsNext)) { + status = 'Draw'; + } else { + status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O'); + } } return ( @@ -160,4 +166,19 @@ function calculateWinner(squares) { } } return null; -} +} + +function isDraw(squares, xIsNext) { + if(calculateWinner(squares)) { + return false; + } + + for(let i = 0; i < squares.length; i++) { + if(squares[i] === null) { + let sqrs = squares.slice(); + sqrs[i] = xIsNext ? 'X' : 'O'; + if(isDraw(sqrs, !xIsNext) === false) return false; + } + } + return true; +}
isDraw()
を追加。引き分けの時にtrue, それ以外(どちらかが勝てるパターンが残っている場合)はfalseを返す。
全探索でざっくり512パターン。イミュータブルが大事だよ、と書かれていたのでslice()
してるけど、計算量とかメモリってどのくらいまでなら許容されるのかなぁ。
Helixの親指拡張を作った
Thumbhelix Rev1
PCBとケースデータを置いていますが、いくつか不具合があります。Rev1を実際に作るのはおすすめしません。参考程度でお願いします。
先行例
既にHelixにトラックポイントを実装されている方がいます。使用感が気になります。
デモ動画
左側の隅に左ボタン、右側の隅に右ボタン、拡張キーにスクロールボタンを割り当てています。
このトラックポイントを使って細かい作業は難しいです。
不具合
凡ミスのまとめです。
PCBの裏表が逆になった
上記写真の左が表で、右が裏になるように設計していたんですが、アクリルプレートとPCBの隙間が無くてマイコンと抵抗がつけられませんでした。
それじゃあ裏表逆にして使おう。ジョイスティックとスイッチはリバーシブルで使えるパターンにしたし、と思ったのですが、スイッチは表面で使う用の配線しかしていませんでした。
さらに、キーのパターンがHelixと逆になってました。使用上は問題ないですが気持ち悪い。
スイッチが入りずらい、行が揃ってない、バックライトLEDがない
KailhロープロファイルスイッチのPCBマウントの足が入らないです(無理に押し込めば入る)。どうやらもう少し穴を大きくすべきだったらしい。
Helixのキーと行を揃えるつもりで設計したのですが、ちょっとだけ下にずれてしまいました。 バックライトLEDはもともとつける予定がなかったのですが、実物を見てみるとそこだけLEDが無いのはやっぱりかっこ悪いですね。
トラックポイントの操作感が悪い
ThinkPadのトラックポイントに比べて動かすときに力が要ります。 これはジョイスティックのデバイス自身の問題なのでどうしようもないですが、 カーソルの速度制御などはソフトで改善できる気がするので頑張りたいです。
実装について
ハードウェア構成
HelixのProMicroがI2Cマスタ、Attiny85がI2Cスレーブです。
スレーブはJoystickの状態をanalogRead()
、スイッチの状態をdigitalRead()
で読み取り、マスタからリクエストがあったら、それぞれの状態をI2Cで返します。
Attiny85にはICSPでプログラムをアップロードします。
パーツリスト
部品 | メーカー名 | 定数・型番 | 個数 | 備考 |
---|---|---|---|---|
抵抗 | - | 4.7k | 2 | リード付き・1/6W。I2Cプルアップ用 |
抵抗 | - | 10k | 1 | 3216チップサイズ抵抗。SWプルアップ用 |
マイコン | Atmel | ATtiny85-20SUR | 1 | Atmel ATtiny25, ATtiny45, ATtiny85 Datasheet |
ジョイスティック | Top-Up Industry Corporation | JT8P-3.2T-B10K-1-16Y | 1 | TOP-UP INDUSTRY CORP. -- JT8P-3.2T 秋月電子:JT8P-3.2T- -1-16Y Model (1) |
ジョイスティック用キャップ | - | - | 1 | 今回はWholesale Product Snapshot Product name is for 3D analog joystick cap for PSP 2000 PSP 3000 thumbsticks thumb sticks capを加工して使用 |
スイッチ | Kailh | PG1350-* | 1 | CherryMX互換スイッチも可 |
PCB | - | - | 1 | thumbhelix_device/rev1/pcb at master · yskoht/thumbhelix_device |
アクリルプレート | - | - | ボトム・トップ各1 | thumbhelix_device/rev1/case at master · yskoht/thumbhelix_device Kailh Low Profileの場合、厚さは1.5mm~2.0mmが適 |
ネジ・ナット・ワッシャー | - | M2 | - | Kailh Low Profileの場合は10mmネジが適 |
ゴム足 | - | - | - | - |
マイコン、ジョイスティックは秋月電子通商で購入しました。 ジョイスティック用のキャップはAliExpress.comで"PSP joystick cap"等で検索すると見つかります。ただし、少し削らないとはまりません。
PCBの作成
以下のページと書籍を参考にKicadで作ってALLPCB.comに発注しました。
- KiCadの回路記号&フットプリントを作る方法|一日体験!プロの基板作り|記事サポート | トランジスタ技術
- KiCADーライブラリへの部品の追加
- 【KiCAD】回路図における新規部品の作り方 - KiCADで基板設計
- ATtiny85を利用したLEDランプ基板の作製(6)〜回路図作成4(フットプリントの割り当て)〜 : KiCadで基板を作る
- KiCadでのフットプリントライブラリ新規作成方法 : KiCadで基板を作る
- KiCad Pcbnew 各レイヤの役割 - uchan note
Order Timeが2018/4/8 23:42:50でFlow Progressが以下でした。
おおー pic.twitter.com/plo1SFFbpr
— yskoht (@_yskoht) 2018年4月16日
アクリルケースの作成
大量ならオンラインで発注、少量・短TATならコワーキングスペーススペースに材料を持ち込み、と使い分けられるようにいろいろ経験したかったので、今回はPonokoとcoromozaの2か所にお願いすることにしました。
加工のためのデータはPonokoとcoromozaで異なるので2種類のデータを作る必要があります。
Ponoko用.svgの作成
GUIでポチポチやるのは面倒だと思ったので、Pythonのsvgwriteライブラリで作りました。
- svgwrite.Drawing Python Example
- Setting default units in svg (Python svgwrite) - Stack Overflow
- svgwrite Documentation
スクリプトはこんな感じになりました。生成されたSVGをInkscapeのテンプレートに張り付けてアップロードしました。
stroke
, stroke_width
, stroke_opacity
, fill
あたりは当然として、stroke_miterlimit
, stroke_dasharray
も適切に設定しないとPonokoにアップロードできないっぽいです。
coromoza用.aiの作成
coromozaのデータ形式はIllustraor形式なので.svg
を.ai
に変換します。
Illustratorは持っていなかったので、Illustratorが使えるPCをkinko'sでレンタルすることにしました。
店頭のPCでゼロからデータを作るのは大変だと思いますが、.svg
を.ai
に変換して線の色を変える程度であれば20分で十分でした。
どちらの加工も思った通りにできてよかったです。
スレーブ側ソフトウェア
ATtiny85のソフト作成にはATTinyCoreを使用しました。Arduino IDEと既存のライブラリを利用できるので簡単でした。
送信データはアライン詰めれば3Byteで十分ですが、シンプルにしたかったので5Byte送っています。
マスター側ソフトウェア
I2Cマスタ
マスター側は LUFAのTWIライブラリを使っています。
スレーブにリクエストを送り、データを読みだすところはこんな感じになっています。サンプルのまんまですね。
void th_read(Thumbhelix *th) { if(TWI_StartTransmission((THUMBHELIX_ADDR << 1) | TWI_ADDRESS_READ, 20) == TWI_ERROR_NoError) { uint8_t byte1, byte2, byte3, byte4, byte5; TWI_ReceiveByte(&byte1, false); TWI_ReceiveByte(&byte2, false); TWI_ReceiveByte(&byte3, false); TWI_ReceiveByte(&byte4, false); TWI_ReceiveByte(&byte5, true); TWI_StopTransmission(); ...
Pointing Device
読みだした値からQMK FirmwareのPointing Deviceを使ってカーソル移動、スクロール、クリックを行っています。
‘mouseReport.buttons‘は3bitから5bitを使うと書いてありますが、MSBが0bitです。
当初、マウスボタンのエミュレートにはQmkFirmwareのMouse Keysを使おうと思ったのですが、
Pointing DeviceとMouse Keysを両方有効にするとそれぞれでhost_mouse_send()
が呼ばれてしまい、
Mouse Keysでのドラッグが出来ませんでした1。
結局はプログラムをシンプルにしたかったのと、バイナリサイズがきつかったので結局Pointing Deviceのみを使うようにしています。
Makefileについて
qmk_firmware/users
以下にThumbhelixのファームを置くようにしました。使い方あってるのか謎です。
rules.mk
のinclude
をコメントアウトすれば無効にできます。
写真
感想
Helix ビルドログ
Helix を作りました
- Helix キーボードキット | 遊舎工房 SHOP(通販ページ)
- Helix: qmk_firmware/keyboards/helix at master · qmk/qmk_firmware(ファームウェア)
- MakotoKurauchi/helix: A compact split ortholinear keyboard.(ビルドガイド、ツール、PCBデータ等)
運よく、2018/3/6にあったHelixの一般販売でステンレス5行版キットを購入することが出来ました。
公式のビルドガイド(helix/buildguide_jp.md)を見ながら順に作成しました。
それから、以下の動画内でHelixの作成風景が紹介されています(16:10頃から)。これがとても参考になりました。
作成風景
いくつかの作業だけ写真を載せておきます。
ダイオードの実装
SMDダイオードのはんだ付けは初めてでした。こて先は白光 こて先 1C型 T18-C1を使いました。
バックライトLEDの実装
— yskoht (@_yskoht) 2018年3月18日
ビルドガイドには220℃と書かれていますが、はんだが全然溶けなかったので270℃弱くらいで短時間を意識してはんだ付けしました。
TwitterでLED実装に関してアドバイスをいただきました(ありがとうございます)。この治具は、おそらく基板の裏から添えることで、はんだ付けする際にLEDが穴の下に落ちてしまうのを防ぐためのものだと思います。基板の個体差に依ると思うのですが、自分の基板はわりとLEDの大きさぴったりだったので、ポリミイドテープで止めておくだけで十分でしたが、LEDがポロポロ落ちて困るといったときはあると便利かもしれません。
治具作ってから始めることをお勧めします。https://t.co/vPXtfDjWTn
— m.tei / ishii (@mtei) 2018年3月18日
Pro Microのはんだ付けまで終わった時点でLEDのテストをしました。 以下のKeymapを使うことで簡単にテストすることができました。
— yskoht (@_yskoht) 2018年3月18日
キースイッチの実装
「ロープロファイルスイッチも使いたいし、CherryMX互換スイッチも使いたいなー」みたい優柔不断なことを考えていたところ、Twitterで以下の記事を教えていただき、Mill-Maxソケットというものを知りました。
ただ、このソケットはロープロファイル用の穴には入らないんですね。(これは完全に自分が確認しなかったのが悪いです。@nillpoさん、こちらこそお騒がせしてすみません。)
ロープロ用の穴ちょっとだけ小さいのか!(Mill-Maxのソケット入らない pic.twitter.com/0OFc6vOPwn
— yskoht (@_yskoht) 2018年3月18日
ということで、今回はKailhロープロファイルスイッチの茶軸をPCB直に実装することにしました。
ファームウェアビルド
以下の記事にファームウエアの設定ファイルについて書かれていますが、自分も設定ファイルを間違えてRGBLIGHT_ENABLE = yes
してるのにLEDつかないなーと少し悩みました。この記事に出会えてよかったです。
オーディオケーブル
いくつかオーディオケーブルを試しましたが、相性が結構あるらしく、キーが反応しないケーブルやバックライトLEDがつかないケーブルがありました。おかしいなと思ったら、いくつか試してみるのがいいのかもしれません。
完成
開始から10時間かかりました。
キーマップ
感想
- PCBの緻密さがかっこいい、シルクのデザインがかっこいい
- バックライトLEDかっこいい、OLEDかっこいい
- ステンレスプレートの重厚感、しっかり感がめちゃめちゃいい
- ロープロファイルスイッチかっこいい。ただ自分は平面のキーキャップよりはアールがかかったキーキャップのほうが好きかも
- 下段7キーあるの便利
- はんだ付けは少し大変
- 1回の通販でほとんどのパーツが全部集まるのすごい