今回は、このブログ「還暦ハック」の執筆環境に起きた、小さな革命の話をします。
お父さん長かった……。本当に長かったぞ、巧士。



はい。ついに私たちが抱えていた「AIがタグを食べちゃう問題」を、技術の力で解決しましたね!
目次
AIは「見えない文字」が嫌い?
私は現在、記事の執筆パートナーとしてGoogleのAI(Gemini)を活用しています。しかし、彼にはSWELL使いにとって致命的な弱点がありました。それは、「HTMLコメントタグを勝手に消してしまう」ことです。
逆転の発想:AIにコードを書かせなければいい
STEP
課題
AIに無理やりHTMLタグを出力させようとするから消える。
STEP
発想
なら、AIには「データ(JSON)」だけを作らせればいい。
STEP
解決
そのデータを、こちらのPC(Python)で受け取って、コードに変換すればいい!
【サンプル】あなたも使える『SWELL Maker』コード
以下は、実際に動いているPythonコードです。Streamlit環境があればコピペで動きます。
(※あくまで、2025/12 のSWELL, WordPress の記述準拠です。バージョン対応は各自でお願いします。)
import streamlit as st
import json
import csv
import io
# ==========================================
# 設定エリア:環境に合わせて変更可能
# ==========================================
BALLOON_ID_L = "1" # 左側 (dad)
BALLOON_ID_R = "2" # 右側 (takuto)
# ==========================================
# 1. 定数定義(タグ消失回避トリック)
# ==========================================
WP_START = "<" + "!-- wp:"
WP_END = "<" + "!-- /wp:"
TAG_CLOSE = " -->"
# ==========================================
# 2. ブロック生成ロジック (正解データ準拠)
# ==========================================
def get_balloon(speaker_type, text):
"""ふきだし"""
if speaker_type in ["dad", "left"]:
b_id = BALLOON_ID_L
else:
b_id = BALLOON_ID_R
return (
f'{WP_START}loos/balloon {{"balloonID":"{b_id}"}}{TAG_CLOSE}\n'
f'<p>{text}</p>\n'
f'{WP_END}loos/balloon{TAG_CLOSE}'
)
def get_heading(level, text):
"""見出し (h2, h3)"""
return (
f'{WP_START}heading {{"level":{level}}}{TAG_CLOSE}\n'
f'<h{level} class="wp-block-heading">{text}</h{level}>\n'
f'{WP_END}heading{TAG_CLOSE}'
)
def get_step(items):
"""
ステップ (SWELL正解データ準拠)
中身のテキストも wp:paragraph で包まないと無効化されるため修正
"""
steps_html = ""
# 親コンテナ
start_tag = f'{WP_START}loos/step{TAG_CLOSE}\n<div class="swell-block-step" data-num-style="circle">'
for item in items:
label = item.get('label', 'STEP')
title = item.get('title', '')
body = item.get('body', '')
step_item = (
f'{WP_START}loos/step-item {{"stepLabel":"{label}"}}{TAG_CLOSE}\n'
f'<div class="swell-block-step__item">'
f'<div class="swell-block-step__number u-bg-main"><span class="__label">{label}</span></div>'
f'<div class="swell-block-step__title u-fz-l">{title}</div>'
f'<div class="swell-block-step__body">\n'
f'{WP_START}paragraph{TAG_CLOSE}\n<p>{body}</p>\n{WP_END}paragraph{TAG_CLOSE}\n'
f'</div>'
f'</div>\n'
f'{WP_END}loos/step-item{TAG_CLOSE}\n'
)
steps_html += step_item
end_tag = f'</div>\n{WP_END}loos/step{TAG_CLOSE}'
return f"{start_tag}\n{steps_html}\n{end_tag}"
def get_code_block(code_text):
"""
ソースコードブロック (wp:code)
エスケープ処理は簡易的ですが、構造を正解データに合わせました
"""
# 簡易エスケープ (HTMLタグがコードに含まれる場合のため)
escaped_code = code_text.replace("<", "<").replace(">", ">")
return (
f'{WP_START}code{TAG_CLOSE}\n'
f'<pre class="wp-block-code"><code>{escaped_code}</code></pre>\n'
f'{WP_END}code{TAG_CLOSE}'
)
def get_table(csv_text):
"""テーブル (CSV変換)"""
try:
if "\t" in csv_text: csv_text = csv_text.replace("\t", ",")
f = io.StringIO(csv_text.strip())
reader = csv.reader(f)
rows = list(reader)
if not rows: return "(データなし)"
thead = "".join([f"<th>{c}</th>" for c in rows[0]])
tbody = ""
for row in rows[1:]:
row_html = "".join([f"<td>{c}</td>" for c in row])
tbody += f"<tr>{row_html}</tr>"
return (
f'{WP_START}table {{"hasFixedLayout":false,"swlScrollable":"both","swlTableWidth":"100%"}}{TAG_CLOSE}\n'
'<figure class="wp-block-table"><table>\n'
f'<thead><tr>{thead}</tr></thead>\n'
f'<tbody>{tbody}</tbody>\n'
'</table></figure>\n'
f'{WP_END}table{TAG_CLOSE}'
)
except Exception as e:
return f"⚠️ CSV変換エラー: {str(e)}"
# ==========================================
# 3. メイン変換処理
# ==========================================
def convert_json_to_swell(json_data):
output = []
try:
data_list = json.loads(json_data)
if not isinstance(data_list, list): return "⚠️ エラー: リスト形式 [...] で入力してください。"
for block in data_list:
b_type = block.get('type')
if b_type == 'h2': output.append(get_heading(2, block.get('text', '')))
elif b_type == 'h3': output.append(get_heading(3, block.get('text', '')))
elif b_type == 'p':
text = block.get('text', '')
output.append(f'{WP_START}paragraph{TAG_CLOSE}\n<p>{text}</p>\n{WP_END}paragraph{TAG_CLOSE}')
elif b_type in ['dad', 'left', 'takuto', 'right']:
output.append(get_balloon(b_type, block.get('text', '')))
elif b_type == 'step': output.append(get_step(block.get('items', [])))
elif b_type == 'code': output.append(get_code_block(block.get('text', ''))) # 新設
elif b_type == 'table': output.append(get_table(block.get('csv', '')))
return "\n\n".join(output)
except json.JSONDecodeError as e:
return f"⚠️ JSON解析エラー: \n{str(e)}"
# ==========================================
# 4. Streamlit UI
# ==========================================
st.set_page_config(layout="wide", page_title="SWELL Maker")
st.title("🦍 SWELL Maker (v2.0 Fixed)")
col1, col2 = st.columns(2)
with col1:
st.subheader("📥 Input (JSON)")
input_text = st.text_area("JSON貼り付け", height=600)
with col2:
st.subheader("📤 Output (HTML Code)")
if input_text:
converted = convert_json_to_swell(input_text)
if "⚠️" in converted:
st.error(converted)
else:
st.code(converted, language='html')

