Skip to content

tag ページ生成機能 (仮) を追加する#44

Merged
zztkm merged 7 commits intomainfrom
feature/tag
Jan 4, 2026
Merged

tag ページ生成機能 (仮) を追加する#44
zztkm merged 7 commits intomainfrom
feature/tag

Conversation

@zztkm
Copy link
Copy Markdown
Member

@zztkm zztkm commented Jan 4, 2026

No description provided.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @zztkm, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

このプルリクエストは、ブログ記事にタグ付け機能を追加し、それらのタグに基づいて動的にタグページを生成する新機能を導入します。これにより、サイト訪問者は特定のトピックに関連する記事を簡単に閲覧できるようになります。設定ファイルを通じて機能の挙動をカスタマイズでき、既存の投稿レイアウトにもタグ表示が統合されます。

Highlights

  • タグページ生成機能の追加: Markdownファイルに定義されたタグに基づいて、自動的にタグページを生成する機能が追加されました。この機能は実験的なものです。
  • 設定の拡張: vss.toml[build.tags]セクションが追加され、タグページの有効化、使用するテンプレート、URLパターンを設定できるようになりました。
  • タグ表示の強化: 個別の投稿ページに、関連するタグを表示し、それぞれのタグページへのリンクを提供する機能が追加されました。
  • 新しいテンプレートとスタイル: タグページ用の新しいHTMLテンプレート(example_site/layouts/tags/default.html)と、タグ表示およびタグページリスト用のCSSスタイルが追加されました。
  • 内部処理の更新: ビルドプロセスが更新され、Markdownファイルから投稿メタデータを収集し、それを利用してタグページを生成するロジックがsrc/subcommand_build.rsに実装されました。

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

このプルリクエストは、タグに基づいて静的ページを生成する新機能を追加するもので、素晴らしい追加機能だと思います。コード全体はよく書かれていますが、特にパフォーマンスと堅牢性に関して改善できる点がいくつか見つかりました。具体的には、Rustコード内で不要なデータのクローンが複数箇所で行われており、これを参照を使うようにリファクタリングすることで効率を向上できます。また、タグページのURLパターンを処理するロジックが特定の形式に依存しすぎているため、より柔軟な形式に対応できるように修正することを提案します。テンプレート側でも、アクセシビリティの向上や、タグがない場合に不要なHTML要素がレンダリングされる問題について修正案を提示しました。これらの点を修正することで、より高品質で効率的な機能になるでしょう。

Comment thread src/subcommand_build.rs
Comment thread src/subcommand_build.rs Outdated
Comment on lines +508 to +511
let tag_path_str = config.build.tags.url_pattern.replace("{tag}", &tag_name);
let tag_path = tag_path_str.trim_start_matches('/').trim_end_matches('/');

let output_path = Path::new(&config.dist).join(tag_path).join("index.html");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

タグページの出力パスを生成する現在のロジックは、設定のurl_patternが常に/で終わることを前提としています。例えば、ユーザーが/tags/{tag}.htmlのようなパターンを設定した場合、dist/tags/some-tag.html/index.htmlという不正なパスが生成されてしまいます。

より堅牢にするために、パスが/で終わるかどうかをチェックし、それに応じてindex.htmlを追記するかどうかを決定するロジックに変更することをお勧めします。

        let tag_path_str = config.build.tags.url_pattern.replace("{tag}", &tag_name);
        let relative_path = tag_path_str.trim_start_matches('/');

        let output_path = if relative_path.ends_with('/') {
            Path::new(&config.dist).join(relative_path).join("index.html")
        } else {
            Path::new(&config.dist).join(relative_path)
        };

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正した

