ganechi
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # フロントエンド再構成 目的:各サービスごとに独立して開発が行えるようなフロントエンド開発の基盤を整える [TOC] ## 現在のディレクトリ構成と問題点 ``` - src - components - items - organisms - templates ... ページ追加のたびに要修正 - pages ... 各ページに実装が集中 - utils - App.tsx - App.css ``` ## 再構成の方針① - Atomic Design > 参考ページ > - [Atomic Design](https://bradfrost.com/blog/post/atomic-web-design/) > - [React で実装する atomic design のコンポーネントごとの責務の分け方とコード規約](https://qiita.com/takano-h/items/8731d8e7413d7b1f6d7b) > - [10分で説明するAtomic Design と、弊チームで扱うためのカスタマイズについて](https://qiita.com/putan/items/ec312314698087fca5b2) ### Atomic Design とは ![Atomic Design](https://i.imgur.com/BuxoSjT.png) ![](https://i.imgur.com/vrEnf6Q.png) - 部品の組み合わせでUIを作成 - グループに分類し、それらの依存関係を一方向に(上図) - 自分より左のレイヤーのみに依存 - 修正したレイヤーより左のレイヤーには影響が出ない - **依存方向は、必ず守ること!!** > We’re not designing pages, we’re designing systems of components.—Stephen Hay ### Atomic Design のイイコト - コンポーネントごとにしっかり分かれていて、UIの機能の再利用性が高い - 多くの画面を少ないコードで実装 - 画面別ではなく、機能別にUI設計ができる - 複数人で並行実装ができる - 開発速度がUP! - 効率良くテストができる ### 各グループの役割 ※IT:ITエンジニアチーム内での共通認識 ### pages ![page](https://i.imgur.com/fZKYhAz.png) - templatesにデータ(from API?)を流し込み、ページを動かす - 1ページ1ファイル - IT:各プロジェクトが管理する単位 #### templates ![temp](https://i.imgur.com/XyBPFm0.png) - atoms,molecules,organismsを配置する - プレイスホルダー(コンテンツがない)だけのスケルトンの状態 - レイアウトを担保する - IT:全プロジェクトで共有 #### organisms ![org](https://i.imgur.com/rVN0cAP.png) - コンテンツが完結しているもの - atoms,molecules,organismsを組み合わせて構成 - 独立して機能し、他のページでも同じ目的で使える #### molecules ![mol](https://i.imgur.com/RwdznKP.png) - atomsを組み合わせた部品 - 最低限の意味をつけるもの - 文字を入れる+クリックする=検索する - 独立して存在はできない - 操作性を担保 ### atoms ![atom](https://i.imgur.com/jcDkHje.png) - HTMLのタグ単位(ex.label,input,button) - Button Atomは、`<button>`に対応 - UIは含むが、動作/ロジックは含まない - OK:Color,fonts,animations - NG:Atomの組み合わせ、他componentと依存関係 - 基本デザインを担保 ### 各グループの境界 - pages vs templates - 実際のデータに、依存する(pages)/依存しない(templates) - templates vs organisms - 配置を自由に、変えられる(templates)/変えられない(organisms) - organisms vs molecules - 組み合わせによって意味/意図が、変わる(molecules)/変わらない(organisms) - molecules vs atoms - 単独コンポーネント(atoms)/複数組み合わせコンポーネント(molecules) ## 再構成の方針② - Recoil > 参考ページ > - [Recoilで快適フロントエンド開発 - Nulab](https://nulab.com/ja/blog/nulab/recoil-example/) > - [Recoilで始めるお手軽フロントエンドDDD](https://zenn.dev/susiyaki/articles/95dea88e673e1f854130) > - [ReactのState管理を比較してみた](https://qiita.com/cheez921/items/7c5f82da375a5988a179) > - [2021年に活用していきたいReactの状態管理ライブラリRecoil](https://tech.aptpod.co.jp/entry/2020/12/19/100000) ### Recoil とは - Meta(元Facebook)が開発中の状態管理ライブラリ - ざっくり言うと、Reactの`useState`みたいな状態保持hookをコンポーネントをまたいで使える - propsのバケツリレーをせずに、コンポーネントの階層に関係なくstateを子コンポーネントに伝えることができる ### Recoil 用語集 - Atom - グローバルな状態変数`RecoilState`を生成 - `key`:アプリ全体でユニークな値、keyでatomを判別 - `default`:初期値 ```typescript= const counterState = atom<number>({ key: "counterState", default: 0 }); ``` - Selector - Atomで生成されたRecoilStateを、使いやす形に加工して取得するための関数 ```typescript= // /src/hooks/RecoilAtom.tsx   ※実際は、AtomFamilyを使う const tempFahrenheit = atom({ key: 'tempFahrenheit', default: 32, }); // /src/hooks/RecoilSelector.tsx const tempCelcius = selector({ key: 'tempCelcius', get: ({get}) => ((get(tempFahrenheit) - 32) * 5) / 9, set: ({set}, newValue) => set( tempFahrenheit, newValue instanceof DefaultValue ? newValue : (newValue * 9) / 5 + 32 ), }); ``` - AtomFamily - 動的にatomを作成してくれる関数 - 同じatomFamilyを用いて、同じ型を持つ別の値の読み取り・書き込みができる - いちいちatomからRecoilStateを生成する手間を解決 - RecoilStateを取得する際に、パラメータをatomFamilyに渡す ```typescript= // /src/hooks/RecoilAtom.tsx import React from "react"; import { useRecoilState, atomFamily } from "recoil"; const inputTextState = atomFamily<string, string>({ key: "inputTextState", default: "" }); // 各コンポーネントにて export const InputFamily = () => { // もしパラメータを同じものにすると変数名が違っても、同じ値になる const [userName, setUserName] = useRecoilState(inputTextState("userName")); const [password, setPassword] = useRecoilState(inputTextState("password")); return ( <> <input value={userName} onChange={(e) => setUserName(e.target.value)} /> <input value={password} onChange={(e) => setPassword(e.target.value)} /> <p>{userName}</p> <p>{password}</p> </> ); }; ``` - SelectorFamily - selectorに引数を渡して条件分岐をしたい時に使用 - idで指定したものを呼び出す - RecoilをsubscribeするHooks 1. `useRecoilState` 「値」と「更新するための関数」両方必要な場合 2. `useRecoilValue` 「値」だけ必要な場合 3. `useSetRecoilState` 「更新する関数」だけ必要な場合 ```typescript= // 1. useStateと同じ const [count, setCount] = useRecoilState(counterState); // 2. useStateからstateだけを取り出したもの const count = useRecoilValue(counterState); // 3. useStateからsetStateだけを取り出したもの const setCount = useSetRecoilState(counterState) ``` - useRecoilCallback - `snapshot`:Recoilの現在のStateを読み取れるオブジェクト - snapshotを用いて必要な時にだけStateを読み込み発火する - 不要な再レンダリングを防げる(`useCallback`と同様) ### Recoilによる状態管理のルール #### 1. atom key, selector keyを一元管理する - Recoilでは、keyをアプリ全体でユニークにする必要 - 将来的に状態の数が増えても、この制約が守られるように、RecoilKeys.tsxにenumを作成してkeyを管理 ```typescript= // RecoilKeys.ts export enum RecoilAtomKeys { TODO_STATE = 'todoState', NOTICE_STATE = 'noticeState' } export enum RecoilSelectorKeys { TODO_TODOS = 'Todo_todos', TODO_TODO_ITEM = 'Todo_todoItem', NOTICE_HAS_UNREAD_NOTICE = 'Notice_hasUnreadNotice' } ``` ```typescript= // todoState.ts import { atom } from 'recoil' import { RecoilAtomKeys, RecoilSelectorKeys } from './RecoilKeys' type TodoItem = { id: string label: string } type TodoState = { todos: TodoItem[] } const todoState = atom<TodoState>({ key: RecoilAtomKeys.TODO_STATE, default: { todos: [] } }) ``` #### 2. atomやselectorをそのままexportしない - 状態設計者の意図しない状態変更を防止 - 用途に応じたカスタムフックのみをexport - `useAddRows`:日報列を追加する - `useRows`:日報列を全て呼び出す - `useDeleteRow`:idを指定して日報列を削除する ```typescript= // todoState.tsx import { useRecoilCallback } from 'recoil' // Actions type TodoActions = { useAddTodoItem: () => (label: string) => void } export const todoActions: TodoActions = { // Todoを追加する useAddTodoItem: () => useRecoilCallback(({ set }) => (label: string) => { set(todoState, (prev) => { const newItem: TodoItem = { id: createNewId(), label } return { ...prev, todos: [...prev.todos, newItem] } }) }, [createNewId]), } // Selectors type TodoSelectors = { useTodos: () => TodoItem[] useTodoItem: (id: string) => TodoItem | undefined } const todosSelector = selector<TodoItem[]>({ key: RecoilSelectorKeys.TODO_TODOS, get: ({ get }) => get(todoState).todos }) const todoItemSelector = selectorFamily<TodoItem | undefined, string>({ key: RecoilSelectorKeys.TODO_TODO_ITEM, get: (id) => ({ get }) => { const todos = get(todoState).todos return todos.find((v) => v.id === id) } }) export const todoSelectors: TodoSelectors = { useTodos: () => useRecoilValue(todosSelector), useTodoItem: (id: string) => useRecoilValue(todoItemSelector(id)) } ``` ## 参考になりそうなGithubリポジトリ - https://github.com/tockri/recoil-sample-for-nulab-blog - styles/ - https://github.com/susiyaki/recoil-frontend-ddd-sample - src/hooks/ ## 再構成後のディレクトリ構成 - public - src - App.tsx - components - pages ... 現状のページ構成 - LoginPage.tsx - HomePage.tsx - ... - templates - AppBar.tsx - AdposSearchResult.tsx ... 答案検索結果の表形式表示 - - organisms - BacklogApiCertification.tsx - UplaodByCsv.tsx - AdposSheetDownload.tsx - molecules - Dialog.tsx - DragDrop.tsx - Pagination.tsx - atoms - Icons.tsx - Image.tsx - constants - Constants.tsx - ConstantsNippo.tsx - api ... APIを叩く部分をwrap,axios/fetchはここでのみ使うイメージ? - AdposApi.tsx ... ADPOS APIを叩く部分wrap - BacklogApi.tsx ... Backlog APIを叩く部分wrap - SlackApi.tsx ... Slack APIを叩く部分wrap - hooks ... utilsをまとめる - recoil ... Recoil State関連 - RecoilKeys.tsx ... RecoilのKey管理 - AdposState.tsx ... AdposのRecoilState管理 - BacklogState.tsx ... BacklogのRecoilState管理 - UseHandler.tsx - UseTracking.tsx - LocalStorage.tsx - FormatDate.tsx - styles ... 見た目の定義 - GlobalStylessty.tsx ... テーマ色の定義など全体に反映(今はGenericTemplateにある) - - routes ... path/pageの対応を定義 - Routes.tsx ... App.tsxからRoute部分を切り出す ## memo ### 命名法則:BCD Design - コンポーネント名に使用される単語の意味や性質を相対的に利用することで、 コンポーネントを Base Case Domain の3つの概念へ法則的に配置し、体系的に管理できるようにする分類手法 - https://qiita.com/misuken/items/19f9f603ab165e228fe1 - Atomic Design : 設計手法 - BCD Design : 分類手法 - Base - 基礎 (名詞) - 基礎的な機能(名詞)そのもの、事実上の “型” を表す単語のことを指します。 - Case - 状況 (動詞/形容詞/名詞) - 状況(動詞/形容詞)や状態(名詞)を表す単語のことを指します。 - Domain - 関心 (名詞) - 人(ロール)や物(名詞)など "関心の対象" を表す単語のことを指します。 ![bcd1](https://i.imgur.com/NgQmWCA.png) - コンポーネント名を突き詰めていくと、以下の4つのパターンに ![bcd2](https://i.imgur.com/vYVBfOy.png) ### importを絶対パスで指定したい - https://zenn.dev/nbr41to/articles/84f4a7a7c1c165af2ef7 - `src`を絶対パスのbaseUrlに指定 ### recoilを使っている某会社のめも(口外禁止) recoil使うコツ - ReactのComponentから読むselector/atomは1つまでにする - Component専用のselectorを用意するのは大いにあり。そのselectorでいろいろなselectorを読んだり、変形する。 - selectorの中にロジックの大部分をいれる - for文やforEachはNG。ループの中を別selectorにしてwaitForAllする - 並列化 - 再計算も最小化される - apiもselectorでラップするとよい - dataloaderを組み合わせるのも可 - apiを組み合わせたり変形するselectorもあってよい - snapshotを使うことに臆病にならない。subscriptionをあえてしたくないときはある - キャッシュの効きにくさ的に配列をパラメーターにするのはやめたほうがいい。user_ids[]みたいなのはNG。 [client_id, user_id]のようなタプルはOK - キャッシュが効くので同じselectorを同じパラメーターでなんども読むのはなんの問題もない - キャッシュがメモリ上にたまるのは注意が必要。キャッシュ戦略をいじるべき場合もある - データ更新系はrecoilを使わないほうがおそらくいい。データ更新後にrefreshをかける。(selectorでできなくはないけど・・だいぶ書き心地も保守性も悪い感じになる) ### めも(平野) - recoilででてくるinstance of DefalutValueとは - https://scrapbox.io/study-react/DefaultValue - 要はreset用のお約束的な記述 - [リンク先](https://github.com/tockri/recoil-sample-for-nulab-blog/blob/main/src/components/pokemon/pokemon-api.ts)の80行目でthenの中にasync使ってるのなぜ - https://teratail.com/questions/357415 - response.json()がPromiseを返すから - 復習用 - https://qiita.com/niusounds/items/37c1f9b021b62194e077 - https://qiita.com/jun1s/items/ecf6965398e00b246249 - https://medium.com/acompany/javascript%E3%81%AEasync-await%E3%82%92%E5%AE%8C%E5%85%A8%E3%81%AB%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B-6552337eb92 - 盲目な相手に極限までわかりやすく解説していたのであろうコメント - https://qiita.com/rana_kualu/items/e6c5c0e4f60b0d18799d#comment-97fdd4d668bcac29972e - awaitがだめな例 - https://okapies.hateblo.jp/entry/2020/12/13/154311 - 例えば非同期関数でresolveする予定の値を用いて後続の処理で使うとき、その処理が2つ以上だとawaitにしてしまうと並列処理ができない - 1.imageUrlを取ってくる - 2.取ってきたimageUrlをキャッシュする - 3.取ってきたimageUrlからimageを取ってくる - この場合はimageUrlが返す生のPromiseを用いて2.3.の処理をthenでつなぐようにすると2.3.が並列で実行される - Promise.allとか使えばよいではないか? - https://qiita.com/okazuki/items/2c17cd509f86c8b13a15 - ↑反論の記事。特にコメントで「小学生か」と罵られている - ユーザーエクスペリエンスもちゃんと考えたい - https://qiita.com/teradonburi/items/5b8f79d26e1b319ac44f - 具体的には以下の様なことを考えて処理速度の基準を設けたい ``` 0~16 ミリ秒: とても良い、ゲームとかでも60FPS(1フレームあたりの描画間隔が16ミリ秒)とかになっていますね 0~100 ミリ秒: ページ内動作としては許容範囲 100~300 ミリ秒: ページ内動作のレスポンスとしてはやや遅い 300~1000 ミリ秒: ページの読み込み単位であればスムーズな体験を提供している 1000 ミリ秒以上: 1秒を超えるとユーザは実行したタスクへの関心を失う 10,000 ミリ秒以上: ユーザーは不満を感じてタスクを中断し、そのまま戻ってこない恐れがあります ``` - カスタムフックではuseCallbackを使っていこうね、という話 - https://blog.uhy.ooo/entry/2021-02-23/usecallback-custom-hooks/ - useCallbackはコンポーネントが再レンダリングされたときも更新されない(useEffectみたいにとある変数が更新されたら更新するみたいな設定ができる)ため、例えばuseMemoを使うような重たいコンポーネント内で呼び出されてた際に毎回更新してしまうようなことが防げる - 簡単な言葉で言い直せば、結局のところ「返り値の関数はuseCallbackで囲んだほうがカスタムフックの汎用性が高くなるからそうしろ」ということです。 場合によってはそのuseCallbackが無駄になるかもしれませんが、観測できるかどうかも分からないオーバーヘッドよりは設計上の要請のほうを優先したいというのが筆者の考えです。(引用) - 要は、全く同じ関数なら「変更した」とプログラムに捉えられないようにしたほうが、余分な再レンダリングを防げるという話。 - 今後責任範囲を切り分けていく上で必要な話かも。 - 参考 - https://qiita.com/soarflat/items/b9d3d17b8ab1f5dbfed2 - Recoilもその辺最適化されているらしい - https://unblog.unreact.jp/blog/2n7qgaa-k - React18ではレンダリングが非同期化(現在LIMEはReact17) - https://reactjs.org/blog/2022/03/29/react-v18.html - https://www.infoq.com/jp/news/2022/04/react-18-concurrent-renderer/ - recoilもこのための布石? - https://www.kwbtblog.com/entry/2020/11/07/032250 - 画面が固まって操作できないみたいな状況に陥らない+レンダリングの中断もできる - ユーザーエクスペリエンス超向上 - ただ、まだ記事が少ない ### めも(大鐘) - Reactのtest - 本当はリファクタリング前にやるべきでしたね。。。 - Jestが、typescriptで一般的、ReactでもOKっぽい - https://zenn.dev/296u/articles/7175641f1c4492 - そのままLIMEのCI/CDを一気に進めちゃいたい - https://note.com/tabelog_frontend/n/n4b8bcb44294c ### TODO - [ ] API切り出し - [x] NippoAPI - [x] BacklogAPI - [ ] ADPOSAPI - [ ] style - [ ] component - [ ] 新規機能 - [ ] 答案検索結果集計の改良 - [ ] 日報、1日のメモを編集可能に - [ ] 問い合わせ・要望の状況表示

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully