From 2d9bc02a0ef3d67a801bb43e822d6a463ff2066c Mon Sep 17 00:00:00 2001 From: 安竹洋平 <61961825+yasutakeyohei@users.noreply.github.com> Date: Sat, 19 Oct 2024 16:48:51 +0900 Subject: build --- build/blog/2024/01/26/new-flat-blog/index.html | 10 +- .../docusaurus-admonition-heading-toc/index.html | 46 ++++---- .../31/sagawa-aiki-kouen-seibi-joukyou/index.html | 58 +++++----- .../03/23/aiki-kouen-opening-ceremony/index.html | 44 ++++---- build/blog/archive/index.html | 10 +- build/blog/atom.xml | 118 ++++++++++----------- build/blog/index.html | 16 +-- build/blog/rss.xml | 118 ++++++++++----------- build/blog/tags/docusaurus/index.html | 12 +-- build/blog/tags/index.html | 10 +- build/blog/tags/v-3-1/index.html | 10 +- .../index.html" | 10 +- .../index.html" | 12 +-- .../index.html" | 12 +-- .../blog/tags/\345\205\254\345\234\222/index.html" | 12 +-- .../index.html" | 12 +-- .../blog/tags/\346\212\200\350\241\223/index.html" | 10 +- 17 files changed, 260 insertions(+), 260 deletions(-) (limited to 'build/blog') diff --git a/build/blog/2024/01/26/new-flat-blog/index.html b/build/blog/2024/01/26/new-flat-blog/index.html index f92cee66..abe10a5b 100644 --- a/build/blog/2024/01/26/new-flat-blog/index.html +++ b/build/blog/2024/01/26/new-flat-blog/index.html @@ -2,7 +2,7 @@ - + 新ふらっとブログ立ち上げました | 東京都小平市議・安竹洋平のまとめ @@ -12,12 +12,12 @@ - - - + + + -
メインコンテンツまでスキップ

新ふらっとブログ立ち上げました

· 約1分
安竹 洋平
東京都小平市議会議員

今後の展開を考え、これまでの議会活動等のまとめをこのDocusaurusの仕組みに移行しています。

+

新ふらっとブログ立ち上げました

· 約1分
安竹 洋平
東京都小平市議会議員

今後の展開を考え、これまでの議会活動等のまとめをこのDocusaurusの仕組みに移行しています。

ふらっとブログもこちらに新しく立ち上げることにしました。

議員活動や、その裏で活用している技術について、誰かの役に立つかもしれないことを中心にブログにしていきます😁

diff --git a/build/blog/2024/01/27/docusaurus-admonition-heading-toc/index.html b/build/blog/2024/01/27/docusaurus-admonition-heading-toc/index.html index b4a31191..ae5e420a 100644 --- a/build/blog/2024/01/27/docusaurus-admonition-heading-toc/index.html +++ b/build/blog/2024/01/27/docusaurus-admonition-heading-toc/index.html @@ -2,7 +2,7 @@ - + Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法 | 東京都小平市議・安竹洋平のまとめ @@ -12,18 +12,18 @@ - - - + + + -

Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法

· 約17分
安竹 洋平
東京都小平市議会議員

Docusaurus🦖

+

Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法

· 約17分
安竹 洋平
東京都小平市議会議員

Docusaurus🦖

1か月弱使いましたがこのDocusaurus(ドキュサウルス)は数あるCMSの中でも秀逸です。

文書作成と管理が容易で、拡張の自由度も非常に高く、完全なオープンソース。

議員活動に重要な「資料を作成しまとめて公開するツール」として現状の最適解と感じます。

Facebookが母体なのでいろいろと気になるところですが、Reactを初めとして有益なソフトウェアを完全なオープンソースとして提供してくれていることは純粋にありがたいと感じます。

-

Admonitionのタイトルが見出しにならない

+

Admonitionのタイトルが見出しにならない

さてDocusaurusにはAdmonition(注意書きや警告文)を容易にMarkdownで書く方法が用意されています。

たとえばinfoなら、次のようにMarkdownで書けば、

Admonitionの記法(mdもしくはmdxに記載)
:::info[infoの例]

ここに文章を書く

:::
@@ -37,47 +37,47 @@

次のように本文中に見出しを書く方法もありますが

Admonitionの記法(mdもしくはmdxに記載)
:::info

#### テスト

~文章~

:::

見た目がイマイチになります。

-
備考

テスト

~文章~

+
備考

テスト

~文章~

ほかのユーザーからの要望も上がっており、私も少し不便に感じていたので、次の仕様になるようカスタマイズしましたのでその方法を解説します。

-

カスタマイズ後はどうなるか

+

カスタマイズ後はどうなるか

後述のカスタマイズをすると、Admonitionのタイトル部に(通常の見出しMarkdownと同様に)#を冒頭に2個以上入れる ことで見出しになります。またTOCにも反映されます。#を2個以上としているのは、H1をAdmonitionには使わないはずのため。#を入れない場合は見出しにならず、TOCにも反映されません。

-

タイトル冒頭に#を入れた場合

+

タイトル冒頭に#を入れた場合

Admonitionの記法(mdもしくはmdxに記載)
:::info[#### 見出しになりTOCに反映されるタイトルの例]

#が4つ分のためH4見出しになります。TOCにも反映されます。

:::

見出しになり、TOCに反映されるタイトルの例

#が4つ分のためH4見出しになります。TOCにも反映されます。

このブログでもTOCに表示されています。
なおマウスカーソルを乗せた際にハッシュリンク(#)が表示されるようにするにはCSSの設定が必要です。

-

タイトル冒頭に#を入れない場合

+

タイトル冒頭に#を入れない場合

Admonitionの記法(mdもしくはmdxに記載)
:::info[見出しにならずTOCに反映されないタイトルの例]

#がないため見出しにならず、TOCにも反映されません。

:::

見出しにならず、TOCに反映されないタイトルの例

#がないため見出しになりません。

-

タイトルにHTMLを入れることも可能

+

タイトルにHTMLを入れることも可能

なおタイトル部にHTMLを入れることもできます。TOCにも反映されます。

テスト123テスト下線


タイトル部のmarkdownは次の通りです。

#### テスト<sup>123</sup>テスト<u>下線</u>

-

カスタマイズに利用した機能

+

カスタマイズに利用した機能

カスタマイズは次の機能を活用しました。

-

RemarkとRehypeについて

+

RemarkとRehypeについて

RemarkとRehypeは、MarkdownをHTMLに変換するプロセスにおいて、AST(抽象構文木・Abstract Syntax Tree)に作用するプラグインです。なおASTを操作するオープンソースのエコシステムの中にはもう1つRetextというプラグインもありますが、Docusaurusには実装されていないようです。

markdownからHTMLへ変換処理の流れ
| ........................ process ........................... |
| .......... parse ... | ... run ... | ... stringify ..........|

+--------+ +----------+
Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output
+--------+ | +----------+
X
|
+--------------+
| Transformers |
+--------------+

上図(Unified Overviewより)にTransformersとあるところがRemark/Rehypeの動作するところ。

RemarkはMarkdown形式で、RehypeはHTML形式でASTを扱います。どちらも同じようにASTを操作できますが、データ構造が違うため、目的に応じて選択することになるのかなと思います。

こちらのサイトなどが詳しいです。

-

Docusaurusにおけるプラグインの実行タイミング

+

Docusaurusにおけるプラグインの実行タイミング

Docusaurusでこれらのプラグインを利用するためにはdocusaurus.config.jsonに設定が必要です。このページによると次の4種類の設定値にてプラグインを登録できます。

デフォルトプラグイン適用前デフォルトプラグイン適用後
Remark
Markdown形式
beforeDefaultRemarkPluginsremarkPlugins
Rehype
HTML形式
beforeDefaultRehypePluginsrehypePlugins

MarkdownからHTMLへの変換処理のところで、Docusaurusは自前のプラグイン(デフォルトプラグイン)を使い「見出しにidをつける」「ASTからTOCを作成する」などの処理を行っています。そのため今回のように「Amonitionのタイトルを読んでTOCに反映する」ためには、デフォルトプラグイン適用前と適用後の両方のタイミングでの処理が必要になります。

-

Swizzlingについて

+

Swizzlingについて

Swizzlingはこちらに説明があるとおりの機能で、簡単に言うとReactのコンポーネントをカスタマイズできる機能です。

Swizzlingの設定をすると、Docusaurusがデフォルトのコンポーネントの代わりに自動的にカスタマイズしたコンポーネントを使用します。

今回は、デフォルトのAdmonitionにないID属性を持たせるためAdmonitionコンポーネントをカスタマイズしました。Swizzlingの設定をすることにより、デフォルトのAdmonitionの代わりにこのカスタムコンポーネントが使われるようにします。

-

動作原理

+

動作原理

TOCは「ASTに含まれているheading要素を単純に配列に入れている」だけですが、この処理はカスタマイズで上書きできません。そこで、カスタマイズできる処理だけでAdmonitionのタイトルをTOCに反映する方法として次を思いつき、実装しました。

  1. docusaurusのデフォルトプラグインがTOCの処理を行うより前に、Admonitionのタイトル部を見出しとして新規作成し、Admonition要素の直前に追加する
  2. @@ -85,34 +85,34 @@
  3. デフォルトプラグインの処理が終了したら作成した見出しは不要になるので削除する。その際、削除する見出しと同じタイトルをもつAdmonition要素を探し、idを与える
  4. AdmonitionコンポーネントでidをHTMLタグに付与する
-

実装

-

docusaurus.config.json

+

実装

+

docusaurus.config.json

まずdocusaurus.config.jsonにimportとplugin設定を記入します(ハイライト部)。

これでDocusaurusデフォルトプラグイン適用の前後にそれぞれ自作のRemark/Rehypeプラグインが実行されることになります。

blogなどを入れている場合は、そのプロパティにも記載します。

docusaurus.config.json
import admonitionTitleToHeadingBeforeTOC from './src/remark/admonition-title-to-heading-before-toc.js';
import admonitionTitleToHeadingAfterTOC from './src/rehype/admonition-title-to-heading-after-toc.js';

export default {
// ...
presets: [
[
'classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
// ...
beforeDefaultRemarkPlugins: [admonitionTitleToHeadingBeforeTOC],
rehypePlugins: [admonitionTitleToHeadingAfterTOC],
},
blog: {
// ...
beforeDefaultRemarkPlugins: [admonitionTitleToHeadingBeforeTOC],
rehypePlugins: [admonitionTitleToHeadingAfterTOC],
},
// ...
}),
]],
// ...
}
-

Remark/Rehypeプラグイン

+

Remark/Rehypeプラグイン

次にプラグインを実装します。

docusaurusのsrcディレクトリ下にrehypeとremarkというディレクトリを作り、次のファイル名と内容で2つのプラグインを作ります。

src/rehype/admonition-title-to-heading-before-toc.js
import {visit} from 'unist-util-visit';

