GeminiにHAIKのプラグインを作ってもらう

chart プラグイン

実行例1

月別売上推移

使い方1

#chart(line, 月別売上推移){{
,山田,田中,吉田
4月, 120, 150,158
5月, 135, 180,177
6月, 150, 170,161
7月, 180, 210,170
8月, 110,166,156
9月, 130,140,175
}}

実行例2

月別売上推移

使い方2

#chart(bar, 月別売上推移){{
,山田,田中,吉田
4月, 120, 150,158
5月, 135, 180,177
6月, 150, 170,161
7月, 180, 210,170
8月, 110,166,156
9月, 130,140,175
}}

chart.inc.php ・・・ プラグインソースコード

<?php
/**
 * PukiWiki/HAIK用 カンマ区切りグラフ描画プラグイン
 * ヘッダー行対応版
 */
 
function plugin_chart_convert() {
    global $vars;
    $args = func_get_args();
    $type = isset($args[0]) ? $args[0] : 'line';
    $title = isset($args[1]) ? $args[1] : '';
 
    // HAIKのソース走査(ブロックボディ抽出)
    $lines = get_source($vars['page']);
    $body_lines = [];
    $found = false;
    foreach ($lines as $line) {
        if (preg_match('/^#chart/', $line)) { $found = true; continue; }
        if ($found && preg_match('/^}}$/', trim($line))) break;
        if ($found) $body_lines[] = $line;
    }
 
    $labels = []; $series = []; $series_names = [];
    
    foreach ($body_lines as $idx => $line) {
        $line = trim($line);
        if ($line === '') continue;
        $cols = str_getcsv($line);
        
        // 1行目がヘッダー(1列目が空か、数値以外)の場合の処理
        if ($idx === 0 && !is_numeric($cols[1])) {
            array_shift($cols); // 先頭の空列を除く
            $series_names = $cols;
            continue;
        }
 
        $labels[] = array_shift($cols);
        foreach ($cols as $i => $val) {
            $series[$i][] = floatval(trim($val));
        }
    }
 
    $chart_id = 'chart_' . md5(uniqid(mt_rand(), true));
    
    // JSON生成
    $js_datasets = [];
    $colors = ['54, 162, 235', '255, 99, 132', '75, 192, 192', '255, 159, 64'];
    foreach ($series as $i => $data) {
        $name = isset($series_names[$i]) ? $series_names[$i] : '系列 ' . ($i + 1);
        $color = $colors[$i % count($colors)];
        $js_datasets[] = "{
            label: '{$name}',
            data: " . json_encode($data) . ",
            borderColor: 'rgba({$color}, 1)',
            backgroundColor: 'rgba({$color}, 0.2)',
            borderWidth: 2,
            tension: 0.3
        }";
    }
 
    $output = '<div style="width:100%; max-width:800px; margin:20px auto;">';
    if ($title) $output .= '<h5 style="text-align:center;background-color:#f0f0f0;padding:10px;font-size:1.2em;">' . htmlspecialchars($title) . '</h5>';
    $output .= '<canvas id="'.$chart_id.'"></canvas></div>';
    $output .= '<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>';
    $output .= "<script>
    document.addEventListener('DOMContentLoaded', function(){
        new Chart(document.getElementById('{$chart_id}'), {
            type: '{$type}',
            data: {
                labels: " . json_encode($labels, JSON_UNESCAPED_UNICODE) . ",
                datasets: [" . implode(',', $js_datasets) . "]
            },
            options: { responsive: true }
        });
    });
    </script>";
    return $output;
}