Sketch.app Plugin の開発メモ

# Sketch Plugin CookBook (ver.51まで)

:warning:**Sketch 52でAPIが変更になっているため、動かない記述があります。**:warning:

忘れやすい自分のための、Sketch.app向けプラグイン開発メモ。

:innocent: ツギハギで書いてきたため、内容をあまり精査できていません。ところどころ書き換えできずに間違ってます。さらにClass自体が非推奨になった（リファレンスからなくなった）ようで、~~この書き方は使えなくなる可能性が高いです。~~「使えなくなる」ではなく、JavaScript API経由で使えってことになりそうです。:innocent:

Sketchのプラグインは、[CocoaScirpt](https://github.com/ccgus/CocoaScript)で開発する。とてもざっくり言うと、Objective-CをJavaScriptライクに扱う言語（キモイ）。記述はJavaScriptスタイルとObjective-Cスタイルがあり、混在もOK。

## ドキュメントとか参考情報

* [Hello, World — Sketch Developer](http://developer.sketchapp.com/)

クラスリファレンスが見たい場合は、こちらのコミットからローカルへクローンしてJekyllでビルドすれば復元できます。

* [BohemianCoding/developer.sketchapp.com
](https://github.com/BohemianCoding/developer.sketchapp.com/tree/c4805b529e23e0a65e82a02284d0215432cd652d)

フォーラム

* [Sketch Developers](http://sketchplugins.com/)

メーリングリストとそのアーカイブ

* [dev@sketchplugins.com](http://mail.sketchplugins.com/mailman/listinfo/dev_sketchplugins.com)
* [The dev Archives](http://mail.sketchplugins.com/pipermail/dev_sketchplugins.com/)

こちらも参考に

* [Sketch Pluginの開発(開発編) - Qiita](http://qiita.com/nerd0geek1/items/e1153acdab3e3e8f3d20)
* [Sketch Pluginの開発(前提知識編) - Qiita](http://qiita.com/nerd0geek1/items/8c7192a6c962e9ac9611)
* [SketchのiOS UI Design Templateを🈲で埋め尽くすプラグイン - Qiita](http://qiita.com/hanamiju/items/a04d0a7f9e9adbb11799#_reference-6caecd2c7b707e6f6202)
* [アプリ用画像アセットを書き出す Sketch Plugin を作ってみた話 - Re.Ra.Ku アドベントカレンダー day 6 - Re.Ra.Ku tech blog](http://techblog.reraku.co.jp/entry/2016/12/06/080000)
* [Sketch3のプラグインを開発する - Qiita](http://qiita.com/noppefoxwolf/items/f161bf1f583259206130)
* [Sketch Plugin についての要約とメモ。あるいは Sketch Plugin の始め方。 - Qiita](http://qiita.com/damele0n/items/01ef0d4820585bcf7b8d)
* [Sketch-Plugins-Cookbook](https://github.com/turbobabr/Sketch-Plugins-Cookbook)
* [Sketch Plugin Snippets for Plugin Developers — Design + Sketch App — Medium](https://medium.com/sketch-app-sources/sketch-plugin-snippets-for-plugin-developers-e9e1d2ab6827)
* [CocoaScript（CocoaScriptのサンプル）](https://github.com/ccgus/CocoaScript/tree/master/examples)

## デバッグ

公式ドキュメント：[Debugging — Sketch Developer](http://developer.sketchapp.com/code-examples/debugging/)

公式ドキュメントではフィルタの設定を「sketch」としているが、プラグインのメッセージだけをみたいのであれば、「sketch plugin」とした方が良い、と思う。

### Class Dump

class-dumpというコマンドラインツールをインストールすると、メソッドやプロパティを調べることができる。[Class-dumpの配布ページ](http://stevenygard.com/projects/class-dump/)からファイルをダウンロードし、中にある`class-dump`を`/usr/local/bin`あたりへコピーする。

```bash
$ cp /Volumes/class-dump-3.5/class-dump /usr/local/bin/class-dump
```

ターミナルで次のコマンドを叩けば、プロパディやメソッドを取得できる。

```bash
# 含まれるフレームワークすべて
$ class-dump /Applications/Sketch.app

# フレームワークを指定
$ class-dump /Applications/Sketch.app/Contents/Frameworks/CocoaScript.framework

# ファイルとしてホームディレクトリへ書き出す場合
$ class-dump /Applications/Sketch.app > ~/sketch-class-dump.txt
```

参考記事でも紹介されていた[Sketch Headers](https://github.com/abynim/Sketch-Headers)で、同様の情報が閲覧できますので、お好きな方で。

### オブジェクトの構成を出力する`treeAsDictionary()`

- [[sketchplugins-dev] A Sketch scripting tip for the weekend](http://mail.sketchplugins.com/pipermail/dev_sketchplugins.com/2014-May/000342.html)

オブジェクトの構造は`treeAsDictionary()`を使うとログに吐き出すことができ、プロパティ名からメソッドを憶測できる。

```
// 選択しているシェイプの構造をログで出力
var sel = context.selection;
var shape = sel[0];
log( shape.frame().treeAsDictionary() );
```

これを実行すると、次の出力を得ることができる。

```js
{
    "<class>" = MSRect;
    constrainProportions = 0;
    height = 159;
    width = 145;
    x = 179;
    y = 169;
}
```

### クラスやインスタンスが持つメソッドを出力する`instanceMethods()`と`classMethods()`

また、class-dumpを紹介したものの、`instanceMethods()`や`classMethods()`を使うと、そのクラスが持っているメソッドを吐き出すこともできる。

```js
// 選択しているシェイプの構造をログで出力
var sel = context.selection;
var shape = sel[0];
log( shape.frame().class().mocha().instanceMethods() );
```

これを実行すると、次の出力を得ることができる（

```
(
    "<MOMethodDescription: 0x6100004266c0 : selector=makeOriginIntegral, typeEncoding=v16@0:8>",
    "<MOMethodDescription: 0x610000426700 : selector=makeRectIntegral, typeEncoding=v16@0:8>",
// 長いため中略
    "<MOMethodDescription: 0x610000428260 : selector=setOrigin:, typeEncoding=v32@0:8{CGPoint=dd}16>",
    "<MOMethodDescription: 0x610000428300 : selector=setRect:, typeEncoding=v48@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16>"
)
```

ちなみに`classMethods()`のものは`new()`しなくても使える。

### プロパティを出力する`properties()`

```js
// プロパティのダンプ
var sel = context.selection;
var shape = sel[0];
log( shape.frame().class().mocha().properties() );
```

### Atom向けのプラグイン実行パッケージ

Atomから直接Sketchプラグインを実行できるパッケージ。パッケージマネージャなどでインストールし`Command + R`すると、定義されてているメソッドをパレットから選択して実行できる。

[Sketch Scripter](https://github.com/timuric/sketchapp-scripter)

ちなみにAutherは[Content Generator Plugin](https://github.com/timuric/Content-generator-sketch-plugin)の人。

## プラグインの構造

```
PLUGINNAME.sketchplugin
 └ Contents
    └ Sketch
       ├ SCRIPT.sketchscript
       └ manifest.json
```

Pluginsメニューへの表示は`manifest.json`で管理するため、`SCRIPT.sketchscript`は複数あっても良い。拡張子も`.js`や`.cocoascript`でも大丈夫。ライブラリなどはSketchディレクトリ内にさらにディレクトリを作成し、その中に突っ込んでもOK。外部のスクリプトを読み込むには、`@import`を使ってファイルの先頭へ次のように書く。

```js
@import 'lib/script.js';
```

### 旧形式のプラグインについて

以前はディレクトリへ実行ファイルのそのものである`.sketchplugin`を突っ込んでおけば良かったが、3.3から形式が変更になっている。3.4からPreferencesへプラグインマネージャーが実装され、`manifest.json`がないと「Legacy Plugins」として1つにまとめられてプラグインを管理できなくなるので注意。

この旧形式でも41現在、きちんとメンテナンスされていれば実行は可能だが、Legacy PluginはPluginsメニューの別メニューにまとめられてしまう。今後、いつ使えなくなるかわからないため、よく使うプラグインが旧形式なら要注意。

こちらを参照：[Plugin News — Sketch Developer](http://developer.sketchapp.com/blog/2016-05-12-plugin-news/)

### manifest.json

manifest.jsonは、Pluginsメニューやプラグインマネージャーで表示する内容を設定するファイル。下記は、拙作「Sketch CSS Sprite Mixin」のファイルの一部を抽出。こちらはがんばって書いている感じなので、最低限の記述は「[Sketch3のプラグインを開発する](http://qiita.com/noppefoxwolf/items/f161bf1f583259206130)」を参照のこと。

```manifest.json
{
    "author": "littlebusters", // プラグインマネージャーで表示
    "menu" : {
        "isRoot" : false, // Pluginsメニュー直下へ表示するかどうか
        "shortcut" : "",
        "items" : [ // メニュー上の並びを設定。commands[INDEX].identifierを指定する。3.3では無効
            "css-sprite-mixin-scss",
            "css-sprite-mixin-sass",
            "css-sprite-mixin-less",
            "-", // 3.4.1より、ハイフンを入れるとセパレータとして表示されるように
            "css-sprite-mixin-stylus"
        ],
        "title" : "Sketch CSS Sprite Mixin" // Pluginsメニューでのフォルダ名
    },
    "identifier": "net.creative-tweet.css-sprite-mixin",
    "version": "1.1.0", // プラグインマネージャーで表示
    "description" : "Generate a code of CSS Sprite Mixin to Clipboard in Sketch.", // プラグインマネージャーで表示
    "name" : "Sketch CSS Sprite Mixin", // プラグインマネージャー上の名前。指定しない場合は、ファイル名から取得される
    "commands" : [
        {
            "name": "Scss", // Pluginsメニュー上の表示
            "identifier": "css-sprite-mixin-scss",
            "handler" : "getScss", // 下のscript内にあるメソッドを指定
            "script": "css-sprite-mixin.sketchscript" // スクリプトファイルを指定
        }
    ]
}
```

## Global Variable

メソッドに`context`という引数を渡して実行してやる。旧形式でいう`doc`や選択しているレイヤーを取得するには次の通り。

```js
var onRun = function(context) {
    var doc = context.document;
    var sel = context.selection;
}
```

## サンプルコード

`doc`となっているところは、事前に`var doc = context.document;`となっている前提とするか、`context.document`と読み替えてください。

### 非推奨になっているmethod

- MSArray.length() -> MSArray.count() // 3.8
- setPatternImage() -> SetImage() // 3.8
- addLayerOfType() -> それぞれのクラスからインスタンスを作成する // 3.8
- convertToSymbol() -> convertArtboardToSymbol() // 41
- MSColor -> MSImmutableColor // 41

### バージョンの取り方

Bohemian Coding的には最新版しかサポートしない感じですが、とりあえずバージョンによる分岐をする場合に必要なバージョンの取得方法。ちなみに、3.8の次は3.9かと思ったら39になってしまったので、39以降は`major`には二桁の数字が入ります。

```js
var appVer = {}
appVer.full = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
appVer.major    = ( appVer.full.split( '.' ) )[0];
appVer.minor    = ( appVer.full.split( '.' ) )[1] || 0;
appVer.revision = ( appVer.full.split( '.' ) )[2] || 0;
```

### ページ関連

**参考**
- [Sketch Command/Pages](https://github.com/bomberstudios/sketch-commands/tree/master/Pages)

#### 現在のページを取得

```js
// JS Style
var doc = context.document;
var currentPage = doc.currentPage();

// Obj-c style
var doc = context.document;
var currentPage = [doc currentPage];
```

#### ドキュメント内のページ数

```js
// JS Style
var doc = context.document;
var docPages = doc.pages();
log( docPages.count() ); // ページ数を表示

// Obj-c style
var doc = context.document;
var docPages = [doc pages];
log( [docPages count] ); // ページ数を表示
```
#### 新しいページを追加

```js
// JS Style
var doc = context.document;
doc.addBlankPage();

// Obj-c style
var doc = context.document;
[doc addBlankPage];
```

#### 現在のページ名を取得

```js
// JS Style
var doc = context.document;
var currentPage = doc.currentPage();
log( currentPage.name() );

// Obj-c style
var doc = context.document;
var currentPage = [doc currentPage];
log( [currentPage name] );
```

#### 現在のページ名を変更

```js
// JS Style
var doc = context.document;
var currentPage = doc.currentPage();
currentPage.setName( 'Page Name' );

// Obj-c style
var doc = context.document;
var currentPage = doc.currentPage();
[[currentPage] setName:'Page Name'];
```

### アートボード関連

#### アートボードの一覧を取得

```js
// JS Style
var doc = context.document;
var artboards = doc.currentPage().artboards();
if ( 0 < artboards.count() ) {
    for ( var i = 0; i < artboards.count(); i++ ) {
        log( artboards[i].name() );
        log( artboards[i].frame().width() );
        log( artboards[i].frame().height() );
        log( artboards[i].frame().x() );
        log( artboards[i].frame().y() );
    }
}

// Obj-c style
var doc = context.document;
var artboards = [[doc currentPage] artboards];
if ( 0 < [artboards count] ) {
    for ( var i = 0; i < [artboards count]; i++ ) {
        log( [[artboards objectAtIndex:i] name] );
        log( [[[artboards objectAtIndex:i] frame] width] );
        log( [[[artboards objectAtIndex:i] frame] height] );
        log( [[[artboards objectAtIndex:i] frame] x] );
        log( [[[artboards objectAtIndex:i] frame] y] );
    }
}
```

#### 現在のアートボードを取得

```js
// JS Style
var doc = context.document;
var currentArtboard = doc.currentPage().currentArtboard();
if ( 0 > currentArtboard.count() ) {
    log( currentArtboard.name() );
}

// Obj-c style
var doc = context.document;
var artboard = [[doc currentPage] currentArtboard];
if ( 0 < [artboards count] ) {
    log( [currentArtboard name] );
}
```

#### アートボードを作成

```js
// JS Style
var doc = context.document;
var artboard = MSArtboardGroup.new();
artboard.setName( 'Artboard Name' );
artboard.frame().setX( 0 );
artboard.frame().setY( 0 );
artboard.frame().setWidth( 480 );
artboard.frame().setHeight( 720 );
doc.addLayer( artboard );

// Obj-c style
var doc = context.document;
var artboard = [MSArtboardGroup new];
[artboard setName: 'Artboard Name'];
[[artboard frame] setX: 0];
[[artboard frame] setY: 0];
[[artboard frame] setWidth: 480];
[[artboard frame] setHeight: 720];
[doc addLayer: artboard];
```

複数のアートボードを追加する場合は、`doc.addLayer()`の部分を次のように変更する。

```js
// JS Style
var currentPage = doc.currentPage();
currentPage.addLayers( NSArray.arrayWithObjects( artboard ) );

// Obj-c Style
var currentPage = [doc currentPage];
[currentPage addLayers:[NSArray arrayWithObjects: artboard]];
```

#### 現在のアートボードへガイドラインを追加する

```js
var doc = context.document;
var artboard = doc.currentPage().currentArtboard();

var vRuler = artboard.verticalRulerData();
var hRuler = artboard.horizontalRulerData();
vRuler.addGuideWithValue( 100 ); // 垂直
hRuler.addGuideWithValue( 100 ); // 水平
```

#### 現在のアートボードのグリッドを設定する

```js
var doc = context.document;
var artboard = doc.currentPage().currentArtboard();

var grid = MSSimpleGrid.new();
grid.setGridSize( 16 );
artboard.grid = grid;
```

### レイヤーの作成と設定

#### グループの作成

```js
var doc = context.document;
var artboard = doc.currentPage().currentArtboard();

var group = MSLayerGroup.new();
group.setName( 'Group Name' );
artboard.addLayers( [group] );
```

#### 矩形（Rectangle）を作成

```js
var doc = context.document;
var artboard = doc.currentPage().currentArtboard();

var rectangle = MSRectangleShape.new();
rectangle.name = 'Layer Name';
rectangle.frame().x = 0;
rectangle.frame().y = 0;
rectangle.frame().width = 100;
rectangle.frame().height = 150;

var shape = MSShapeGroup.shapeWithPath( rectangle );
artboard.addLayers( [shape] );
```

#### ベジェを作成

**参考**
- [Sketch Plugins Cookbook](https://github.com/turbobabr/Sketch-Plugins-Cookbook#user-content-create-line-shape)

```js
var doc = context.document;
var artboard = doc.currentPage().currentArtboard();

var bezierPath = NSBezierPath.bezierPath();

bezierPath.moveToPoint( NSMakePoint( 0, 0 ) ); // パスを開始する座標

// 直線を引く
bezierPath.lineToPoint( NSMakePoint( 50, 50 ) ); // アンカーポイントの座標

// 曲線を引く
var ancher        = NSMakePoint( 200, 200 ); // アンカーポイントの座標
var currentHandle = NSMakePoint( 150, 200 ); // 作成するアンカーポイントのハンドルの座標
var prevHandle    = NSMakePoint(  50, 100 ); // 元のアンカーポイントのハンドルの座標
// 曲線を生成
[bezierPath curveToPoint:ancher controlPoint1:prevHandle controlPoint2:currentHandle];

// パスを閉じる
bezierLine.closePath();

var shape = MSShapeGroup.shapeWithBezierPath( bezierPath );
artboard.addLayers( [shape] ); // ベジェシェイプをアートボードへ追加
```

#### マスクの作成

```js
var doc = context.document;
var artboard = doc.currentPage().currentArtboard();

// マスクの影響を抑える為にグループを作成
var group = MSLayerGroup.new();
group.setName( 'Mask Group' );

// グループ内にマスクの矩形を作成

var maskRect = MSRectangleShape.new();
maskRect.name = 'Mask';
maskRect.frame().x = 0;
maskRect.frame().y = 0;
maskRect.frame().width = 100;
maskRect.frame().height = 150;

var mask = MSShapeGroup.shapeWithPath( maskRect );
group.addLayers( [mask] );

// マスク化
mask.setHasClippingMask( true );
// マスク化による親レイヤーの境界や座標を更新
```

#### ビットマップを読み込む

```js
var doc = context.document;
var artboard = doc.currentPage().currentArtboard();

// 読み込むファイルのパスを設定
var filePath = '/Users/USERNAME/file/to/path.png';

var image = NSImage.new().initWithContentsOfFile( filePath );
var imageData = MSImageData.alloc().initWithImage_convertColorSpace_(image, true);
var bitmap = MSBitmapLayer.new();
bitmap.image = imageData;
bitmap.name = 'Imported Bitmap';
bitmap.frame().x = 0;
bitmap.frame().y = 0;

artboard.addLayers( [bitmap] );
```

#### スライスを作成

```js
var doc = context.document;
var artboard = doc.currentPage().currentArtboard();

var slice = MSSliceLayer.new();
slice.name = 'Slice Name';
slice.frame().x = 0;
slice.frame().y = 0;
slice.frame().width = 100;
slice.frame().height = 150;

artboard.addSlice( slice );
```

#### Radiusの設定

```js
// Rectangleツールで作成した矩形を選択した状態で
var sel = context.selection;
var rect = sel.firstObject().layers().firstObject();

rect.cornerRadiusFloat = 10;

// 個別にRadiusを設定する場合は次の通り
// rect.cornerRadiusString = "7/0/0/10";
```

#### Blending Modeの設定

```js
// 矩形レイヤー（rectangle）を作成したのち
var contextSettings = rectangle.style().contextSettings();
contextSettings.blendMode = 1;
```

Blend Modeの数値は、こちらを参照のこと

- [MSGraphicsContextSettings — Sketch Developer](http://developer.sketchapp.com/reference/MSGraphicsContextSettings/)

#### レイヤー自体のOpacity設定

```js
// Blending Modeの設定のコードの後に
contextSettings.opacity = 0.5; // 0から1の値
```

### スタイリング

#### Fill（Flat Color）を設定

```js
// 矩形レイヤー（rectangle）を作成したのち
// Fillを追加してプロパティを取得
var fill = rectangle.style().addStylePartOfType( 0 );
// Fillに適用するカラーを設定
var color = MSImmutableColor.colorWithSVGString( '#ff0000' );
color.alpha = 0.8;
// Fillにカラーを適用
fill.color = color;
```

`addStylePartOfType()`した場合、デフォルトとしてFlat Colorが適用される。Gradientから変更する場合は、`fill.setFillType( 0 )`とする。

#### Fill（Gradient）を設定

```js
// 矩形レイヤー（rectangle）を作成したのち
// Fillを追加してプロパティを取得
var fill = rectangle.style().addStylePartOfType( 0 );

// Fillの種類をGradientに変更
fill.setFillType( 1 );
// Gradientプロパティを取得
var gradient = fill.gradient();
// Gradient Typeの設定 / 0: Linear / 1: Radial / 2: Angular
gradient.setGradientType( 0 );
// Gradient Stopを追加 / value: 0..1
gradient.addStopAtLength( 0.5 );
// Gradient Stopに適用するカラーと透明度を設定
var gradientColor = MSImmutableColor.colorWithSVGString( '#ff0000' );
gradientColor.alpha = 0.8;
// Gradient Stopにカラーを適用
gradient.setColor_atIndex_( gradientColor, 1 );
// Gradient Stopの位置を設定
gradient.setPoint_atIndex_( CGPointMake( 1, 1 ), 1 );
```

Gradient Stopの位置を設定する`CGPointMake()`は、レイヤーの大きさに対する割合をX・Yで指定する。
例えばW100×H100のレイヤーで、`CGPointMake( 0.4, 0.5 )`と指定すれば、オブジェクトの左上起点でX40/Y50の位置へGradient Stopが移動する。ただし、`atIndex`を両端以外で指定している場合、両端を結ぶ直線上の範囲で移動が行われる。

#### Fill（Pattern）を設定

```js
// 矩形レイヤー（rectangle）を作成したのち
// Fillを追加してプロパティを取得
var fill = rectangle.style().addStylePartOfType( 0 );

// Fillの種類をPatternに変更
fill.setFillType( 4 );
// 整列方法を設定 / 0: Tile / Fill: 1
fill.setPatternFillType( 1 );

// パターン画像のパスから画像データを作ってFillへ設定する
var imageData = MSImageData.alloc().initWithImage_convertColorSpace_( '/file/to/path/document.png', false );
fill.setImage( imageData );
```

#### Fill（Noise）を設定

```js
// 矩形レイヤー（rectangle）を作成したものとする
// Fillを追加してプロパティを取得
var fill = rectangle.style().addStylePartOfType( 0 );

// Fillの種類をNoiseに変更
fill.fillType = 5; // fill.setFillType( 5 )でもOK
// ノイズの種類を指定 / 0: Original / 1: Black / 2: White / 3: Color
fill.noiseIndex = 3;
// IntensityはOpacityとして設定する
fill.contextSettings().opacity = 0.5;
```

#### ドロップシャドウ / インナーシャドウ

```js
// 矩形 rectangle を作成済みとして
// 2: ドロップシャドウ / 3: インナーシャドウ
var effect = addStylePart( rectangle, 2 );

// ぼかしの大きさ
effect.blurRadius = 10;

// XY座標
effect.offsetX = 0;
effect.offsetY = 0;

// スプレッドの大きさ
effect.spread = 0;

// 色と不透明度
var color = MSImmutableColor.colorWithSVGString( getHexColor( attrs[i].ShadowColor ) );
color.alpha = getOpacity( attrs[i].ShadowColor );
effect.color = color;
```

#### ぼかし各種

```js
// 矩形レイヤー（rectangle）を作成したものとする
var effect = rectangle.style().blur();

// ぼかし効果を有効化
effect.isEnabled = 1;

// 0: ガウス / 1: モーション / 2: ズーム / 3: バックグラウンド（多分）
effect.type = 2;

// ぼかしの大きさ
effect.radius = 10;
```

### テキストレイヤーの作成

```js
var doc = context.document;
var artboard = doc.currentPage().currentArtboard();

var text = MSTextLayer.new();

// 0: Auto（デフォルト） / 1: テキストボックスの幅を固定
text.textBehaviour = 1;

// テキストに文字列を入れる
text.stringValue = 'テキストの内容だよ';

// フォントサイズの設定
text.fontSize = '24';

// フォントの設定（Postscript名で指定する）
text.fontPostscriptName = 'フォント名';

// テキストカラーの設定
text.textColor = MSImmutableColor.colorWithSVGString( '#ff0000' );

// テキストボックスの大きさを最適化
text.adjustFrameToFit();

// パラグラフを設定する
var pragraph = NSMutableParagraphStyle.new();
pragraph.minimumLineHeight = 12;
pragraph.maximumLineHeight = 24;
text.addAttribute_value_( NSParagraphStyleAttributeName, pragraph );

// テキストの揃え - 0: 左 / 1: 右 / 2: 中央
text.setTextAlignment( 0 );

// サイズや位置、レイヤー名なども設定できます
artboard.addLayers( [text] );
```

### 文字の一部の設定を変更する

```js
// 変更する範囲を作成（0文字から2文字目まで）
var range = NSMakeRange( 0, 2 );

// フォント名をサイズを指定
var textFont = NSFont.fontWithName_size_( 'フォント名（Postscript）', 24 );

// 色を指定
var color = MSImmutableColor.colorWithSVGString( '#ff0000' );

// 一部の変更を実行
text.setIsEditingText( true );
text.addAttribute_value_forRange_( NSFontAttributeName, textFont, range );
text.addAttribute_value_forRange_( NSForegroundColorAttributeName, color, range );
text.setIsEditingText( false );

// パラグラフも設定できますが、多分一部だけを変えることはないでしょうね
text.addAttribute_value_forRange_( NSParagraphStyleAttributeName, pragraph, range );
```

### Symbol

#### アートボードからシンボルを作成する

あらかじめSymbolにするアートボードを作成しておくこと。

```js
MSSymbolMaster.convertArtboardToSymbol( artboard );
// artboard = あらかじめ作成していたアートボード
```

ただこれではインスタンスを作成しづらいので、オブジェクトに放り込んでおくと良い…かも？

```js
var symbols['symbolName'] = MSSymbolMaster.convertArtboardToSymbol( artboard );
```

#### Symbol Instanceの作成

```js
var doc = context.document;
var cp  = doc.currentPage();
for( var i = 0; i < doc.pages().count(); i++ ) {
    if( 'Symbols' == doc.pages()[i].name() ) {
        var symbolsPage = doc.pages()[i];
        break;
    }
}

var symbolInstance = symbolsPage.artboards()[0].newSymbolInstance();
symbolInstance.setName( 'Symbol Instance' );
symbolInstance.frame().x = 0;
symbolInstance.frame().y = 100;
cp.addLayers( [symbolInstance] );
```

### メッセージ・ダイアログ関連

**参考**
- [User Input & Feedback](http://bohemiancoding.com/sketch/support/developer/02-common-tasks/05.html)

#### ウィンドウ下部へメッセージ

```js
// JS Style
doc.showMessage( 'Message' );

// Obj-c style
[doc showMessage: 'Meaasage'];
```

#### Cocoaでモーダルダイアログ

アイコンとかも設定できるはず。

```js
// JS Style
var userInput = COSAlertWindow.new();

//ダイアログタイトル
userInput.setMessageText( 'Dialog Title' );

// テキストフィールド
userInput.addTextLabelWithValue( 'Text Field Title' );
userInput.addTextFieldWithValue( 'Text Field Placeholder' );

// チェックボックス
var checkbox = NSButton.alloc().initWithFrame( NSMakeRect( 0, 0, 300, 18 ) );
checkbox.setButtonType( NSSwitchButton );
checkbox.setTitle( 'Switch Button Title' );
checkbox.setTag( 'value' );
checkbox.setState( false );
userInput.addAccessoryView( checkbox );

// ラジオボタン
var radiobutton = NSButton.alloc().initWithFrame( NSMakeRect( 0, 0, 300, 18 ) );
radiobutton.setButtonType( NSRadioButton );
radiobutton.setTitle( 'Radio Button Title' );
radiobutton.setTag( 'value' );
radiobutton.setState( true );
userInput.addAccessoryView( radiobutton );

// ボタン
userInput.addButtonWithTitle('OK');

// 実行
userInput.runModal();


// Obj-c style
var userInput = [COSAlertWindow new];

//ダイアログタイトル
[userInput setMessageText:'Dialog Title'];

// テキストフィールド
[userInput addTextLabelWithValue:'Text Field Title'];
[userInput addTextFieldWithValue:'Text Field Placeholder'];

// チェックボックス
var checkbox = [[NSButton alloc] initWithFrame:NSMakeRect( 0, 0, 300, 18 )];
[checkbox setButtonType:NSSwitchButton];
[checkbox setTitle: 'Switch Button Title'];
[checkbox setTag: 'value'];
[checkbox setState: false];
[userInput addAccessoryView: checkbox];

// ラジオボタン
var radiobutton = [[NSButton alloc] initWithFrame:NSMakeRect( 0, 0, 300, 18 )];
[radiobutton setButtonType: NSRadioButton];
[radiobutton setTitle: 'Radio Button Title'];
[radiobutton setTag: 'value'];
[radiobutton setState: true];
[userInput addAccessoryView: radiobutton];

// ボタン
[userInput addButtonWithTitle: 'OK'];

// 実行
[userInput runModal];
```

setButtonTypeは[このあたりを参照](https://developer.apple.com/library/prerelease/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSButtonCell_Class/index.html#//apple_ref/swift/enum/NSButtonType)

#### Fw.jsf でいう、prompot();

```js
// JS Style
var userInput = doc.askForUserInput_initialValue( 'Message', 'Default Value' );

// Obj-c style
var position = [doc askForUserInput: 'Message' initialValue: 'Default Value'];
```

JavaScript スタイルの書き方が分からなかったが、「_」でつなぐと良いことを教わった。

#### JSONファイルを選択してパース

ただし、`runModalForDirectory`が10.6で非推奨になっているので、他の方法を探す（10.10でも一応動作可能）。

```js
var openPanel = NSOpenPanel.openPanel();
    openPanel.setTitle( "Choose a JSON File" ); // ダイアログのタイトル
    openPanel.setCanCreateDirectories = false; // ディレクトリの作成を許可するか
    openPanel.setCanChooseFiles = true; // ファイル選択を許可するか

var fileTypes = ['json']; // 選択できるファイルタイプを設定
var openPanelButtonPressed = openPanel.runModalForDirectory_file_types_( nil, nil, fileTypes );

if ( openPanelButtonPressed == NSFileHandlingPanelOKButton ) {
    var filePath = openPanel.URL().path();
    var json = JSON.parse( NSString.stringWithContentsOfFile( filePath ) ); 
    log( 'Open File from: ' + filePath );
} else {
    return false;
}
```

### そのほか

#### フォントの一覧を取得

```
var fontList = NSFontManager.sharedFontManager().availableFontFamilies();
```

#### フォントファミリーを取得

```js
var fontFamilies = NSFontManager.sharedFontManager().availableMembersOfFontFamily('Helvetica Neue');
```

引数として、フォント名を渡す。PostScript名はNGで、「フォントの一覧」で取得したフォント名を使うこと。

#### ArrayからJSONに

例えば上記の「フォントの一覧」から作成する場合

```js
var fontList = NSFontManager.sharedFontManager().availableFontFamilies();
var data = NSJSONSerialization.dataWithJSONObject_options_error_(fontList, 0, nil);
var json = NSString.alloc().initWithData_encoding_(data, 4);
```

## Plugins Directoryへの登録

オリジナルドキュメント：[Contributing](https://github.com/sketchplugins/plugin-directory/blob/master/CONTRIBUTING.md)

GitHubやウェブサイト等へアクセスし、ダウンロードできるように準備しておく。

1. [Sketch Plugins Directory](https://github.com/sketchplugins/plugin-directory)のリポジトリをForkし、ローカルへCloneする。
1. `plugins.json`を編集する。
    1. Object（`{}`の括り）をコピペする。
    1. それぞれの項目を英語で埋める。Google翻訳したような英語でも問題なさそう。  
    最低限、`description`（プラグインの説明）・`name`（リポジトリ名）・`title`（プラグイン名）・`owner`（リポジトリの所有者名）を埋める。  
    そのほかの情報として、`owner`（作者名 リポジトリ所有者名以外を使う場合）・`homepage`（リポジトリ以外でプラグインを配布している場合のリンク先）・`lastUpdated`（最終更新日）がある。
    1. 不安なら、適当なJSON linterを通しておく。
1. Terminal.appで、Cloneしたディレクトリへ移動し、`rake`コマンドを実行すると、`plugins.json`に合わせて、`README.md`が更新される。
1. リモートのリポジトリへプッシュし、元のリポジトリへPull Requestを送る。メッセージは何でもOK。