const plugin = (options) => {
const transformer = async (ast) => {
let newBeginningText = "";
const visitor = ((node, index, parent) => {
if (node.type === 'containerDirective') {
// :::infoなどに続くタイトル冒頭Text部(冒頭#を含む(もしくは含まない)部分)を取得(:::info ##** )
// (タイトル全体にはHTML等が含まれる可能性があるため冒頭Text部だけ操作する、残りはシャロ―コピー)
const beginningText = node.children[0].children[0].value;

// タイトル冒頭Text部に#が2つ以上連続しているとき
if(/^##/.test(beginningText)) {
// タイトル冒頭部から#とそれに続く空白を削除
newBeginningText = beginningText.replace(/^#+/, '').trim();

// タイトル部冒頭だけ更新し、残りはシャロ―コピー
// まずタイトル部全体をシャロ―コピー
let titleNodes = [...node.children[0].children];

// 冒頭要素のvalueを更新(ほかはシャロ―コピー)
const newTitleBeginningNode = {
...titleNodes[0],
value: newBeginningText,
}

// タイトルノードの冒頭要素だけ更新(ほかはシャロ―コピー)
const newTitleNodes = [ ...titleNodes ];
newTitleNodes[0] = newTitleBeginningNode;

// visitしているcontainerDirectiveの前にheadingノードを追加
parent.children.splice(index, 0, {
type: 'heading',
depth: (beginningText.match(/^##+/) || [''])[0].length, // #の連続数がheadingの深さ
children: newTitleNodes,
});
// 次に検索するのはindexを2つ分飛ばしたノード
return index + 2;
}
}
});

visit(ast, 'containerDirective', visitor);

};
return transformer;
};

export default plugin;
src/rehype/admonition-title-to-heading-after-toc.js
import {visit} from 'unist-util-visit';

const plugin = (options) => {
const transformer = async (ast) => {
let hId = null;
let hContent = null;
visit(ast, 'element', (node, index, parent) => {
if (/^h[2-6]$/.test(node.tagName) && node.properties && node.properties.id) {
// H要素(h2~h6)を見つけた場合

// IDとタイトルの冒頭Text部を取得する
hId = node.properties.id;
hContent = node.children ? node.children[0].value :
node.children[0].children[0] ? node.children[0].children[0].value : '';

// 続くAdmonitionを探す(docはH要素とadmonitionが連続しているが
// blogではなぜか改行要素{ type:'text', value:'\n' }が間に入っているので念のため隣接3要素を探す
for (let i = index + 1; i < index + 4 && i < parent.children.length; i++) {
if(parent.children[i] && parent.children[i].tagName === 'admonition') {
// admonition(div)を見つけた場合
const admonitionNode = parent.children[i];

// admonitionタイトルの冒頭Text部分を取得(properties.titleもしくはchildren[0].children[0].value)
const admonitionNodeTitle = admonitionNode.properties.title ? admonitionNode.properties.title :
admonitionNode.children[0] && admonitionNode.children[0].children[0] ? admonitionNode.children[0].children[0].value : '';

if(/^##/.test(admonitionNodeTitle) && admonitionNodeTitle.replace(/^#+/, '').trim() === hContent.trim()) {
// #で始まっていて、タイトル冒頭部が同じ場合
// divのidをHタグのidに設定
admonitionNode.properties.id = hId;
// H要素を削除
parent.children.splice(index, 1);
}
}
}
}
});
};
return transformer;
};

export default plugin;
Admonitionのツリー構造

参考までに、Remarkのプラグインから見るとAdmonitionのASTはたとえば次のようになっています。

Admonitionのツリー構造(一例)
{
type: 'containerDirective',
name: 'info',
attributes: {},
children: [
{
type: 'paragraph',
data: { directiveLabel: true },
children: [
{
type: 'text',
value: '#### info title もしHTML等が入ると(ここにaタグを入れると)',
position: [Object]
},
{
type: 'mdxJsxTextElement',
name: 'a',
attributes: [],
position: [Object],
data: [Object],
children: [Array]
},
{ type: 'text', value: 'このようにタイトル部が別々の要素として配列に入っている。', position: [Object] }
],
position: {
start: { line: 1347, column: 8, offset: 34053 },
end: { line: 1347, column: 55, offset: 34100 }
}
},
{ type: 'paragraph', children: [Array], position: [Object] },
...
],
...
}
-

Swizzling

+

Swizzling

次にSwizzlingです。

Docusaurusのsrc/themeディレクトリにAdmonitionというディレクトリを作り、次の一例のファイルを格納します。

なおここに説明がある通りSwizzlingにはEjectingとWrappingの方法があります。

WrappingではAdmonitionの内部までカスタマイズできないため「タイトル部分にidプロパティを付ける」といったことができません。そのためEjectingを使います。

注記

Ejectingを使う場合はバージョンアップによってデフォルトのコンポーネントと挙動が変わってくる可能性があるのでアップグレードの際は注意が必要です。

-

Ejecting

+

Ejecting

npm run swizzle @docusaurus/theme-classic Admonition -- --eject

Docusaurus V3.1では次のようなメッセージが出ますので、YESを選びます。

? Do you really want to swizzle this unsafe internal component? » - Use arrow-keys. Return to submit.
NO: cancel and stay safe
> YES: I know what I am doing!

Ejectingをすると、実質的にnode_modulesの@docusaurus/theme-classic/lib/themeにあるコンポーネントがsrc/themeディレクトリにコピーされます。

あとはコピーされたコンポーネントをいじるだけです。

-

コードの変更

+

コードの変更

Layoutファイルを次のように変更するのみです。

src/theme/Admonition/Layout/index.js
import React from 'react';
import clsx from 'clsx';
import {ThemeClassNames} from '@docusaurus/theme-common';
import styles from './styles.module.css';
import headingStyles from '@docusaurus/theme-classic/lib/theme/Heading/styles.module.css';

function AdmonitionContainer({type, className, children}) {
return (
<div
className={clsx(
ThemeClassNames.common.admonition,
ThemeClassNames.common.admonitionType(type),
styles.admonition,
className,
)}>
{children}
</div>
);
}
function AdmonitionHeading({icon, title, id}) {
let depth = 0;
let trimmedTitle = title;
// titleにHTML等が含まれている場合は文字列ではなく配列になる
if(typeof title === "string") {
// 文字列冒頭の#の数を数える(
depth = title.match ? (title.toString().match(/^#+/) || [''])[0].length : 0;
// #を省いたタイトルを得る
trimmedTitle = depth > 0 ? title.replace(/^#+/, '').trim() : title;
} else if (typeof title[0] === "string") {
depth = title[0].match ? (title[0].match(/^#+/) || [''])[0].length : 0;
trimmedTitle = depth > 0 ? [title[0].replace(/^#+/, '').trim(), ...title.slice(1)] : title;
}

// スクロール位置調整のcss
const classNames = clsx("anchor", "title", headingStyles.anchorWithStickyNavbar);
// depthに応じて見出しタグをレンダー
return (
<div className={styles.admonitionHeading}>
<span className={styles.admonitionIcon}>{icon}</span>
{(() => {
if (depth == 3) {
return(
<h3
id={id}
className={classNames}
>
{trimmedTitle}
</h3>
)
} else if (depth == 4) {
return(
<h4
id={id}
className={classNames}
>
{trimmedTitle}
</h4>
)
} else if (depth == 5) {
return(
<h5
id={id}
className={classNames}
>
{trimmedTitle}
</h5>
)
} else if (depth == 6) {
return(
<h6
id={id}
className={classNames}
>
{trimmedTitle}
</h6>
)
} else {
return(
<>
{trimmedTitle}
</>
)
}
})()}
</div>
);
}
function AdmonitionContent({children}) {
return children ? (
<div className={styles.admonitionContent}>{children}</div>
) : null;
}
export default function AdmonitionLayout(props) {
const {type, icon, title, children, className, id} = props;
return (
<AdmonitionContainer type={type} className={className}>
<AdmonitionHeading title={title} icon={icon} id={id} />
<AdmonitionContent>{children}</AdmonitionContent>
</AdmonitionContainer>
);
}

上記を設定後、npm start等の再起動が必要です。

-

以上です。

+

以上です。

\ No newline at end of file diff --git a/build/blog/2024/01/31/sagawa-aiki-kouen-seibi-joukyou/index.html b/build/blog/2024/01/31/sagawa-aiki-kouen-seibi-joukyou/index.html index 9acb6982..99f35bd5 100644 --- a/build/blog/2024/01/31/sagawa-aiki-kouen-seibi-joukyou/index.html +++ b/build/blog/2024/01/31/sagawa-aiki-kouen-seibi-joukyou/index.html @@ -2,7 +2,7 @@ - + 合気公園の整備進捗状況 | 東京都小平市議・安竹洋平のまとめ @@ -12,77 +12,77 @@ - - - + + + -

合気公園の整備進捗状況

· 約6分
安竹 洋平
東京都小平市議会議員

小平市上水南町二丁目の佐川幸義邸跡地に新設される合気公園の整備状況を見てきましたので、写真と動画で現在の様子をお知らせします。

+

合気公園の整備進捗状況

· 約6分
安竹 洋平
東京都小平市議会議員

小平市上水南町二丁目の佐川幸義邸跡地に新設される合気公園の整備状況を見てきましたので、写真と動画で現在の様子をお知らせします。

順調に進んでいるようですね😊

-

これまでの経緯

+

これまでの経緯

合気公園に関してこれまでの経緯はこちら↓にまとめています。

-

市民による公園整備案

+

市民による公園整備案

旧佐川邸の公園化を考える会が市に提出した公園計画案はこちら↓(ランドスケープデザイナー・鈴木綾氏による)

旧佐川邸の公園化を考える会が市に提出した合気公園計画案

-

市の整備イメージ案

+

市の整備イメージ案

上記の提案を受けて市が令和5年5月に提示した整備イメージ案がこちら↓

市民による計画案を受けて小平市水と緑と公園課が提示した合気公園整備イメージ案

この整備イメージに基づいて整備が進んでいます。

-

写真

+

写真

次の写真と動画は令和6年1月30日(火)に撮影したものです。

-

お知らせ

+

お知らせ

合気公園整備中・お知らせ

-

野外卓・顕彰碑・背なしベンチ

+

野外卓・顕彰碑・背なしベンチ

養生されている?道場跡の部分が見えます。

合気公園整備中・野外卓・顕彰碑・背なしベンチ

-

パーゴラ・顕彰碑

+

パーゴラ・顕彰碑

合気公園整備中・パーゴラ・顕彰碑

-

メッシュフェンス・ブロック

+

メッシュフェンス・ブロック

合気公園整備中・メッシュフェンス・ブロック

-

水飲み場・東側出入口

+

水飲み場・東側出入口

園名碑が少し写っています。トラックが止まっていて撮影できませんでした。

-

水飲み場・東側出入口

+

水飲み場・東側出入口

合気公園整備中・水飲み場・東側出入口

-

庭園付近

+

庭園付近

合気公園整備中・庭園付近

-

西側出入口への道・目隠しフェンス

+

西側出入口への道・目隠しフェンス

合気公園整備中・西側出入口への道・目隠しフェンス

-

南東の角

+

南東の角

道路が拡幅されていることが分かります。

合気公園整備中・南東の角

-

庭園

+

庭園

このあたりは今後手が入るのかもですね。

合気公園整備中・園庭

-

売却予定地

+

売却予定地

奥の砂が盛ってあるところが売却予定地です。

合気公園整備中・売却予定地(奥の砂が盛ってあるところ)

-

拡幅された東側道路

+

拡幅された東側道路

合気公園整備中・拡幅された東側道路

-

道路拡幅は売却予定地前まで

+

道路拡幅は売却予定地前まで

合気公園整備中・道路拡幅は売却予定地前まで

-

佐川氏が四股を踏んでいたとされる石

+

佐川氏が四股を踏んでいたとされる石

右下の平らな石が、佐川幸義氏が四股を踏んでいたとされる石です。

合気公園整備中・四股を踏んでいたと思われる石

-

西側出入口

+

西側出入口

案内板はまだ設置されていません。

合気公園整備中・西側出入口

-

西側園路と目隠しフェンス

+

西側園路と目隠しフェンス

合気公園整備中・西側園路と目隠しフェンス

-

西側の工事看板・目隠しフェンス上部

+

西側の工事看板・目隠しフェンス上部

目隠しフェンス上部は人の乗り越えられないような突起が出ています。

合気公園整備中・西側の工事看板・目隠しフェンス上部

-

西側園路の舗装

+

西側園路の舗装

合気公園整備中・西側園路の舗装

-

動画

+

動画

-

以上です。完成が楽しみですね。

+

以上です。完成が楽しみですね。

\ No newline at end of file diff --git a/build/blog/2024/03/23/aiki-kouen-opening-ceremony/index.html b/build/blog/2024/03/23/aiki-kouen-opening-ceremony/index.html index ccce42d6..15b75fe8 100644 --- a/build/blog/2024/03/23/aiki-kouen-opening-ceremony/index.html +++ b/build/blog/2024/03/23/aiki-kouen-opening-ceremony/index.html @@ -2,7 +2,7 @@ - + 小平市立合気公園・開園セレモニー | 東京都小平市議・安竹洋平のまとめ @@ -12,22 +12,22 @@ - - - + + + -

小平市立合気公園・開園セレモニー

· 約7分
安竹 洋平
東京都小平市議会議員

小平市上水南町二丁目の佐川幸義邸跡地に新設された小平市立合気公園がついに開園しました!

+

小平市立合気公園・開園セレモニー

· 約7分
安竹 洋平
東京都小平市議会議員

小平市上水南町二丁目の佐川幸義邸跡地に新設された小平市立合気公園がついに開園しました!

令和6年3月23日(土)午前10時から現地で行われた開園セレモニーに参加しましたので、写真と動画で様子をお知らせします。

-

これまでの経緯

+

これまでの経緯

合気公園に関してこれまでの経緯はこちら↓にまとめています。

-

セレモニーの様子

+

セレモニーの様子

次のような次第で、約30分間行われました。

  1. 開会の辞(水と緑と公園課長)
  2. @@ -53,19 +53,19 @@

    除幕直前

    除幕直後
    ↑市長、市議会議長、木村氏、岡江氏により、顕彰碑の除幕が行われました。

    -

    式次第(小平市)

    +

    式次第(小平市)

    -

    そのほかの写真

    +

    そのほかの写真

    次の写真と動画はセレモニー直後と3月28日(木)に撮影したものです。

    -

    出入口

    +

    出入口

    正面出入口
    正面出入口

    北東側出入口
    北東側出入口

    -

    園名碑

    +

    園名碑

    合気公園・園名碑
    道場に使われていた屋根瓦(鬼瓦)の一部が埋め込まれています。道場関係の方によるデザインです。

    -

    顕彰碑

    +

    顕彰碑

    合気公園・顕彰碑

    合気公園・顕彰碑詳細

    @@ -79,33 +79,33 @@ 円満に滞り無く動じて居るのである
    その調和が合氣なのである

    -

    日本庭園

    +

    日本庭園

    日本庭園(1)

    日本庭園(2)
    早くも子どもたちが石に上って遊んでいました😍

    -

    道場跡地横にあるベンチ

    +

    道場跡地横にあるベンチ

    道場跡地横のベンチ
    緑色に舗装された部分が道場跡地です。

    -

    テーブル

    +

    テーブル

    ベンチとテーブル

    -

    パーゴラとベンチ

    +

    パーゴラとベンチ

    パーゴラとベンチ

    -

    水飲み

    +

    水飲み

    水飲み

    -

    南西出入口への通路

    +

    南西出入口への通路

    南西出入口への通路

    -

    看板

    +

    看板

    公園の立て看板

    -

    売却予定地

    +

    売却予定地

    売却予定地
    佐川幸義氏のご遺族から土地約1,300㎡と現金約3,000万円をご遺贈いただき、さらにふるさと納税で小平市として過去最多の約1,600万円ものご寄附をいただきました。ありがとうございました。それでもなお整備費用として約2千万円が不足しているという理由から、小林洋子市政である小平市は1区画を売却するとしました。

    路線価は約21万円/㎡ですので、土地の評価額は約2.7億円となります。合計で優に3億円もの寄附をいただいたことになります。また市民と市が理想的な形での協働ができたり、ふるさと納税で歴代記録を更新している画期的な事業だったわけですから、最後は市に残りの整備費用を出してもらいたかったところです。残念ですがこの1区画は令和6年度中に売却予定です。

    -

    動画

    +

    動画

    Xに投稿した動画をご覧ください。

    -
+
\ No newline at end of file diff --git a/build/blog/archive/index.html b/build/blog/archive/index.html index b7d854eb..6ed3c6a3 100644 --- a/build/blog/archive/index.html +++ b/build/blog/archive/index.html @@ -2,7 +2,7 @@ - + アーカイブ | 東京都小平市議・安竹洋平のまとめ @@ -12,11 +12,11 @@ - - - + + + - + \ No newline at end of file diff --git a/build/blog/atom.xml b/build/blog/atom.xml index 8fcdb2cd..f3e7cf5b 100644 --- a/build/blog/atom.xml +++ b/build/blog/atom.xml @@ -16,14 +16,14 @@ 小平市上水南町二丁目の佐川幸義邸跡地に新設された小平市立合気公園がついに開園しました!

令和6年3月23日(土)午前10時から現地で行われた開園セレモニーに参加しましたので、写真と動画で様子をお知らせします。

-

これまでの経緯

+

これまでの経緯

合気公園に関してこれまでの経緯はこちら↓にまとめています。

-

セレモニーの様子

+

セレモニーの様子

次のような次第で、約30分間行われました。

  1. 開会の辞(水と緑と公園課長)
  2. @@ -49,19 +49,19 @@

    除幕直前

    除幕直後
    ↑市長、市議会議長、木村氏、岡江氏により、顕彰碑の除幕が行われました。

    -

    式次第(小平市)

    +

    式次第(小平市)

    -

    そのほかの写真

    +

    そのほかの写真

    次の写真と動画はセレモニー直後と3月28日(木)に撮影したものです。

    -

    出入口

    +

    出入口

    正面出入口
    正面出入口

    北東側出入口
    北東側出入口

    -

    園名碑

    +

    園名碑

    合気公園・園名碑
    道場に使われていた屋根瓦(鬼瓦)の一部が埋め込まれています。道場関係の方によるデザインです。

    -

    顕彰碑

    +

    顕彰碑

    合気公園・顕彰碑

    合気公園・顕彰碑詳細

    @@ -75,28 +75,28 @@ 円満に滞り無く動じて居るのである
    その調和が合氣なのである

    -

    日本庭園

    +

    日本庭園

    日本庭園(1)

    日本庭園(2)
    早くも子どもたちが石に上って遊んでいました😍

    -

    道場跡地横にあるベンチ

    +

    道場跡地横にあるベンチ

    道場跡地横のベンチ
    緑色に舗装された部分が道場跡地です。

    -

    テーブル

    +

    テーブル

    ベンチとテーブル

    -

    パーゴラとベンチ

    +

    パーゴラとベンチ

    パーゴラとベンチ

    -

    水飲み

    +

    水飲み

    水飲み

    -

    南西出入口への通路

    +

    南西出入口への通路

    南西出入口への通路

    -

    看板

    +

    看板

    公園の立て看板

    -

    売却予定地

    +

    売却予定地

    売却予定地
    佐川幸義氏のご遺族から土地約1,300㎡と現金約3,000万円をご遺贈いただき、さらにふるさと納税で小平市として過去最多の約1,600万円ものご寄附をいただきました。ありがとうございました。それでもなお整備費用として約2千万円が不足しているという理由から、小林洋子市政である小平市は1区画を売却するとしました。

    路線価は約21万円/㎡ですので、土地の評価額は約2.7億円となります。合計で優に3億円もの寄附をいただいたことになります。また市民と市が理想的な形での協働ができたり、ふるさと納税で歴代記録を更新している画期的な事業だったわけですから、最後は市に残りの整備費用を出してもらいたかったところです。残念ですがこの1区画は令和6年度中に売却予定です。

    -

    動画

    +

    動画

    Xに投稿した動画をご覧ください。

    @@ -122,65 +122,65 @@ 小平市上水南町二丁目の佐川幸義邸跡地に新設される合気公園の整備状況を見てきましたので、写真と動画で現在の様子をお知らせします。

    順調に進んでいるようですね😊

    -

    これまでの経緯

    +

    これまでの経緯

    合気公園に関してこれまでの経緯はこちら↓にまとめています。

    -

    市民による公園整備案

    +

    市民による公園整備案

    旧佐川邸の公園化を考える会が市に提出した公園計画案はこちら↓(ランドスケープデザイナー・鈴木綾氏による)

    旧佐川邸の公園化を考える会が市に提出した合気公園計画案

    -

    市の整備イメージ案

    +

    市の整備イメージ案

    上記の提案を受けて市が令和5年5月に提示した整備イメージ案がこちら↓

    市民による計画案を受けて小平市水と緑と公園課が提示した合気公園整備イメージ案

    この整備イメージに基づいて整備が進んでいます。

    -

    写真

    +

    写真

    次の写真と動画は令和6年1月30日(火)に撮影したものです。

    -

    お知らせ

    +

    お知らせ

    合気公園整備中・お知らせ

    -

    野外卓・顕彰碑・背なしベンチ

    +

    野外卓・顕彰碑・背なしベンチ

    養生されている?道場跡の部分が見えます。

    合気公園整備中・野外卓・顕彰碑・背なしベンチ

    -

    パーゴラ・顕彰碑

    -

    合気公園整備中・パーゴラ・顕彰��碑

    -

    メッシュフェンス・ブロック

    +

    パーゴラ・顕彰碑

    +

    合気公園整備中・パーゴラ・顕彰碑

    +

    メッシュフェンス・ブロック

    合気公園整備中・メッシュフェンス・ブロック

    -

    水飲み場・東側出入口

    +

    水飲み場・東側出入口

    園名碑が少し写っています。トラックが止まっていて撮影できませんでした。

    -

    水飲み場・東側出入口

    +

    水飲み場・東側出入口

    合気公園整備中・水飲み場・東側出入口

    -

    庭園付近

    +

    庭園付近

    合気公園整備中・庭園付近

    -

    西側出入口への道・目隠しフェンス

    +

    西側出入口への道・目隠しフェンス

    合気公園整備中・西側出入口への道・目隠しフェンス

    -

    南東の角

    +

    南東の角

    道路が拡幅されていることが分かります。

    合気公園整備中・南東の角

    -

    庭園

    +

    庭園

    このあたりは今後手が入るのかもですね。

    合気公園整備中・園庭

    -

    売却予定地

    +

    売却予定地

    奥の砂が盛ってあるところが売却予定地です。

    合気公園整備中・売却予定地(奥の砂が盛ってあるところ)

    -

    拡幅された東側道路

    +

    拡幅された東側道路

    合気公園整備中・拡幅された東側道路

    -

    道路拡幅は売却予定地前まで

    +

    道路拡幅は売却予定地前まで

    合気公園整備中・道路拡幅は売却予定地前まで

    -

    佐川氏が四股を踏んでいたとされる石

    +

    佐川氏が四股を踏んでいたとされる石

    右下の平らな石が、佐川幸義氏が四股を踏んでいたとされる石です。

    合気公園整備中・四股を踏んでいたと思われる石

    -

    西側出入口

    +

    西側出入口

    案内板はまだ設置されていません。

    合気公園整備中・西側出入口

    -

    西側園路と目隠しフェンス

    +

    西側園路と目隠しフェンス

    合気公園整備中・西側園路と目隠しフェンス

    -

    西側の工事看板・目隠しフェンス上部

    +

    西側の工事看板・目隠しフェンス上部

    目隠しフェンス上部は人の乗り越えられないような突起が出ています。

    合気公園整備中・西側の工事看板・目隠しフェンス上部

    -

    西側園路の舗装

    +

    西側園路の舗装

    合気公園整備中・西側園路の舗装

    -

    動画

    +

    動画

    @@ -202,13 +202,13 @@ 2024-01-27T00:00:00.000Z - Docusaurus🦖 + Docusaurus🦖

    1か月弱使いましたがこのDocusaurus(ドキュサウルス)は数あるCMSの中でも秀逸です。

    文書作成と管理が容易で、拡張の自由度も非常に高く、完全なオープンソース。

    議員活動に重要な「資料を作成しまとめて公開するツール」として現状の最適解と感じます。

    Facebookが母体なのでいろいろと気になるところですが、Reactを初めとして有益なソフトウェアを完全なオープンソースとして提供してくれていることは純粋にありがたいと感じます。

    -

    Admonitionのタイトルが見出しにならない

    +

    Admonitionのタイトルが見出しにならない

    さてDocusaurusにはAdmonition(注意書きや警告文)を容易にMarkdownで書く方法が用意されています。

    たとえばinfoなら、次のようにMarkdownで書けば、

    Admonitionの記法(mdもしくはmdxに記載)
    :::info[infoの例]

    ここに文章を書く

    :::
    @@ -222,47 +222,47 @@

    次のように本文中に見出しを書く方法もありますが

    Admonitionの記法(mdもしくはmdxに記載)
    :::info

    #### テスト

    ~文章~

    :::

    見た目がイマイチになります。

    -
    備考

    テスト

    ~文章~

    +
    備考

    テスト

    ~文章~

    ほかのユーザーからの要望も上がっており、私も少し不便に感じていたので、次の仕様になるようカスタマイズしましたのでその方法を解説します。

    -

    カスタマイズ後はどうなるか

    +

    カスタマイズ後はどうなるか

    後述のカスタマイズをすると、Admonitionのタイトル部に(通常の見出しMarkdownと同様に)#を冒頭に2個以上入れる ことで見出しになります。またTOCにも反映されます。#を2個以上としているのは、H1をAdmonitionには使わないはずのため。#を入れない場合は見出しにならず、TOCにも反映されません。

    -

    タイトル冒頭に#を入れた場合

    +

    タイトル冒頭に#を入れた場合

    Admonitionの記法(mdもしくはmdxに記載)
    :::info[#### 見出しになりTOCに反映されるタイトルの例]

    #が4つ分のためH4見出しになります。TOCにも反映されます。

    :::

    見出しになり、TOCに反映されるタイトルの例

    #が4つ分のためH4見出しになります。TOCにも反映されます。

    このブログでもTOCに表示されています。
    なおマウスカーソルを乗せた際にハッシュリンク(#)が表示されるようにするにはCSSの設定が必要です。

    -

    タイトル冒頭に#を入れない場合

    +

    タイトル冒頭に#を入れない場合

    Admonitionの記法(mdもしくはmdxに記載)
    :::info[見出しにならずTOCに反映されないタイトルの例]

    #がないため見出しにならず、TOCにも反映されません。

    :::

    見出しにならず、TOCに反映されないタイトルの例

    #がないため見出しになりません。

    -

    タイトルにHTMLを入れることも可能

    +

    タイトルにHTMLを入れることも可能

    なおタイトル部にHTMLを入れることもできます。TOCにも反映されます。

    テスト123テスト下線


    タイトル部のmarkdownは次の通りです。

    #### テスト<sup>123</sup>テスト<u>下線</u>

    -

    カスタマイズに利用した機能

    +

    カスタマイズに利用した機能

    カスタマイズは次の機能を活用しました。

    -

    RemarkとRehypeについて

    +

    RemarkとRehypeについて

    RemarkとRehypeは、MarkdownをHTMLに変換するプロセスにおいて、AST(抽象構文木・Abstract Syntax Tree)に作用するプラグインです。なおASTを操作するオープンソースのエコシステムの中にはもう1つRetextというプラグインもありますが、Docusaurusには実装されていないようです。

    markdownからHTMLへ変換処理の流れ
    | ........................ process ........................... |
    | .......... parse ... | ... run ... | ... stringify ..........|

    +--------+ +----------+
    Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output
    +--------+ | +----------+
    X
    |
    +--------------+
    | Transformers |
    +--------------+

    上図(Unified Overviewより)にTransformersとあるところがRemark/Rehypeの動作するところ。

    RemarkはMarkdown形式で、RehypeはHTML形式でASTを扱います。どちらも同じようにASTを操作できますが、データ構造が違うため、目的に応じて選択することになるのかなと思います。

    こちらのサイトなどが詳しいです。

    -

    Docusaurusにおけるプラグインの実行タイミング

    +

    Docusaurusにおけるプラグインの実行タイミング

    Docusaurusでこれらのプラグインを利用するためにはdocusaurus.config.jsonに設定が必要です。このページによると次の4種類の設定値にてプラグインを登録できます。

    デフォルトプラグイン適用前デフォルトプラグイン適用後
    Remark
    Markdown形式
    beforeDefaultRemarkPluginsremarkPlugins
    Rehype
    HTML形式
    beforeDefaultRehypePluginsrehypePlugins

    MarkdownからHTMLへの変換処理のところで、Docusaurusは自前のプラグイン(デフォルトプラグイン)を使い「見出しにidをつける」「ASTからTOCを作成する」などの処理を行っています。そのため今回のように「Amonitionのタイトルを読んでTOCに反映する」ためには、デフォルトプラグイン適用前と適用後の両方のタイミングでの処理が必要になります。

    -

    Swizzlingについて

    +

    Swizzlingについて

    Swizzlingはこちらに説明があるとおりの機能で、簡単に言うとReactのコンポーネントをカスタマイズできる機能です。

    Swizzlingの設定をすると、Docusaurusがデフォルトのコンポーネントの代わりに自動的にカスタマイズしたコンポーネントを使用します。

    今回は、デフォルトのAdmonitionにないID属性を持たせるためAdmonitionコンポーネントをカスタマイズしました。Swizzlingの設定をすることにより、デフォルトのAdmonitionの代わりにこのカスタムコンポーネントが使われるようにします。

    -

    動作原理

    +

    動作原理

    TOCは「ASTに含まれているheading要素を単純に配列に入れている」だけですが、この処理はカスタマイズで上書きできません。そこで、カスタマイズできる処理だけでAdmonitionのタイトルをTOCに反映する方法として次を思いつき、実装しました。

    1. docusaurusのデフォルトプラグインがTOCの処理を行うより前に、Admonitionのタイトル部を見出しとして新規作成し、Admonition要素の直前に追加する
    2. @@ -270,31 +270,31 @@
    3. デフォルトプラグインの処理が終了したら作成した見出しは不要になるので削除する。その際、削除する見出しと同じタイトルをもつAdmonition要素を探し、idを与える
    4. AdmonitionコンポーネントでidをHTMLタグに付与する
    -

    実装

    -

    docusaurus.config.json

    +

    実装

    +

    docusaurus.config.json

    まずdocusaurus.config.jsonにimportとplugin設定を記入します(ハイライト部)。

    これでDocusaurusデフォルトプラグイン適用の前後にそれぞれ自作のRemark/Rehypeプラグインが実行されることになります。

    blogなどを入れている場合は、そのプロパティにも記載します。

    docusaurus.config.json
    import admonitionTitleToHeadingBeforeTOC from './src/remark/admonition-title-to-heading-before-toc.js';
    import admonitionTitleToHeadingAfterTOC from './src/rehype/admonition-title-to-heading-after-toc.js';

    export default {
    // ...
    presets: [
    [
    'classic',
    /** @type {import('@docusaurus/preset-classic').Options} */
    ({
    docs: {
    // ...
    beforeDefaultRemarkPlugins: [admonitionTitleToHeadingBeforeTOC],
    rehypePlugins: [admonitionTitleToHeadingAfterTOC],
    },
    blog: {
    // ...
    beforeDefaultRemarkPlugins: [admonitionTitleToHeadingBeforeTOC],
    rehypePlugins: [admonitionTitleToHeadingAfterTOC],
    },
    // ...
    }),
    ]],
    // ...
    }
    -

    Remark/Rehypeプラグイン

    +

    Remark/Rehypeプラグイン

    次にプラグインを実装します。

    docusaurusのsrcディレクトリ下にrehypeとremarkというディレクトリを作り、次のファイル名と内容で2つのプラグインを作ります。

    -
    src/rehype/admonition-title-to-heading-before-toc.js
    import {visit} from 'unist-util-visit';

    const plugin = (options) => {
    const transformer = async (ast) => {
    let newBeginningText = "";
    const visitor = ((node, index, parent) => {
    if (node.type === 'containerDirective') {
    // :::infoなどに続くタイトル冒頭Text部(冒頭#を含む(もしくは含まない)部分)を取得(:::info ##** )
    // (タイトル全体にはHTML等が含まれる可能性があるため冒頭Text部だけ操作する、残りはシャロ―コピー)
    const beginningText = node.children[0].children[0].value;

    // タイトル冒頭Text部に#が2つ以上連続しているとき
    if(/^##/.test(beginningText)) {
    // タイトル冒頭部から#とそれに続く空白を削除
    newBeginningText = beginningText.replace(/^#+/, '').trim();

    // タイトル部冒頭だけ更新し、残りはシャロ―コピー
    // まずタイトル部全体をシャロ―コピー
    let titleNodes = [...node.children[0].children];

    // 冒頭要素のvalueを更新(ほかはシャロ―コピー)
    const newTitleBeginningNode = {
    ...titleNodes[0],
    value: newBeginningText,
    }

    // タイトルノードの冒頭要素だけ更新(ほかはシャロ―コピー)
    const newTitleNodes = [ ...titleNodes ];
    newTitleNodes[0] = newTitleBeginningNode;

    // visitしているcontainerDirectiveの前にheadingノードを追加
    parent.children.splice(index, 0, {
    type: 'heading',
    depth: (beginningText.match(/^##+/) || [''])[0].length, // #の連続数がheadingの深さ
    children: newTitleNodes,
    });
    // 次に検索するのはindexを2つ分飛ばしたノード
    return index + 2;
    }
    }
    });

    visit(ast, 'containerDirective', visitor);

    };
    return transformer;
    };

    export default plugin;
    +
    src/rehype/admonition-title-to-heading-before-toc.js
    import {visit} from 'unist-util-visit';

    const plugin = (options) => {
    const transformer = async (ast) => {
    let newBeginningText = "";
    const visitor = ((node, index, parent) => {
    if (node.type === 'containerDirective') {
    // :::infoなどに続くタイトル冒頭Text部(冒頭#を含む(もしくは含まない)部分)を取得(:::info ##** )
    // (タイトル全体にはHTML等が含まれる可能性があるため冒頭Text部だけ操作する、残りはシャロ―コピー)
    const beginningText = node.children[0].children[0].value;

    // タイトル冒頭Text部に#が2つ以上連続しているとき
    if(/^##/.test(beginningText)) {
    // タイトル冒頭部から#とそれに続く空白を削除
    newBeginningText = beginningText.replace(/^#+/, '').trim();

    // タイトル部冒頭だけ更新し、残りはシャロ―コピー
    // まずタイトル部全体をシャロ―コピー
    let titleNodes = [...node.children[0].children];

    // 冒頭要素のvalueを更新(ほかはシャロ―コピー)
    const newTitleBeginningNode = {
    ...titleNodes[0],
    value: newBeginningText,
    }

    // タイトルノードの冒頭要素だけ更新(ほかはシャロ―コピー)
    const newTitleNodes = [ ...titleNodes ];
    newTitleNodes[0] = newTitleBeginningNode;

    // visitしているcontainerDirectiveの前にheadingノードを追加
    parent.children.splice(index, 0, {
    type: 'heading',
    depth: (beginningText.match(/^##+/) || [''])[0].length, // #の連続数がheadingの深さ
    children: newTitleNodes,
    });
    // 次に検索するのはindexを2つ分飛ばしたノード
    return index + 2;
    }
    }
    });

    visit(ast, 'containerDirective', visitor);

    };
    return transformer;
    };

    export default plugin;
    src/rehype/admonition-title-to-heading-after-toc.js
    import {visit} from 'unist-util-visit';

    const plugin = (options) => {
    const transformer = async (ast) => {
    let hId = null;
    let hContent = null;
    visit(ast, 'element', (node, index, parent) => {
    if (/^h[2-6]$/.test(node.tagName) && node.properties && node.properties.id) {
    // H要素(h2~h6)を見つけた場合

    // IDとタイトルの冒頭Text部を取得する
    hId = node.properties.id;
    hContent = node.children ? node.children[0].value :
    node.children[0].children[0] ? node.children[0].children[0].value : '';

    // 続くAdmonitionを探す(docはH要素とadmonitionが連続しているが
    // blogではなぜか改行要素{ type:'text', value:'\n' }が間に入っているので念のため隣接3要素を探す
    for (let i = index + 1; i < index + 4 && i < parent.children.length; i++) {
    if(parent.children[i] && parent.children[i].tagName === 'admonition') {
    // admonition(div)を見つけた場合
    const admonitionNode = parent.children[i];

    // admonitionタイトルの冒頭Text部分を取得(properties.titleもしくはchildren[0].children[0].value)
    const admonitionNodeTitle = admonitionNode.properties.title ? admonitionNode.properties.title :
    admonitionNode.children[0] && admonitionNode.children[0].children[0] ? admonitionNode.children[0].children[0].value : '';

    if(/^##/.test(admonitionNodeTitle) && admonitionNodeTitle.replace(/^#+/, '').trim() === hContent.trim()) {
    // #で始まっていて、タイトル冒頭部が同じ場合
    // divのidをHタグのidに設定
    admonitionNode.properties.id = hId;
    // H要素を削除
    parent.children.splice(index, 1);
    }
    }
    }
    }
    });
    };
    return transformer;
    };

    export default plugin;
    Admonitionのツリー構造

    参考までに、Remarkのプラグインから見るとAdmonitionのASTはたとえば次のようになっています。

    Admonitionのツリー構造(一例)
    {
    type: 'containerDirective',
    name: 'info',
    attributes: {},
    children: [
    {
    type: 'paragraph',
    data: { directiveLabel: true },
    children: [
    {
    type: 'text',
    value: '#### info title もしHTML等が入ると(ここにaタグを入れると)',
    position: [Object]
    },
    {
    type: 'mdxJsxTextElement',
    name: 'a',
    attributes: [],
    position: [Object],
    data: [Object],
    children: [Array]
    },
    { type: 'text', value: 'このようにタイトル部が別々の要素として配列に入っている。', position: [Object] }
    ],
    position: {
    start: { line: 1347, column: 8, offset: 34053 },
    end: { line: 1347, column: 55, offset: 34100 }
    }
    },
    { type: 'paragraph', children: [Array], position: [Object] },
    ...
    ],
    ...
    }
    -

    Swizzling

    +

    Swizzling

    次にSwizzlingです。

    Docusaurusのsrc/themeディレクトリにAdmonitionというディレクトリを作り、次の一例のファイルを格納します。

    なおここに説明がある通りSwizzlingにはEjectingとWrappingの方法があります。

    WrappingではAdmonitionの内部までカスタマイズできないため「タイトル部分にidプロパティを付ける」といったことができません。そのためEjectingを使います。

    注記

    Ejectingを使う場合はバージョンアップによってデフォルトのコンポーネントと挙動が変わってくる可能性があるのでアップグレードの際は注意が必要です。

    -

    Ejecting

    +

    Ejecting

    npm run swizzle @docusaurus/theme-classic Admonition -- --eject

    Docusaurus V3.1では次のようなメッセージが出ますので、YESを選びます。

    ? Do you really want to swizzle this unsafe internal component? » - Use arrow-keys. Return to submit.
    NO: cancel and stay safe
    > YES: I know what I am doing!

    Ejectingをすると、実質的にnode_modulesの@docusaurus/theme-classic/lib/themeにあるコンポーネントがsrc/themeディレクトリにコピーされます。

    あとはコピーされたコンポーネントをいじるだけです。

    -

    コードの変更

    +

    コードの変更

    Layoutファイルを次のように変更するのみです。

    src/theme/Admonition/Layout/index.js
    import React from 'react';
    import clsx from 'clsx';
    import {ThemeClassNames} from '@docusaurus/theme-common';
    import styles from './styles.module.css';
    import headingStyles from '@docusaurus/theme-classic/lib/theme/Heading/styles.module.css';

    function AdmonitionContainer({type, className, children}) {
    return (
    <div
    className={clsx(
    ThemeClassNames.common.admonition,
    ThemeClassNames.common.admonitionType(type),
    styles.admonition,
    className,
    )}>
    {children}
    </div>
    );
    }
    function AdmonitionHeading({icon, title, id}) {
    let depth = 0;
    let trimmedTitle = title;
    // titleにHTML等が含まれている場合は文字列ではなく配列になる
    if(typeof title === "string") {
    // 文字列冒頭の#の数を数える(
    depth = title.match ? (title.toString().match(/^#+/) || [''])[0].length : 0;
    // #を省いたタイトルを得る
    trimmedTitle = depth > 0 ? title.replace(/^#+/, '').trim() : title;
    } else if (typeof title[0] === "string") {
    depth = title[0].match ? (title[0].match(/^#+/) || [''])[0].length : 0;
    trimmedTitle = depth > 0 ? [title[0].replace(/^#+/, '').trim(), ...title.slice(1)] : title;
    }

    // スクロール位置調整のcss
    const classNames = clsx("anchor", "title", headingStyles.anchorWithStickyNavbar);
    // depthに応じて見出しタグをレンダー
    return (
    <div className={styles.admonitionHeading}>
    <span className={styles.admonitionIcon}>{icon}</span>
    {(() => {
    if (depth == 3) {
    return(
    <h3
    id={id}
    className={classNames}
    >
    {trimmedTitle}
    </h3>
    )
    } else if (depth == 4) {
    return(
    <h4
    id={id}
    className={classNames}
    >
    {trimmedTitle}
    </h4>
    )
    } else if (depth == 5) {
    return(
    <h5
    id={id}
    className={classNames}
    >
    {trimmedTitle}
    </h5>
    )
    } else if (depth == 6) {
    return(
    <h6
    id={id}
    className={classNames}
    >
    {trimmedTitle}
    </h6>
    )
    } else {
    return(
    <>
    {trimmedTitle}
    </>
    )
    }
    })()}
    </div>
    );
    }
    function AdmonitionContent({children}) {
    return children ? (
    <div className={styles.admonitionContent}>{children}</div>
    ) : null;
    }
    export default function AdmonitionLayout(props) {
    const {type, icon, title, children, className, id} = props;
    return (
    <AdmonitionContainer type={type} className={className}>
    <AdmonitionHeading title={title} icon={icon} id={id} />
    <AdmonitionContent>{children}</AdmonitionContent>
    </AdmonitionContainer>
    );
    }

    上記を設定後、npm start等の再起動が必要です。

    diff --git a/build/blog/index.html b/build/blog/index.html index f36c9282..6fe22cb9 100644 --- a/build/blog/index.html +++ b/build/blog/index.html @@ -2,7 +2,7 @@ - + ふらっとブログ | 東京都小平市議・安竹洋平のまとめ @@ -12,17 +12,17 @@ - - - + + + -

    Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法

    · 約17分
    安竹 洋平
    東京都小平市議会議員

    Docusaurus🦖

    +

    Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法

    · 約17分
    安竹 洋平
    東京都小平市議会議員

    Docusaurus🦖

    1か月弱使いましたがこのDocusaurus(ドキュサウルス)は数あるCMSの中でも秀逸です。

    文書作成と管理が容易で、拡張の自由度も非常に高く、完全なオープンソース。

    -

    議員活動に重要な「資料を作成しまとめて公開するツール」として現状の最適解と感じます。

    新ふらっとブログ立ち上げました

    · 約1分
    安竹 洋平
    東京都小平市議会議員

    今後の展開を考え、これまでの議会活動等のまとめをこのDocusaurusの仕組みに移行しています。

    ふらっとブログもこちらに新しく立ち上げることにしました。

    議員活動や、その裏で活用している技術について、誰かの役に立つかもしれないことを中心にブログにしていきます😁

    diff --git a/build/blog/rss.xml b/build/blog/rss.xml index 368a9770..b0079ce3 100644 --- a/build/blog/rss.xml +++ b/build/blog/rss.xml @@ -17,14 +17,14 @@ 小平市上水南町二丁目の佐川幸義邸跡地に新設された小平市立合気公園がついに開園しました!

    令和6年3月23日(土)午前10時から現地で行われた開園セレモニーに参加しましたので、写真と動画で様子をお知らせします。

    -

    これまでの経緯

    +

    これまでの経緯

    合気公園に関してこれまでの経緯はこちら↓にまとめています。

    -

    セレモニーの様子

    +

    セレモニーの様子

    次のような次第で、約30分間行われました。

    1. 開会の辞(水と緑と公園課長)
    2. @@ -50,19 +50,19 @@

      除幕直前

      除幕直後
      ↑市長、市議会議長、木村氏、岡江氏により、顕彰碑の除幕が行われました。

      -

      式次第(小平市)

      +

      式次第(小平市)

      -

      そのほかの写真

      +

      そのほかの写真

      次の写真と動画はセレモニー直後と3月28日(木)に撮影したものです。

      -

      出入口

      +

      出入口

      正面出入口
      正面出入口

      北東側出入口
      北東側出入口

      -

      園名碑

      +

      園名碑

      合気公園・園名碑
      道場に使われていた屋根瓦(鬼瓦)の一部が埋め込まれています。道場関係の方によるデザインです。

      -

      顕彰碑

      +

      顕彰碑

      合気公園・顕彰碑

      合気公園・顕彰碑詳細

      @@ -76,28 +76,28 @@ 円満に滞り無く動じて居るのである
      その調和が合氣なのである

      -

      日本庭園

      +

      日本庭園

      日本庭園(1)

      日本庭園(2)
      早くも子どもたちが石に上って遊んでいました😍

      -

      道場跡地横にあるベンチ

      +

      道場跡地横にあるベンチ

      道場跡地横のベンチ
      緑色に舗装された部分が道場跡地です。

      -

      テーブル

      +

      テーブル

      ベンチとテーブル

      -

      パーゴラとベンチ

      +

      パーゴラとベンチ

      パーゴラとベンチ

      -

      水飲み

      +

      水飲み

      水飲み

      -

      南西出入口への通路

      +

      南西出入口への通路

      南西出入口への通路

      -

      看板

      +

      看板

      公園の立て看板

      -

      売却予定地

      +

      売却予定地

      売却予定地
      佐川幸義氏のご遺族から土地約1,300㎡と現金約3,000万円をご遺贈いただき、さらにふるさと納税で小平市として過去最多の約1,600万円ものご寄附をいただきました。ありがとうございました。それでもなお整備費用として約2千万円が不足しているという理由から、小林洋子市政である小平市は1区画を売却するとしました。

      路線価は約21万円/㎡ですので、土地の評価額は約2.7億円となります。合計で優に3億円もの寄附をいただいたことになります。また市民と市が理想的な形での協働ができたり、ふるさと納税で歴代記録を更新している画期的な事業だったわけですから、最後は市に残りの整備費用を出してもらいたかったところです。残念ですがこの1区画は令和6年度中に売却予定です。

      -

      動画

      +

      動画

      Xに投稿した動画をご覧ください。

      @@ -119,65 +119,65 @@ 小平市上水南町二丁目の佐川幸義邸跡地に新設される合気公園の整備状況を見てきましたので、写真と動画で現在の様子をお知らせします。

      順調に進んでいるようですね😊

      -

      これまでの経緯

      +

      これまでの経緯

      合気公園に関してこれまでの経緯はこちら↓にまとめています。

      -

      市民による公園整備案

      +

      市民による公園整備案

      旧佐川邸の公園化を考える会が市に提出した公園計画案はこちら↓(ランドスケープデザイナー・鈴木綾氏による)

      旧佐川邸の公園化を考える会が市に提出した合気公園計画案

      -

      市の整備イメージ案

      +

      市の整備イメージ案

      上記の提案を受けて市が令和5年5月に提示した整備イメージ案がこちら↓

      市民による計画案を受けて小平市水と緑と公園課が提示した合気公園整備イメージ案

      この整備イメージに基づいて整備が進んでいます。

      -

      写真

      +

      写真

      次の写真と動画は令和6年1月30日(火)に撮影したものです。

      -

      お知らせ

      +

      お知らせ

      合気公園整備中・お知らせ

      -

      野外卓・顕彰碑・背なしベンチ

      +

      野外卓・顕彰碑・背なしベンチ

      養生されている?道場跡の部分が見えます。

      合気公園整備中・野外卓・顕彰碑・背なしベンチ

      -

      パーゴラ・顕彰碑

      -

      合気公園整備中・パーゴラ・顕彰��碑

      -

      メッシュフェンス・ブロック

      +

      パーゴラ・顕彰碑

      +

      合気公園整備中・パーゴラ・顕彰碑

      +

      メッシュフェンス・ブロック

      合気公園整備中・メッシュフェンス・ブロック

      -

      水飲み場・東側出入口

      +

      水飲み場・東側出入口

      園名碑が少し写っています。トラックが止まっていて撮影できませんでした。

      -

      水飲み場・東側出入口

      +

      水飲み場・東側出入口

      合気公園整備中・水飲み場・東側出入口

      -

      庭園付近

      +

      庭園付近

      合気公園整備中・庭園付近

      -

      西側出入口への道・目隠しフェンス

      +

      西側出入口への道・目隠しフェンス

      合気公園整備中・西側出入口への道・目隠しフェンス

      -

      南東の角

      +

      南東の角

      道路が拡幅されていることが分かります。

      合気公園整備中・南東の角

      -

      庭園

      +

      庭園

      このあたりは今後手が入るのかもですね。

      合気公園整備中・園庭

      -

      売却予定地

      +

      売却予定地

      奥の砂が盛ってあるところが売却予定地です。

      合気公園整備中・売却予定地(奥の砂が盛ってあるところ)

      -

      拡幅された東側道路

      +

      拡幅された東側道路

      合気公園整備中・拡幅された東側道路

      -

      道路拡幅は売却予定地前まで

      +

      道路拡幅は売却予定地前まで

      合気公園整備中・道路拡幅は売却予定地前まで

      -

      佐川氏が四股を踏んでいたとされる石

      +

      佐川氏が四股を踏んでいたとされる石

      右下の平らな石が、佐川幸義氏が四股を踏んでいたとされる石です。

      合気公園整備中・四股を踏んでいたと思われる石

      -

      西側出入口

      +

      西側出入口

      案内板はまだ設置されていません。

      合気公園整備中・西側出入口

      -

      西側園路と目隠しフェンス

      +

      西側園路と目隠しフェンス

      合気公園整備中・西側園路と目隠しフェンス

      -

      西側の工事看板・目隠しフェンス上部

      +

      西側の工事看板・目隠しフェンス上部

      目隠しフェンス上部は人の乗り越えられないような突起が出ています。

      合気公園整備中・西側の工事看板・目隠しフェンス上部

      -

      西側園路の舗装

      +

      西側園路の舗装

      合気公園整備中・西側園路の舗装

      -

      動画

      +

      動画

      @@ -195,13 +195,13 @@ https://yasutakeyohei.com/docs/blog/2024/01/27/docusaurus-admonition-heading-toc/ Sat, 27 Jan 2024 00:00:00 GMT - Docusaurus🦖 + Docusaurus🦖

      1か月弱使いましたがこのDocusaurus(ドキュサウルス)は数あるCMSの中でも秀逸です。

      文書作成と管理が容易で、拡張の自由度も非常に高く、完全なオープンソース。

      議員活動に重要な「資料を作成しまとめて公開するツール」として現状の最適解と感じます。

      Facebookが母体なのでいろいろと気になるところですが、Reactを初めとして有益なソフトウェアを完全なオープンソースとして提供してくれていることは純粋にありがたいと感じます。

      -

      Admonitionのタイトルが見出しにならない

      +

      Admonitionのタイトルが見出しにならない

      さてDocusaurusにはAdmonition(注意書きや警告文)を容易にMarkdownで書く方法が用意されています。

      たとえばinfoなら、次のようにMarkdownで書けば、

      Admonitionの記法(mdもしくはmdxに記載)
      :::info[infoの例]

      ここに文章を書く

      :::
      @@ -215,47 +215,47 @@

      次のように本文中に見出しを書く方法もありますが

      Admonitionの記法(mdもしくはmdxに記載)
      :::info

      #### テスト

      ~文章~

      :::

      見た目がイマイチになります。

      -
      備考

      テスト

      ~文章~

      +
      備考

      テスト

      ~文章~

      ほかのユーザーからの要望も上がっており、私も少し不便に感じていたので、次の仕様になるようカスタマイズしましたのでその方法を解説します。

      -

      カスタマイズ後はどうなるか

      +

      カスタマイズ後はどうなるか

      後述のカスタマイズをすると、Admonitionのタイトル部に(通常の見出しMarkdownと同様に)#を冒頭に2個以上入れる ことで見出しになります。またTOCにも反映されます。#を2個以上としているのは、H1をAdmonitionには使わないはずのため。#を入れない場合は見出しにならず、TOCにも反映されません。

      -

      タイトル冒頭に#を入れた場合

      +

      タイトル冒頭に#を入れた場合

      Admonitionの記法(mdもしくはmdxに記載)
      :::info[#### 見出しになりTOCに反映されるタイトルの例]

      #が4つ分のためH4見出しになります。TOCにも反映されます。

      :::

      見出しになり、TOCに反映されるタイトルの例

      #が4つ分のためH4見出しになります。TOCにも反映されます。

      このブログでもTOCに表示されています。
      なおマウスカーソルを乗せた際にハッシュリンク(#)が表示されるようにするにはCSSの設定が必要です。

      -

      タイトル冒頭に#を入れない場合

      +

      タイトル冒頭に#を入れない場合

      Admonitionの記法(mdもしくはmdxに記載)
      :::info[見出しにならずTOCに反映されないタイトルの例]

      #がないため見出しにならず、TOCにも反映されません。

      :::

      見出しにならず、TOCに反映されないタイトルの例

      #がないため見出しになりません。

      -

      タイトルにHTMLを入れることも可能

      +

      タイトルにHTMLを入れることも可能

      なおタイトル部にHTMLを入れることもできます。TOCにも反映されます。

      テスト123テスト下線


      タイトル部のmarkdownは次の通りです。

      #### テスト<sup>123</sup>テスト<u>下線</u>

      -

      カスタマイズに利用した機能

      +

      カスタマイズに利用した機能

      カスタマイズは次の機能を活用しました。

      -

      RemarkとRehypeについて

      +

      RemarkとRehypeについて

      RemarkとRehypeは、MarkdownをHTMLに変換するプロセスにおいて、AST(抽象構文木・Abstract Syntax Tree)に作用するプラグインです。なおASTを操作するオープンソースのエコシステムの中にはもう1つRetextというプラグインもありますが、Docusaurusには実装されていないようです。

      markdownからHTMLへ変換処理の流れ
      | ........................ process ........................... |
      | .......... parse ... | ... run ... | ... stringify ..........|

      +--------+ +----------+
      Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output
      +--------+ | +----------+
      X
      |
      +--------------+
      | Transformers |
      +--------------+

      上図(Unified Overviewより)にTransformersとあるところがRemark/Rehypeの動作するところ。

      RemarkはMarkdown形式で、RehypeはHTML形式でASTを扱います。どちらも同じようにASTを操作できますが、データ構造が違うため、目的に応じて選択することになるのかなと思います。

      こちらのサイトなどが詳しいです。

      -

      Docusaurusにおけるプラグインの実行タイミング

      +

      Docusaurusにおけるプラグインの実行タイミング

      Docusaurusでこれらのプラグインを利用するためにはdocusaurus.config.jsonに設定が必要です。このページによると次の4種類の設定値にてプラグインを登録できます。

      デフォルトプラグイン適用前デフォルトプラグイン適用後
      Remark
      Markdown形式
      beforeDefaultRemarkPluginsremarkPlugins
      Rehype
      HTML形式
      beforeDefaultRehypePluginsrehypePlugins

      MarkdownからHTMLへの変換処理のところで、Docusaurusは自前のプラグイン(デフォルトプラグイン)を使い「見出しにidをつける」「ASTからTOCを作成する」などの処理を行っています。そのため今回のように「Amonitionのタイトルを読んでTOCに反映する」ためには、デフォルトプラグイン適用前と適用後の両方のタイミングでの処理が必要になります。

      -

      Swizzlingについて

      +

      Swizzlingについて

      Swizzlingはこちらに説明があるとおりの機能で、簡単に言うとReactのコンポーネントをカスタマイズできる機能です。

      Swizzlingの設定をすると、Docusaurusがデフォルトのコンポーネントの代わりに自動的にカスタマイズしたコンポーネントを使用します。

      今回は、デフォルトのAdmonitionにないID属性を持たせるためAdmonitionコンポーネントをカスタマイズしました。Swizzlingの設定をすることにより、デフォルトのAdmonitionの代わりにこのカスタムコンポーネントが使われるようにします。

      -

      動作原理

      +

      動作原理

      TOCは「ASTに含まれているheading要素を単純に配列に入れている」だけですが、この処理はカスタマイズで上書きできません。そこで、カスタマイズできる処理だけでAdmonitionのタイトルをTOCに反映する方法として次を思いつき、実装しました。

      1. docusaurusのデフォルトプラグインがTOCの処理を行うより前に、Admonitionのタイトル部を見出しとして新規作成し、Admonition要素の直前に追加する
      2. @@ -263,31 +263,31 @@
      3. デフォルトプラグインの処理が終了したら作成した見出しは不要になるので削除する。その際、削除する見出しと同じタイトルをもつAdmonition要素を探し、idを与える
      4. AdmonitionコンポーネントでidをHTMLタグに付与する
      -

      実装

      -

      docusaurus.config.json

      +

      実装

      +

      docusaurus.config.json

      まずdocusaurus.config.jsonにimportとplugin設定を記入します(ハイライト部)。

      これでDocusaurusデフォルトプラグイン適用の前後にそれぞれ自作のRemark/Rehypeプラグインが実行されることになります。

      blogなどを入れている場合は、そのプロパティにも記載します。

      docusaurus.config.json
      import admonitionTitleToHeadingBeforeTOC from './src/remark/admonition-title-to-heading-before-toc.js';
      import admonitionTitleToHeadingAfterTOC from './src/rehype/admonition-title-to-heading-after-toc.js';

      export default {
      // ...
      presets: [
      [
      'classic',
      /** @type {import('@docusaurus/preset-classic').Options} */
      ({
      docs: {
      // ...
      beforeDefaultRemarkPlugins: [admonitionTitleToHeadingBeforeTOC],
      rehypePlugins: [admonitionTitleToHeadingAfterTOC],
      },
      blog: {
      // ...
      beforeDefaultRemarkPlugins: [admonitionTitleToHeadingBeforeTOC],
      rehypePlugins: [admonitionTitleToHeadingAfterTOC],
      },
      // ...
      }),
      ]],
      // ...
      }
      -

      Remark/Rehypeプラグイン

      +

      Remark/Rehypeプラグイン

      次にプラグインを実装します。

      docusaurusのsrcディレクトリ下にrehypeとremarkというディレクトリを作り、次のファイル名と内容で2つのプラグインを作ります。

      -
      src/rehype/admonition-title-to-heading-before-toc.js
      import {visit} from 'unist-util-visit';

      const plugin = (options) => {
      const transformer = async (ast) => {
      let newBeginningText = "";
      const visitor = ((node, index, parent) => {
      if (node.type === 'containerDirective') {
      // :::infoなどに続くタイトル冒頭Text部(冒頭#を含む(もしくは含まない)部分)を取得(:::info ##** )
      // (タイトル全体にはHTML等が含まれる可能性があるため冒頭Text部だけ操作する、残りはシャロ―コピー)
      const beginningText = node.children[0].children[0].value;

      // タイトル冒頭Text部に#が2つ以上連続しているとき
      if(/^##/.test(beginningText)) {
      // タイトル冒頭部から#とそれに続く空白を削除
      newBeginningText = beginningText.replace(/^#+/, '').trim();

      // タイトル部冒頭だけ更新し、残りはシャロ―コピー
      // まずタイトル部全体をシャロ―コピー
      let titleNodes = [...node.children[0].children];

      // 冒頭要素のvalueを更新(ほかはシャロ―コピー)
      const newTitleBeginningNode = {
      ...titleNodes[0],
      value: newBeginningText,
      }

      // タイトルノードの冒頭要素だけ更新(ほかはシャロ―コピー)
      const newTitleNodes = [ ...titleNodes ];
      newTitleNodes[0] = newTitleBeginningNode;

      // visitしているcontainerDirectiveの前にheadingノードを追加
      parent.children.splice(index, 0, {
      type: 'heading',
      depth: (beginningText.match(/^##+/) || [''])[0].length, // #の連続数がheadingの深さ
      children: newTitleNodes,
      });
      // 次に検索するのはindexを2つ分飛ばしたノード
      return index + 2;
      }
      }
      });

      visit(ast, 'containerDirective', visitor);

      };
      return transformer;
      };

      export default plugin;
      +
      src/rehype/admonition-title-to-heading-before-toc.js
      import {visit} from 'unist-util-visit';

      const plugin = (options) => {
      const transformer = async (ast) => {
      let newBeginningText = "";
      const visitor = ((node, index, parent) => {
      if (node.type === 'containerDirective') {
      // :::infoなどに続くタイトル冒頭Text部(冒頭#を含む(もしくは含まない)部分)を取得(:::info ##** )
      // (タイトル全体にはHTML等が含まれる可能性があるため冒頭Text部だけ操作する、残りはシャロ―コピー)
      const beginningText = node.children[0].children[0].value;

      // タイトル冒頭Text部に#が2つ以上連続しているとき
      if(/^##/.test(beginningText)) {
      // タイトル冒頭部から#とそれに続く空白を削除
      newBeginningText = beginningText.replace(/^#+/, '').trim();

      // タイトル部冒頭だけ更新し、残りはシャロ―コピー
      // まずタイトル部全体をシャロ―コピー
      let titleNodes = [...node.children[0].children];

      // 冒頭要素のvalueを更新(ほかはシャロ―コピー)
      const newTitleBeginningNode = {
      ...titleNodes[0],
      value: newBeginningText,
      }

      // タイトルノードの冒頭要素だけ更新(ほかはシャロ―コピー)
      const newTitleNodes = [ ...titleNodes ];
      newTitleNodes[0] = newTitleBeginningNode;

      // visitしているcontainerDirectiveの前にheadingノードを追加
      parent.children.splice(index, 0, {
      type: 'heading',
      depth: (beginningText.match(/^##+/) || [''])[0].length, // #の連続数がheadingの深さ
      children: newTitleNodes,
      });
      // 次に検索するのはindexを2つ分飛ばしたノード
      return index + 2;
      }
      }
      });

      visit(ast, 'containerDirective', visitor);

      };
      return transformer;
      };

      export default plugin;
      src/rehype/admonition-title-to-heading-after-toc.js
      import {visit} from 'unist-util-visit';

      const plugin = (options) => {
      const transformer = async (ast) => {
      let hId = null;
      let hContent = null;
      visit(ast, 'element', (node, index, parent) => {
      if (/^h[2-6]$/.test(node.tagName) && node.properties && node.properties.id) {
      // H要素(h2~h6)を見つけた場合

      // IDとタイトルの冒頭Text部を取得する
      hId = node.properties.id;
      hContent = node.children ? node.children[0].value :
      node.children[0].children[0] ? node.children[0].children[0].value : '';

      // 続くAdmonitionを探す(docはH要素とadmonitionが連続しているが
      // blogではなぜか改行要素{ type:'text', value:'\n' }が間に入っているので念のため隣接3要素を探す
      for (let i = index + 1; i < index + 4 && i < parent.children.length; i++) {
      if(parent.children[i] && parent.children[i].tagName === 'admonition') {
      // admonition(div)を見つけた場合
      const admonitionNode = parent.children[i];

      // admonitionタイトルの冒頭Text部分を取得(properties.titleもしくはchildren[0].children[0].value)
      const admonitionNodeTitle = admonitionNode.properties.title ? admonitionNode.properties.title :
      admonitionNode.children[0] && admonitionNode.children[0].children[0] ? admonitionNode.children[0].children[0].value : '';

      if(/^##/.test(admonitionNodeTitle) && admonitionNodeTitle.replace(/^#+/, '').trim() === hContent.trim()) {
      // #で始まっていて、タイトル冒頭部が同じ場合
      // divのidをHタグのidに設定
      admonitionNode.properties.id = hId;
      // H要素を削除
      parent.children.splice(index, 1);
      }
      }
      }
      }
      });
      };
      return transformer;
      };

      export default plugin;
      Admonitionのツリー構造

      参考までに、Remarkのプラグインから見るとAdmonitionのASTはたとえば次のようになっています。

      Admonitionのツリー構造(一例)
      {
      type: 'containerDirective',
      name: 'info',
      attributes: {},
      children: [
      {
      type: 'paragraph',
      data: { directiveLabel: true },
      children: [
      {
      type: 'text',
      value: '#### info title もしHTML等が入ると(ここにaタグを入れると)',
      position: [Object]
      },
      {
      type: 'mdxJsxTextElement',
      name: 'a',
      attributes: [],
      position: [Object],
      data: [Object],
      children: [Array]
      },
      { type: 'text', value: 'このようにタイトル部が別々の要素として配列に入っている。', position: [Object] }
      ],
      position: {
      start: { line: 1347, column: 8, offset: 34053 },
      end: { line: 1347, column: 55, offset: 34100 }
      }
      },
      { type: 'paragraph', children: [Array], position: [Object] },
      ...
      ],
      ...
      }
      -

      Swizzling

      +

      Swizzling

      次にSwizzlingです。

      Docusaurusのsrc/themeディレクトリにAdmonitionというディレクトリを作り、次の一例のファイルを格納します。

      なおここに説明がある通りSwizzlingにはEjectingとWrappingの方法があります。

      WrappingではAdmonitionの内部までカスタマイズできないため「タイトル部分にidプロパティを付ける」といったことができません。そのためEjectingを使います。

      注記

      Ejectingを使う場合はバージョンアップによってデフォルトのコンポーネントと挙動が変わってくる可能性があるのでアップグレードの際は注意が必要です。

      -

      Ejecting

      +

      Ejecting

      npm run swizzle @docusaurus/theme-classic Admonition -- --eject

      Docusaurus V3.1では次のようなメッセージが出ますので、YESを選びます。

      ? Do you really want to swizzle this unsafe internal component? » - Use arrow-keys. Return to submit.
      NO: cancel and stay safe
      > YES: I know what I am doing!

      Ejectingをすると、実質的にnode_modulesの@docusaurus/theme-classic/lib/themeにあるコンポーネントがsrc/themeディレクトリにコピーされます。

      あとはコピーされたコンポーネントをいじるだけです。

      -

      コードの変更

      +

      コードの変更

      Layoutファイルを次のように変更するのみです。

      src/theme/Admonition/Layout/index.js
      import React from 'react';
      import clsx from 'clsx';
      import {ThemeClassNames} from '@docusaurus/theme-common';
      import styles from './styles.module.css';
      import headingStyles from '@docusaurus/theme-classic/lib/theme/Heading/styles.module.css';

      function AdmonitionContainer({type, className, children}) {
      return (
      <div
      className={clsx(
      ThemeClassNames.common.admonition,
      ThemeClassNames.common.admonitionType(type),
      styles.admonition,
      className,
      )}>
      {children}
      </div>
      );
      }
      function AdmonitionHeading({icon, title, id}) {
      let depth = 0;
      let trimmedTitle = title;
      // titleにHTML等が含まれている場合は文字列ではなく配列になる
      if(typeof title === "string") {
      // 文字列冒頭の#の数を数える(
      depth = title.match ? (title.toString().match(/^#+/) || [''])[0].length : 0;
      // #を省いたタイトルを得る
      trimmedTitle = depth > 0 ? title.replace(/^#+/, '').trim() : title;
      } else if (typeof title[0] === "string") {
      depth = title[0].match ? (title[0].match(/^#+/) || [''])[0].length : 0;
      trimmedTitle = depth > 0 ? [title[0].replace(/^#+/, '').trim(), ...title.slice(1)] : title;
      }

      // スクロール位置調整のcss
      const classNames = clsx("anchor", "title", headingStyles.anchorWithStickyNavbar);
      // depthに応じて見出しタグをレンダー
      return (
      <div className={styles.admonitionHeading}>
      <span className={styles.admonitionIcon}>{icon}</span>
      {(() => {
      if (depth == 3) {
      return(
      <h3
      id={id}
      className={classNames}
      >
      {trimmedTitle}
      </h3>
      )
      } else if (depth == 4) {
      return(
      <h4
      id={id}
      className={classNames}
      >
      {trimmedTitle}
      </h4>
      )
      } else if (depth == 5) {
      return(
      <h5
      id={id}
      className={classNames}
      >
      {trimmedTitle}
      </h5>
      )
      } else if (depth == 6) {
      return(
      <h6
      id={id}
      className={classNames}
      >
      {trimmedTitle}
      </h6>
      )
      } else {
      return(
      <>
      {trimmedTitle}
      </>
      )
      }
      })()}
      </div>
      );
      }
      function AdmonitionContent({children}) {
      return children ? (
      <div className={styles.admonitionContent}>{children}</div>
      ) : null;
      }
      export default function AdmonitionLayout(props) {
      const {type, icon, title, children, className, id} = props;
      return (
      <AdmonitionContainer type={type} className={className}>
      <AdmonitionHeading title={title} icon={icon} id={id} />
      <AdmonitionContent>{children}</AdmonitionContent>
      </AdmonitionContainer>
      );
      }

      上記を設定後、npm start等の再起動が必要です。

      diff --git a/build/blog/tags/docusaurus/index.html b/build/blog/tags/docusaurus/index.html index abcc0473..d43f01ff 100644 --- a/build/blog/tags/docusaurus/index.html +++ b/build/blog/tags/docusaurus/index.html @@ -2,7 +2,7 @@ - + 「docusaurus」タグの記事が2件件あります | 東京都小平市議・安竹洋平のまとめ @@ -12,15 +12,15 @@ - - - + + + -

      「docusaurus」タグの記事が2件件あります

      全てのタグを見る

      Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法

      · 約17分
      安竹 洋平
      東京都小平市議会議員

      Docusaurus🦖

      +

      「docusaurus」タグの記事が2件件あります

      全てのタグを見る

      Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法

      · 約17分
      安竹 洋平
      東京都小平市議会議員

      Docusaurus🦖

      1か月弱使いましたがこのDocusaurus(ドキュサウルス)は数あるCMSの中でも秀逸です。

      文書作成と管理が容易で、拡張の自由度も非常に高く、完全なオープンソース。

      -

      議員活動に重要な「資料を作成しまとめて公開するツール」として現状の最適解と感じます。

      新ふらっとブログ立ち上げました

      · 約1分
      安竹 洋平
      東京都小平市議会議員

      今後の展開を考え、これまでの議会活動等のまとめをこのDocusaurusの仕組みに移行しています。

      ふらっとブログもこちらに新しく立ち上げることにしました。

      議員活動や、その裏で活用している技術について、誰かの役に立つかもしれないことを中心にブログにしていきます😁

      diff --git a/build/blog/tags/index.html b/build/blog/tags/index.html index bae945f6..99824c1a 100644 --- a/build/blog/tags/index.html +++ b/build/blog/tags/index.html @@ -2,7 +2,7 @@ - + タグ | 東京都小平市議・安竹洋平のまとめ @@ -12,11 +12,11 @@ - - - + + + - + \ No newline at end of file diff --git a/build/blog/tags/v-3-1/index.html b/build/blog/tags/v-3-1/index.html index ff876975..631abffb 100644 --- a/build/blog/tags/v-3-1/index.html +++ b/build/blog/tags/v-3-1/index.html @@ -2,7 +2,7 @@ - + 「v3.1」タグの記事が1件件あります | 東京都小平市議・安竹洋平のまとめ @@ -12,12 +12,12 @@ - - - + + + -

      「v3.1」タグの記事が1件件あります

      全てのタグを見る

      Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法

      · 約17分
      安竹 洋平
      東京都小平市議会議員

      Docusaurus🦖

      +

      「v3.1」タグの記事が1件件あります

      全てのタグを見る

      Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法

      · 約17分
      安竹 洋平
      東京都小平市議会議員

      Docusaurus🦖

      1か月弱使いましたがこのDocusaurus(ドキュサウルス)は数あるCMSの中でも秀逸です。

      文書作成と管理が容易で、拡張の自由度も非常に高く、完全なオープンソース。

      議員活動に重要な「資料を作成しまとめて公開するツール」として現状の最適解と感じます。

      diff --git "a/build/blog/tags/\343\201\212\347\237\245\343\202\211\343\201\233/index.html" "b/build/blog/tags/\343\201\212\347\237\245\343\202\211\343\201\233/index.html" index 5f9ff293..f15b15d9 100644 --- "a/build/blog/tags/\343\201\212\347\237\245\343\202\211\343\201\233/index.html" +++ "b/build/blog/tags/\343\201\212\347\237\245\343\202\211\343\201\233/index.html" @@ -2,7 +2,7 @@ - + 「お知らせ」タグの記事が1件件あります | 東京都小平市議・安竹洋平のまとめ @@ -12,12 +12,12 @@ - - - + + + -

      「お知らせ」タグの記事が1件件あります

      全てのタグを見る

      新ふらっとブログ立ち上げました

      · 約1分
      安竹 洋平
      東京都小平市議会議員

      今後の展開を考え、これまでの議会活動等のまとめをこのDocusaurusの仕組みに移行しています。

      +

      「お知らせ」タグの記事が1件件あります

      全てのタグを見る

      新ふらっとブログ立ち上げました

      · 約1分
      安竹 洋平
      東京都小平市議会議員

      今後の展開を考え、これまでの議会活動等のまとめをこのDocusaurusの仕組みに移行しています。

      ふらっとブログもこちらに新しく立ち上げることにしました。

      議員活動や、その裏で活用している技術について、誰かの役に立つかもしれないことを中心にブログにしていきます😁

      diff --git "a/build/blog/tags/\343\201\276\343\201\241\343\201\245\343\201\217\343\202\212/index.html" "b/build/blog/tags/\343\201\276\343\201\241\343\201\245\343\201\217\343\202\212/index.html" index 5fbd58be..92a4a22b 100644 --- "a/build/blog/tags/\343\201\276\343\201\241\343\201\245\343\201\217\343\202\212/index.html" +++ "b/build/blog/tags/\343\201\276\343\201\241\343\201\245\343\201\217\343\202\212/index.html" @@ -2,7 +2,7 @@ - + 「まちづくり」タグの記事が2件件あります | 東京都小平市議・安竹洋平のまとめ @@ -12,13 +12,13 @@ - - - + + + -

      「まちづくり」タグの記事が2件件あります

      全てのタグを見る

      合気公園の整備進捗状況

      · 約6分
      安竹 洋平
      東京都小平市議会議員

      小平市上水南町二丁目の佐川幸義邸跡地に新設される合気公園の整備状況を見てきましたので、写真と動画で現在の様子をお知らせします。

      +

      「まちづくり」タグの記事が2件件あります

      全てのタグを見る
      \ No newline at end of file diff --git "a/build/blog/tags/\344\275\220\345\267\235\345\271\270\347\276\251\346\260\217/index.html" "b/build/blog/tags/\344\275\220\345\267\235\345\271\270\347\276\251\346\260\217/index.html" index fef321ef..a75aaa26 100644 --- "a/build/blog/tags/\344\275\220\345\267\235\345\271\270\347\276\251\346\260\217/index.html" +++ "b/build/blog/tags/\344\275\220\345\267\235\345\271\270\347\276\251\346\260\217/index.html" @@ -2,7 +2,7 @@ - + 「佐川幸義氏」タグの記事が2件件あります | 東京都小平市議・安竹洋平のまとめ @@ -12,13 +12,13 @@ - - - + + + -

      「佐川幸義氏」タグの記事が2件件あります

      全てのタグを見る

      合気公園の整備進捗状況

      · 約6分
      安竹 洋平
      東京都小平市議会議員

      小平市上水南町二丁目の佐川幸義邸跡地に新設される合気公園の整備状況を見てきましたので、写真と動画で現在の様子をお知らせします。

      +

      「佐川幸義氏」タグの記事が2件件あります

      全てのタグを見る
      \ No newline at end of file diff --git "a/build/blog/tags/\345\205\254\345\234\222/index.html" "b/build/blog/tags/\345\205\254\345\234\222/index.html" index 729e5877..d5feefe4 100644 --- "a/build/blog/tags/\345\205\254\345\234\222/index.html" +++ "b/build/blog/tags/\345\205\254\345\234\222/index.html" @@ -2,7 +2,7 @@ - + 「公園」タグの記事が2件件あります | 東京都小平市議・安竹洋平のまとめ @@ -12,13 +12,13 @@ - - - + + + -

      「公園」タグの記事が2件件あります

      全てのタグを見る

      合気公園の整備進捗状況

      · 約6分
      安竹 洋平
      東京都小平市議会議員

      小平市上水南町二丁目の佐川幸義邸跡地に新設される合気公園の整備状況を見てきましたので、写真と動画で現在の様子をお知らせします。

      +

      「公園」タグの記事が2件件あります

      全てのタグを見る
      \ No newline at end of file diff --git "a/build/blog/tags/\345\220\210\346\260\227\345\205\254\345\234\222/index.html" "b/build/blog/tags/\345\220\210\346\260\227\345\205\254\345\234\222/index.html" index c4e05143..01a28bd2 100644 --- "a/build/blog/tags/\345\220\210\346\260\227\345\205\254\345\234\222/index.html" +++ "b/build/blog/tags/\345\220\210\346\260\227\345\205\254\345\234\222/index.html" @@ -2,7 +2,7 @@ - + 「合気公園」タグの記事が2件件あります | 東京都小平市議・安竹洋平のまとめ @@ -12,13 +12,13 @@ - - - + + + -

      「合気公園」タグの記事が2件件あります

      全てのタグを見る

      合気公園の整備進捗状況

      · 約6分
      安竹 洋平
      東京都小平市議会議員

      小平市上水南町二丁目の佐川幸義邸跡地に新設される合気公園の整備状況を見てきましたので、写真と動画で現在の様子をお知らせします。

      +

      「合気公園」タグの記事が2件件あります

      全てのタグを見る
      \ No newline at end of file diff --git "a/build/blog/tags/\346\212\200\350\241\223/index.html" "b/build/blog/tags/\346\212\200\350\241\223/index.html" index a5e5bdfb..c6b40dcf 100644 --- "a/build/blog/tags/\346\212\200\350\241\223/index.html" +++ "b/build/blog/tags/\346\212\200\350\241\223/index.html" @@ -2,7 +2,7 @@ - + 「技術」タグの記事が1件件あります | 東京都小平市議・安竹洋平のまとめ @@ -12,12 +12,12 @@ - - - + + + -

      「技術」タグの記事が1件件あります

      全てのタグを見る

      Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法

      · 約17分
      安竹 洋平
      東京都小平市議会議員

      Docusaurus🦖

      +

      「技術」タグの記事が1件件あります

      全てのタグを見る

      Docusaurusの注意書きや警告文のタイトルを見出しにして、目次にも乗せる方法

      · 約17分
      安竹 洋平
      東京都小平市議会議員

      Docusaurus🦖

      1か月弱使いましたがこのDocusaurus(ドキュサウルス)は数あるCMSの中でも秀逸です。

      文書作成と管理が容易で、拡張の自由度も非常に高く、完全なオープンソース。

      議員活動に重要な「資料を作成しまとめて公開するツール」として現状の最適解と感じます。

      -- cgit v1.2.3-54-g00ecf