Comment on lines +19 to +26
<div class="tags-section">
<h3>Tags</h3>
<ul class="tag-list">
{{#tags}}
<li><a href="{{url}}" class="tag-link">{{name}}</a></li>
{{/tags}}
</ul>
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

現在の実装では、投稿にタグがない場合でも「Tags」という見出しと空のリスト(<ul>)が表示されてしまいます。
ramhornsテンプレートでは、リストが空でない場合にのみブロックをレンダリングする機能があります。このセクション全体を条件ブロックで囲むことで、タグが存在する場合にのみ表示されるように修正することをお勧めします。

この修正を有効にするには、バックエンドのRenderContext構造体のtagsフィールドをVec<Tag>からOption<Vec<Tag>>に変更する必要があります。そして、タグのリストが空の場合はNoneを、そうでない場合はSome(tags)をコンテキストに渡してください。ramhornsOptionを正しく解釈し、Someの場合のみブロックをレンダリングします。

テンプレートの修正案は以下の通りです。{{#tags}}Optionをチェックし、{{#.}}Vecをイテレートします。

    {{#tags}}
    <div class="tags-section">
        <h3>Tags</h3>
        <ul class="tag-list">
            {{#.}}
            <li><a href="{{url}}" class="tag-link">{{name}}</a></li>
            {{/.}}
        </ul>
    </div>
    {{/tags}}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正した

Comment thread example_site/layouts/tags/default.html Outdated
Comment thread src/subcommand_build.rs
@zztkm

This comment was marked as off-topic.

@zztkm
Copy link
Copy Markdown
Member Author

zztkm commented Jan 4, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

このプルリクエストでは、タグページの生成機能が追加されています。全体的にうまく実装されていますが、いくつか改善できる点があります。具体的には、投稿テンプレートでのタグ表示ロジックのバグ、およびタグページ生成時のパフォーマンスに関する改善提案をコメントしました。これらの点を修正することで、より堅牢で効率的な機能になるでしょう。

Comment thread example_site/layouts/posts/default.html Outdated
Comment on lines +19 to +28
{{#tags}}
<div class="tags-section">
<h3>Tags</h3>
<ul class="tag-list">
{{#tags}}
<li><a href="{{url}}" class="tag-link">{{name}}</a></li>
{{/tags}}
</ul>
</div>
{{/tags}}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

このMustacheテンプレートのタグセクションの実装に問題があります。現在の実装では、投稿に複数のタグがある場合、タグの数だけ「Tags」セクションが繰り返し表示されてしまいます。また、各セクション内のタグリストは空になります。

{{#tags}}tags 配列を反復処理するため、外側の {{#tags}} ブロックがタグごとに繰り返されます。内側の {{#tags}} は、反復中のコンテキスト(単一の Tag オブジェクト)に tags プロパティがないため、何もレンダリングしません。

タグが存在する場合にのみセクションを表示し、その中にすべてのタグをリストするには、ramhornsが提供する _first_last というヘルパー変数を使うのが良い方法です。これにより、ループの最初と最後の要素に対してのみラッパー要素を出力できます。

    {{#tags}}
        {{#_first}}
    <div class="tags-section">
        <h3>Tags</h3>
        <ul class="tag-list">
        {{/_first}}
            <li><a href="{{url}}" class="tag-link">{{name}}</a></li>
        {{#_last}}
        </ul>
    </div>
        {{/_last}}
    {{/tags}}

Comment thread src/subcommand_build.rs
Comment on lines +146 to 153
#[derive(Content)]
struct TagPageContext {
site_title: String,
site_description: String,
base_url: String,
tag_name: String,
posts: Vec<PostMetadata>,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

パフォーマンス向上のため、TagPageContextPostMetadata のベクタを所有する代わりに、参照を保持するように変更することをお勧めします。これにより、generate_tag_pages 関数内で PostMetadata オブジェクトを複数回クローンすることを避けられます。この変更は、後述する generate_tag_pages 内のループの変更と合わせて行ってください。

Suggested change
#[derive(Content)]
struct TagPageContext {
site_title: String,
site_description: String,
base_url: String,
tag_name: String,
posts: Vec<PostMetadata>,
}
#[derive(Content)]
struct TagPageContext<'a> {
site_title: String,
site_description: String,
base_url: String,
tag_name: String,
posts: Vec<&'a PostMetadata>,
}

Comment thread src/subcommand_build.rs
Comment on lines +472 to +481
let mut tag_to_posts: HashMap<String, Vec<PostMetadata>> = HashMap::new();

for post in all_posts {
for tag_name in &post.tags {
tag_to_posts
.entry(tag_name.clone())
.or_default()
.push(post.clone());
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

現在の実装では、投稿が複数のタグを持つ場合に PostMetadata オブジェクトが複数回クローンされています。これは、特に投稿数やタグ数が多い場合にパフォーマンスの低下を招く可能性があります。PostMetadata のクローンを避けるために、参照を HashMap に格納するように変更することをお勧めします。これにより、メモリ使用量と処理時間を削減できます。この変更を行うには、先に提案したように TagPageContext の定義も変更する必要があります。

Suggested change
let mut tag_to_posts: HashMap<String, Vec<PostMetadata>> = HashMap::new();
for post in all_posts {
for tag_name in &post.tags {
tag_to_posts
.entry(tag_name.clone())
.or_default()
.push(post.clone());
}
}
let mut tag_to_posts: HashMap<String, Vec<&PostMetadata>> = HashMap::new();
for post in &all_posts {
for tag_name in &post.tags {
tag_to_posts
.entry(tag_name.clone())
.or_default()
.push(post);
}
}

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

このプルリクエストは、タグに基づいて静的ページを生成する新機能を追加するものです。全体として、機能追加に必要なコード、設定、テンプレート、およびサンプルサイトの更新が含まれており、うまく実装されています。
いくつか改善の余地がある点についてコメントしました。特に、ramhornsテンプレートの構文に関するバグの修正と、パフォーマンス改善、コードの可読性向上に関する提案です。
これらの点をご確認ください。

Comment thread example_site/layouts/posts/default.html Outdated
Comment on lines +19 to +28
{{#tags}}
<div class="tags-section">
<h3>Tags</h3>
<ul class="tag-list">
{{#tags}}
<li><a href="{{url}}" class="tag-link">{{name}}</a></li>
{{/tags}}
</ul>
</div>
{{/tags}}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

ramhornsテンプレートでは、Vecに対する{{#tags}}はイテレータとして機能します。現在のネストした{{#tags}}では、外側のループがタグごとにdivを繰り返し描画し、内側のループはTagオブジェクトにtagsプロパティがないため何も描画しない、という意図しない動作になります。
タグがある場合にのみセクション全体を表示したい場合は、ramhornsの現在の機能ではテンプレートのみでの実現は難しいため、まずはタグリストが正しく描画されるように修正することを提案します。
この修正により、タグがない場合でも<h3>Tags</h3>が表示されるようになりますが、これはCSSで非表示にするか、Rust側でコンテキストにhas_tagsのようなフラグを追加することで将来的には解決できます。

    <div class="tags-section">
        <h3>Tags</h3>
        <ul class="tag-list">
            {{#tags}}
            <li><a href="{{url}}" class="tag-link">{{name}}</a></li>
            {{/tags}}
        </ul>
    </div>

Comment thread src/subcommand_build.rs
Comment on lines +474 to +481
for post in all_posts {
for tag_name in &post.tags {
tag_to_posts
.entry(tag_name.clone())
.or_default()
.push(post.clone());
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

このループでは、投稿が持つタグの数だけ post.clone() が呼ばれ、PostMetadata のディープコピーが発生します。投稿数やタグ数が多くなると、このクローン処理がパフォーマンスのボトルネックになる可能性があります。

改善案として、PostMetadatastd::rc::Rc でラップすることを検討してみてください。これにより、高価なクローンを避け、参照カウントによる軽量なポインターのコピーで済むようになります。

例:

use std::rc::Rc;
...
// all_posts を Vec<Rc<PostMetadata>> に変換
let all_posts_rc: Vec<_> = all_posts.into_iter().map(Rc::new).collect();
let mut tag_to_posts: HashMap<String, Vec<Rc<PostMetadata>>> = HashMap::new();
for post_rc in &all_posts_rc {
    for tag_name in &post_rc.tags {
        tag_to_posts.entry(tag_name.clone()).or_default().push(post_rc.clone());
    }
}

この変更には TagPageContextposts フィールドの型変更も必要になります。

Comment thread src/subcommand_build.rs
Comment on lines +513 to +518
let output_path = if relative_path.ends_with('/') {
let tag_path = tag_path_str.trim_start_matches('/').trim_end_matches('/');
Path::new(&config.dist).join(tag_path).join("index.html")
} else {
Path::new(&config.dist).join(relative_path)
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

このパス生成ロジックはよりシンプルに書くことができます。
relative_path は末尾に / を含んでいる場合、Path::join はそれをディレクトリとして正しく解釈します。
そのため、tag_path_str から再度パスを組み立てる必要はなく、relative_path を直接利用することでコードが簡潔になります。

        let output_path = if relative_path.ends_with('/') {
            Path::new(&config.dist).join(relative_path).join("index.html")
        } else {
            Path::new(&config.dist).join(relative_path)
        };

@zztkm zztkm merged commit 799ef38 into main Jan 4, 2026
2 checks passed
@zztkm zztkm deleted the feature/tag branch January 4, 2026 11:38
@zztkm zztkm restored the feature/tag branch January 4, 2026 11:39
@zztkm zztkm deleted the feature/tag branch January 4, 2026 11:39
@zztkm zztkm restored the feature/tag branch January 4, 2026 12:08
@zztkm zztkm deleted the feature/tag branch January 4, 2026 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant