1
0
moji/scpt/ai_moji_generator/css_generator.py
2025-06-06 03:53:38 +09:00

256 lines
7.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
AI Moji CSS Generator
フォントメタデータからCSS/SCSSファイルを自動生成
"""
import json
from pathlib import Path
from typing import Dict, List
from datetime import datetime
class AIMojiCSSGenerator:
def __init__(self, output_dir: str = "../../dist"):
self.output_dir = Path(output_dir)
self.css_dir = self.output_dir / "css"
self.scss_dir = self.output_dir / "scss"
self.metadata_file = self.output_dir / "metadata.json"
# ディレクトリ作成
self.css_dir.mkdir(parents=True, exist_ok=True)
self.scss_dir.mkdir(parents=True, exist_ok=True)
def load_metadata(self) -> Dict:
"""メタデータファイルを読み込み"""
if not self.metadata_file.exists():
raise FileNotFoundError(f"メタデータファイルが見つかりません: {self.metadata_file}")
with open(self.metadata_file, 'r', encoding='utf-8') as f:
return json.load(f)
def generate_css_template(self, metadata: Dict) -> str:
"""FontAwesome風CSSテンプレート生成"""
font_family = metadata["font_family"]
css_prefix = metadata["css_prefix"]
css_content = f"""/*!
* AI Moji Icons v{metadata["version"]}
* Generated on {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
* {metadata["total_icons"]} icons in the set
*/
@font-face {{
font-family: '{font_family}';
src: url('../fonts/{font_family}.eot');
src: url('../fonts/{font_family}.eot?#iefix') format('embedded-opentype'),
url('../fonts/{font_family}.woff2') format('woff2'),
url('../fonts/{font_family}.woff') format('woff'),
url('../fonts/{font_family}.ttf') format('truetype'),
url('../fonts/{font_family}.svg#{font_family}') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
}}
.{css_prefix} {{
/* use !important to prevent issues with browser extensions that change fonts */
font-family: '{font_family}' !important;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}}
/* Individual Icon Classes */
"""
# 各アイコンのCSSクラス生成
for icon in metadata["icons"]:
css_content += f'.{icon["css_class"]}::before {{\n'
css_content += f' content: "\\{icon["unicode"][2:]}";\n'
css_content += '}\n\n'
return css_content
def generate_scss_variables(self, metadata: Dict) -> str:
"""SCSS変数ファイル生成"""
scss_content = f"""//
// AI Moji Icons Variables
// Generated on {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
//
// Font Configuration
$aimoji-font-family: '{metadata["font_family"]}' !default;
$aimoji-font-path: '../fonts' !default;
$aimoji-css-prefix: '{metadata["css_prefix"]}' !default;
$aimoji-version: '{metadata["version"]}' !default;
// Unicode Variables
"""
# 各アイコンのUnicode変数
for icon in metadata["icons"]:
var_name = icon["name"].replace("-", "_").replace(".", "_")
scss_content += f'$aimoji-{var_name}: "\\{icon["unicode"][2:]}";\n'
scss_content += "\n// Icon Map for iteration\n"
scss_content += "$aimoji-icons: (\n"
for i, icon in enumerate(metadata["icons"]):
var_name = icon["name"].replace("-", "_").replace(".", "_")
comma = "," if i < len(metadata["icons"]) - 1 else ""
scss_content += f' "{icon["name"]}": $aimoji-{var_name}{comma}\n'
scss_content += ");\n"
return scss_content
def generate_scss_mixins(self) -> str:
"""SCSS mixinファイル生成"""
return """//
// AI Moji Icons Mixins
//
// Base icon mixin
@mixin aimoji-base() {
font-family: $aimoji-font-family;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
// Icon content mixin
@mixin aimoji-icon($unicode) {
@include aimoji-base();
&::before {
content: $unicode;
}
}
// Size mixins
@mixin aimoji-size($size) {
font-size: $size;
line-height: 1;
}
// Rotation mixins
@mixin aimoji-rotate($degrees) {
transform: rotate(#{$degrees}deg);
}
@mixin aimoji-flip-horizontal() {
transform: scaleX(-1);
}
@mixin aimoji-flip-vertical() {
transform: scaleY(-1);
}
// Animation mixins
@mixin aimoji-spin() {
animation: aimoji-spin 2s infinite linear;
}
@keyframes aimoji-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(359deg); }
}
@mixin aimoji-pulse() {
animation: aimoji-pulse 1s infinite;
}
@keyframes aimoji-pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
"""
def minify_css(self, css_content: str) -> str:
"""CSS最小化簡易版"""
import re
# コメント削除
css_content = re.sub(r'/\*.*?\*/', '', css_content, flags=re.DOTALL)
# 余分な空白削除
css_content = re.sub(r'\s+', ' ', css_content)
# セミコロン前後の空白削除
css_content = re.sub(r'\s*;\s*', ';', css_content)
# ブレース前後の空白削除
css_content = re.sub(r'\s*{\s*', '{', css_content)
css_content = re.sub(r'\s*}\s*', '}', css_content)
return css_content.strip()
def generate_all(self) -> Dict:
"""全CSSファイル生成"""
print("📝 CSS/SCSSファイル生成開始...")
# メタデータ読み込み
metadata = self.load_metadata()
# CSS生成
css_content = self.generate_css_template(metadata)
css_file = self.css_dir / "aimoji.css"
with open(css_file, 'w', encoding='utf-8') as f:
f.write(css_content)
print(f"✅ CSS生成完了: {css_file}")
# CSS最小化版生成
css_min_content = self.minify_css(css_content)
css_min_file = self.css_dir / "aimoji.min.css"
with open(css_min_file, 'w', encoding='utf-8') as f:
f.write(css_min_content)
print(f"✅ CSS最小化版生成完了: {css_min_file}")
# SCSS変数生成
scss_vars = self.generate_scss_variables(metadata)
scss_vars_file = self.scss_dir / "_variables.scss"
with open(scss_vars_file, 'w', encoding='utf-8') as f:
f.write(scss_vars)
print(f"✅ SCSS変数生成完了: {scss_vars_file}")
# SCSS mixins生成
scss_mixins = self.generate_scss_mixins()
scss_mixins_file = self.scss_dir / "_mixins.scss"
with open(scss_mixins_file, 'w', encoding='utf-8') as f:
f.write(scss_mixins)
print(f"✅ SCSS mixins生成完了: {scss_mixins_file}")
return {
"css_file": str(css_file),
"css_min_file": str(css_min_file),
"scss_vars_file": str(scss_vars_file),
"scss_mixins_file": str(scss_mixins_file),
"total_icons": metadata["total_icons"]
}
def main():
"""メイン実行関数"""
try:
generator = AIMojiCSSGenerator()
result = generator.generate_all()
print("\n🎨 CSS/SCSS生成完了!")
print(f"📊 総アイコン数: {result['total_icons']}")
except Exception as e:
print(f"❌ エラーが発生しました: {e}")
return 1
return 0
if __name__ == "__main__":
exit(main())