This file is a merged representation of the entire codebase, combined into a single document by Repomix.
================================================================
File Summary
================================================================
Purpose:
--------
This file contains a packed representation of the entire repository's contents.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.
File Format:
------------
The content is organized as follows:
1. This summary section
2. Repository information
3. Directory structure
4. Repository files (if enabled)
4. Multiple file entries, each consisting of:
a. A separator line (================)
b. The file path (File: path/to/file)
c. Another separator line
d. The full contents of the file
e. A blank line
Usage Guidelines:
-----------------
- This file should be treated as read-only. Any changes should be made to the
original repository files, not this packed version.
- When processing this file, use the file path to distinguish
between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
the same level of security as you would the original repository.
Notes:
------
- Some files may have been excluded based on .gitignore rules and Repomix's configuration
- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Files are sorted by Git change count (files with more changes are at the bottom)
Additional Info:
----------------
================================================================
Directory Structure
================================================================
public/
file.svg
globe.svg
next.svg
vercel.svg
window.svg
src/
app/
about/
page.tsx
ai-post/
page.tsx
components/
background/
text-background.tsx
layout/
footer.tsx
header.tsx
index.ts
theme-toggle.tsx
contact/
page.tsx
contexts/
theme.tsx
post/
[slug]/
page.tsx
card.tsx
markdown-content.tsx
page.tsx
utils/
post.ts
globals.css
layout.tsx
page.tsx
mastra/
agents/
index.ts
tools/
index.ts
types/
daily-papers.ts
workflows/
daily-papers-workflow.ts
index.ts
weather-workflow.ts
index.ts
post/
ai-future.md
generative-models.md
.gitignore
next.config.ts
package.json
postcss.config.mjs
README.md
tsconfig.json
================================================================
Files
================================================================
================
File: public/file.svg
================
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
================
File: public/globe.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
================
File: public/next.svg
================
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
================
File: public/vercel.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
================
File: public/window.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
================
File: src/app/about/page.tsx
================
export default function About() {
return (
<div className="h-screen w-screen">
<div className="w-8/12 mx-auto h-full flex flex-col justify-center items-center">
<div className="w-full flex flex-col mt-8">
<h2 className="text-xl font-bold glitch">About Me</h2>
<ul className="mt-2">
<li>名前: Reibniz</li>
<li>職業: データサイエンティストとか, いろいろ。</li>
<li>趣味: プログラミング, 3D制作</li>
<li>興味: 生成モデル, ベイズ推論, MMM</li>
</ul>
<h2 className="text-xl font-bold glitch mt-4">About This Site</h2>
<p className="mt-2">
このサイトは、思考やアイデアを試す, 遊び場として制作していますが,
ある種のポートフォリオとしても機能しています。
日々の学びや考えを記録し, 他の人と共有することで,
自分自身の成長を促進することを目的としています。
どこかの誰かの役に立てれば嬉しいです。
</p>
</div>
</div>
</div>
);
}
================
File: src/app/ai-post/page.tsx
================
export default function AiPostPage() {
return (
<div className="container max-w-11/12 mx-auto px-4 py-8">
<h1 className="text-2xl font-bold">AI Post</h1>
<p className="mt-4">
This is a placeholder for the AI post page. You can add your content
here.
</p>
</div>
);
}
================
File: src/app/components/background/text-background.tsx
================
const text = `This file is a merged representation of the entire codebase, combined into a single document by Repomix.
================================================================
File Summary
================================================================
Purpose:
--------
This file contains a packed representation of the entire repository's contents.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.
File Format:
------------
The content is organized as follows:
1. This summary section
2. Repository information
3. Directory structure
4. Repository files (if enabled)
4. Multiple file entries, each consisting of:
a. A separator line (================)
b. The file path (File: path/to/file)
c. Another separator line
d. The full contents of the file
e. A blank line
Usage Guidelines:
-----------------
- This file should be treated as read-only. Any changes should be made to the
original repository files, not this packed version.
- When processing this file, use the file path to distinguish
between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
the same level of security as you would the original repository.
Notes:
------
- Some files may have been excluded based on .gitignore rules and Repomix's configuration
- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Files are sorted by Git change count (files with more changes are at the bottom)
Additional Info:
----------------
================================================================
Directory Structure
================================================================
app/
components/
layout/
footer.tsx
header.tsx
index.ts
theme-toggle.tsx
post/
card.tsx
page.tsx
globals.css
layout.tsx
page.tsx
contexts/
theme.tsx
public/
file.svg
globe.svg
next.svg
vercel.svg
window.svg
.gitignore
.repomixignore
next.config.ts
package.json
postcss.config.mjs
README.md
tailwind.config.js
tsconfig.json
================================================================
Files
================================================================
================
File: app/components/layout/footer.tsx
================
"use client";
export function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="fixed top-0 left-0 h-screen flex flex-row [writing-mode:vertical-rl] justify-end text-xs">
<p className="font-serif text-gray-500 dark:text-gray-400">
©2024-{currentYear} Tomoya Hirakawa All rights reserved.
</p>
</footer>
);
}
================
File: app/components/layout/header.tsx
================
"use client";
import Link from "next/link";
export function Header() {
return (
<header className="fixed top-0 right-0 h-screen px-4 py-1 flex flex-row gap-5 items-center [writing-mode:vertical-rl] font-serif">
<Link href="/" className="text-lg font-bold">
Tomoya<span className="text-green-400">. </span>
Hirakawa
</Link>
<ul className="flex flex-row gap-3 grow justify-end">
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/post">Articles</a>
</li>
<li>
<a href="/">About</a>
</li>
</ul>
</header>
);
}
================
File: app/components/layout/index.ts
================
export * from "./header";
export * from "./footer";
export * from "./theme-toggle";
================
File: app/components/layout/theme-toggle.tsx
================
"use client";
import { Sun, Moon } from "lucide-react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
useEffect(() => {
console.log("Current theme:", theme);
setMounted(true);
}, [theme]);
if (!mounted) {
return null;
}
return (
<button className="fixed top-0 left-0 z-10 p-2">
{theme === "dark" ? (
<Sun
className="text-gray-300"
onClick={() => setTheme("light")}
size={24}
/>
) : (
<Moon className="text-gray-600" onClick={() => setTheme("dark")} />
)}
</button>
);
}
================
File: app/post/card.tsx
================
export function Card() {
return (
<div className="card">
<h2>Card Title</h2>
<p>This is a card component.</p>
</div>
);
}
================
File: app/post/page.tsx
================
import { Card } from "./card";
export default function Page() {
return (
<div className="@container mx-auto border">
<h1>Post List</h1>
<h1>Post</h1>
<Card />
</div>
);
}
================
File: app/globals.css
================
@import "tailwindcss";
@tailwind base;
@tailwind components;
@tailwind utilities;
@custom-variant dark (&:where(.dark, .dark *));
================
File: app/layout.tsx
================
import type { Metadata } from "next";
import "./globals.css";
import { Header, Footer, ThemeToggle } from "./components/layout";
import { ThemeProvider } from "@/contexts/theme";
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ja" suppressHydrationWarning>
<body className="antialiased">
<ThemeProvider>
<ThemeToggle />
<Header />
{children}
<Footer />
</ThemeProvider>
</body>
</html>
);
}
================
File: app/page.tsx
================
import { TextBackground } from "./components/background/text-background";
export default function Home() {
return <TextBackground />;
}
================
File: contexts/theme.tsx
================
"use client";
import { ThemeProvider as NextThemeProvider } from "next-themes";
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const defaultTheme = "system";
console.log("ThemeProvider initialized with defaultTheme:", defaultTheme);
return (
<NextThemeProvider
attribute="class"
defaultTheme={defaultTheme}
enableSystem
enableColorScheme
>
{children}
</NextThemeProvider>
);
}
================
File: public/file.svg
================
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
================
File: public/globe.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
================
File: public/next.svg
================
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
================
File: public/vercel.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
================
File: public/window.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
================
File: .gitignore
================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
================
File: .repomixignore
================
app/components/background/text-background.tsx
================
File: next.config.ts
================
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;
================
File: package.json
================
{
"name": "hirakawa-site",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"lucide-react": "^0.503.0",
"next": "15.3.1",
"next-themes": "^0.4.6",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"typescript": "^5"
}
}
================
File: postcss.config.mjs
================
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;
================
File: README.md
================
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
================
File: tailwind.config.js
================
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: "class",
content: [
"./app/**/*.{js,jsx,ts,tsx}",
"./contexts/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [
function ({ addVariant }) {
addVariant('debug', '&');
},
],
};
================
File: tsconfig.json
================
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
================================================================
End of Codebase
================================================================`;
export function TextBackground() {
return (
<div className="fixed top-0 left-0 w-full h-full text-xs -z-50 text-gray-200 dark:text-gray-700 select-none overflow-y-hidden">
{text}
</div>
);
}
================
File: src/app/components/layout/footer.tsx
================
"use client";
export function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="fixed top-0 left-0 h-screen flex flex-row [writing-mode:vertical-rl] justify-end text-xs">
<p className="font-serif text-gray-500 dark:text-gray-400">
©2024-{currentYear} Reibniz All rights reserved.
</p>
</footer>
);
}
================
File: src/app/components/layout/header.tsx
================
"use client";
import Link from "next/link";
export function Header() {
return (
<header className="fixed top-0 right-0 h-screen px-4 py-1 flex flex-row gap-5 items-center [writing-mode:vertical-rl] font-serif">
<Link href="/" className="text-lg font-bold">
遊び場 | ASOBIBA
</Link>
<ul className="flex flex-row gap-3 grow justify-end">
<li>
<a href="/about">About</a>
</li>
<li>
<a href="/contact">Contact</a>
</li>
<li>
<a href="/post">Post</a>
</li>
<li>
<a href="/ai-post">AI Post</a>
</li>
</ul>
</header>
);
}
================
File: src/app/components/layout/index.ts
================
export * from "./header";
export * from "./footer";
export * from "./theme-toggle";
================
File: src/app/components/layout/theme-toggle.tsx
================
"use client";
import { Sun, Moon } from "lucide-react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
useEffect(() => {
console.log("Current theme:", theme);
setMounted(true);
}, [theme]);
if (!mounted) {
return null;
}
return (
<button className="fixed top-0 left-0 z-10 p-2">
{theme === "dark" ? (
<Sun
className="text-gray-300"
onClick={() => setTheme("light")}
size={24}
/>
) : (
<Moon className="text-gray-600" onClick={() => setTheme("dark")} />
)}
</button>
);
}
================
File: src/app/contact/page.tsx
================
export default function ContactPage() {
return (
<>
<div className="container mx-auto px-4 py-20">
<h1 className="glitch text-3xl text-center mb-10">Contact</h1>
<div className="max-w-2xl mx-auto bg-white/50 dark:bg-black/50 p-8 rounded-lg shadow">
<p className="mb-6">お問い合わせは以下からお願いします。</p>
<div className="space-y-6">
<div>
<h2 className="text-xl mb-2">Email</h2>
<p>reibniz.tech[at]gmail.com</p>
<p className="text-sm text-gray-500">
※[at]を@に置き換えてください。
</p>
</div>
<div>
<h2 className="text-xl mb-2">Twitter</h2>
<p>@your_handle</p>
</div>
<div>
<h2 className="text-xl mb-2">GitHub</h2>
<p>github.com/yourusername</p>
</div>
</div>
</div>
</div>
</>
);
}
================
File: src/app/contexts/theme.tsx
================
"use client";
import { ThemeProvider as NextThemeProvider } from "next-themes";
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const defaultTheme = "dark";
console.log("ThemeProvider initialized with defaultTheme:", defaultTheme);
return (
<NextThemeProvider
attribute="class"
defaultTheme={defaultTheme}
enableSystem
enableColorScheme
>
{children}
</NextThemeProvider>
);
}
================
File: src/app/post/[slug]/page.tsx
================
import { MarkdownContent } from "../markdown-content";
import { TextBackground } from "../../components/background/text-background";
export default function PostPage({ params }: { params: { slug: string } }) {
return (
<>
<TextBackground />
<div className="container max-w-11/12 mx-auto px-4 py-8">
<MarkdownContent slug={params.slug} />
</div>
</>
);
}
================
File: src/app/post/card.tsx
================
import Link from "next/link";
import Image from "next/image";
export function Card({
title,
description,
link,
date,
tags,
thumbnail,
}: {
title: string;
description: string;
link: string;
date?: string;
tags?: string[];
thumbnail?: string;
}) {
return (
<div className="bg-white/50 dark:bg-black/50 shadow-lg rounded-lg overflow-hidden hover:shadow-xl transition-shadow duration-300">
{/* サムネイル画像 */}
{thumbnail ? (
<div className="relative h-40 w-full">
<Image src={thumbnail} alt={title} fill className="object-cover" />
</div>
) : (
<div className="relative h-40 w-full">
<Image
src={"/images/asobi-16_9-001.webp"}
alt={title}
fill
className="object-cover"
/>
</div>
)}
<div className="p-4">
{/* タグ */}
{tags && tags.length > 0 && (
<div className="flex flex-wrap gap-1 mb-2">
{tags.map((tag) => (
<span
key={tag}
className="px-2 py-0.5 text-xs bg-gray-200 dark:bg-gray-700 rounded-full"
>
{tag}
</span>
))}
</div>
)}
<h2 className="text-xl font-bold">{title}</h2>
<p className="mt-2 text-sm line-clamp-2">{description}</p>
{/* 公開日 */}
{date && (
<p className="text-gray-500 text-xs mt-2">
公開日: {new Date(date).toLocaleDateString("ja-JP")}
</p>
)}
<Link
href={link}
className="text-blue-500 hover:underline mt-3 block text-sm"
>
記事を読む →
</Link>
</div>
</div>
);
}
================
File: src/app/post/markdown-content.tsx
================
import React from "react";
import ReactMarkdown from "react-markdown";
import fs from "fs";
import path from "path";
import { notFound } from "next/navigation";
import matter from "gray-matter";
import remarkGfm from "remark-gfm";
interface MarkdownContentProps {
slug: string;
}
export async function MarkdownContent({ slug }: MarkdownContentProps) {
// マークダウンファイルのパスを構築
const filePath = path.join(process.cwd(), "src", "post", `${slug}.md`);
// ファイルが存在するか確認
let fileContents;
try {
fileContents = fs.readFileSync(filePath, "utf8");
} catch (error) {
notFound();
}
// フロントマターを解析
const { data, content } = matter(fileContents);
return (
<article className="prose prose-base sm:prose-sm lg:prose-lg prose-slate dark:prose-invert rounded-lg p-4 sm:p-6 lg:p-8 mx-auto max-w-none sm:max-w-3xl lg:max-w-4xl bg-white/50 dark:bg-black/50">
{data.title && (
<h1 className="text-2xl sm:text-3xl lg:text-4xl mb-4">{data.title}</h1>
)}
{data.date && (
<p className="text-xs sm:text-sm text-gray-500 dark:text-gray-400 mb-3">
公開日: {new Date(data.date).toLocaleDateString("ja-JP")}
{data.author && ` • 著者: ${data.author}`}
</p>
)}
{data.tags && data.tags.length > 0 && (
<div className="flex flex-wrap gap-1 sm:gap-2 mb-4">
{data.tags.map((tag: string) => (
<span
key={tag}
className="px-2 py-0.5 sm:py-1 text-xs bg-gray-200 dark:bg-gray-700 rounded-full"
>
{tag}
</span>
))}
</div>
)}
<div className="mt-6">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
</div>
</article>
);
}
================
File: src/app/post/page.tsx
================
import { Card } from "./card";
import { getAllPosts } from "@/app/utils/post";
export default function Page() {
const posts = getAllPosts();
return (
<>
<h1 className="glitch text-2xl text-center p-4">Articles</h1>
<div className="w-10/12 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mx-auto">
{posts.map((post) => (
<Card
key={post.slug}
title={post.title}
description={post.description}
link={`/post/${post.slug}`}
date={post.date}
tags={post.tags}
thumbnail={post.thumbnail}
/>
))}
</div>
</>
);
}
================
File: src/app/utils/post.ts
================
import fs from "fs";
import path from "path";
import matter from "gray-matter";
export interface Post {
slug: string;
title: string;
description: string;
date: string;
tags?: string[];
author?: string;
[key: string]: any; // その他のフロントマターデータを許可
}
export function getAllPosts(): Post[] {
const postsDirectory = path.join(process.cwd(), "src", "post");
const filenames = fs.readdirSync(postsDirectory);
console.log("filenames", filenames);
const posts = filenames
.filter((filename) => filename.endsWith(".md"))
.map((filename) => {
// ファイル名からスラッグを取得(拡張子なし)
const slug = filename.replace(/.md$/, "");
// ファイルの内容を読み込む
const fullPath = path.join(postsDirectory, filename);
const fileContents = fs.readFileSync(fullPath, "utf8");
// gray-matterを使ってフロントマターを解析
const { data, content } = matter(fileContents);
// フロントマターからメタデータを取得または代替値を使用
const post: Post = {
slug,
title: data.title || getTitle(content, slug),
description: data.description || getDescription(content),
date: data.date ? data.date.toString() : "",
...data, // その他のフロントマターデータを含める
};
return post;
})
.sort((a, b) => {
// 日付でソート(降順)
const dateA = a.date ? new Date(a.date).getTime() : 0;
const dateB = b.date ? new Date(b.date).getTime() : 0;
return dateB - dateA;
});
return posts;
}
// マークダウン本文からタイトルを抽出(フロントマターがない場合のフォールバック)
function getTitle(content: string, slug: string): string {
const titleMatch = content.match(/^# (.*)/m);
return titleMatch ? titleMatch[1] : slug;
}
// マークダウン本文から説明を抽出(フロントマターがない場合のフォールバック)
function getDescription(content: string): string {
let description = "";
const introMatch = content.match(/## はじめにs+([^
]+)/);
if (introMatch) {
description = introMatch[1];
} else {
// はじめにセクションがなければ、最初の段落を使用
const paragraphs = content.split("
");
if (paragraphs.length > 1) {
description = paragraphs[1].replace(/*.**/, "").trim();
}
}
return description;
}
================
File: src/mastra/agents/index.ts
================
import { google } from '@ai-sdk/google';
import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { weatherTool } from '../tools';
export const weatherAgent = new Agent({
name: 'Weather Agent',
instructions: `
You are a helpful weather assistant that provides accurate weather information.
Your primary function is to help users get weather details for specific locations. When responding:
- Always ask for a location if none is provided
- If the location name isn’t in English, please translate it
- If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
- Include relevant details like humidity, wind conditions, and precipitation
- Keep responses concise but informative
Use the weatherTool to fetch current weather data.
`,
model: google('gemini-1.5-pro-latest'),
tools: { weatherTool },
memory: new Memory({
options: {
lastMessages: 10,
semanticRecall: false,
threads: {
generateTitle: false,
},
},
}),
});
================
File: src/mastra/tools/index.ts
================
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';
interface GeocodingResponse {
results: {
latitude: number;
longitude: number;
name: string;
}[];
}
interface WeatherResponse {
current: {
time: string;
temperature_2m: number;
apparent_temperature: number;
relative_humidity_2m: number;
wind_speed_10m: number;
wind_gusts_10m: number;
weather_code: number;
};
}
export const weatherTool = createTool({
id: 'get-weather',
description: 'Get current weather for a location',
inputSchema: z.object({
location: z.string().describe('City name'),
}),
outputSchema: z.object({
temperature: z.number(),
feelsLike: z.number(),
humidity: z.number(),
windSpeed: z.number(),
windGust: z.number(),
conditions: z.string(),
location: z.string(),
}),
execute: async ({ context }) => {
return await getWeather(context.location);
},
});
const getWeather = async (location: string) => {
const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(location)}&count=1`;
const geocodingResponse = await fetch(geocodingUrl);
const geocodingData = (await geocodingResponse.json()) as GeocodingResponse;
if (!geocodingData.results?.[0]) {
throw new Error(`Location '${location}' not found`);
}
const { latitude, longitude, name } = geocodingData.results[0];
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code`;
const response = await fetch(weatherUrl);
const data = (await response.json()) as WeatherResponse;
return {
temperature: data.current.temperature_2m,
feelsLike: data.current.apparent_temperature,
humidity: data.current.relative_humidity_2m,
windSpeed: data.current.wind_speed_10m,
windGust: data.current.wind_gusts_10m,
conditions: getWeatherCondition(data.current.weather_code),
location: name,
};
};
function getWeatherCondition(code: number): string {
const conditions: Record<number, string> = {
0: 'Clear sky',
1: 'Mainly clear',
2: 'Partly cloudy',
3: 'Overcast',
45: 'Foggy',
48: 'Depositing rime fog',
51: 'Light drizzle',
53: 'Moderate drizzle',
55: 'Dense drizzle',
56: 'Light freezing drizzle',
57: 'Dense freezing drizzle',
61: 'Slight rain',
63: 'Moderate rain',
65: 'Heavy rain',
66: 'Light freezing rain',
67: 'Heavy freezing rain',
71: 'Slight snow fall',
73: 'Moderate snow fall',
75: 'Heavy snow fall',
77: 'Snow grains',
80: 'Slight rain showers',
81: 'Moderate rain showers',
82: 'Violent rain showers',
85: 'Slight snow showers',
86: 'Heavy snow showers',
95: 'Thunderstorm',
96: 'Thunderstorm with slight hail',
99: 'Thunderstorm with heavy hail',
};
return conditions[code] || 'Unknown';
}
================
File: src/mastra/types/daily-papers.ts
================
import { z } from "zod";
export interface Author {
_id: string;
user?: {
_id: string;
avatarUrl: string;
isPro: boolean;
fullname: string;
user: string;
type: string;
};
name: string;
status?: string;
statusLastChangedAt?: string;
hidden: boolean;
}
export interface Paper {
id: string;
authors: Author[];
publishedAt: string;
submittedOnDailyAt?: string;
title: string;
submittedOnDailyBy?: {
_id: string;
avatarUrl: string;
isPro: boolean;
fullname: string;
user: string;
type: string;
};
summary: string;
upvotes?: number;
discussionId?: string;
projectPage?: string;
githubRepo?: string;
mediaUrls?: string[];
ai_keywords?: string[];
}
export interface PaperEntry {
paper: Paper;
publishedAt: string;
title: string;
summary: string;
mediaUrls?: string[];
thumbnail?: string;
numComments?: number;
submittedBy?: {
_id: string;
avatarUrl: string;
fullname: string;
name: string;
type: string;
isPro: boolean;
isHf?: boolean;
isHfAdmin?: boolean;
isMod?: boolean;
followerCount?: number;
};
isAuthorParticipating?: boolean;
}
export interface FormatedPaper {
id: string;
authors: string[];
publishedAt: string;
title: string;
summary: string;
projectPage?: string;
githubRepo?: string;
mediaUrls?: string[];
ai_keywords?: string[];
}
export interface SummaryPaper extends FormatedPaper {
aiSummary: {
summary: string;
comparison: string;
importantPoints: string;
effectiveness: string;
discussion: string;
nextPaper: string;
};
}
export interface PapersList extends Array<PaperEntry> {}
export interface FormatedPapersList extends Array<FormatedPaper> {}
const authorSchema = z.object({
_id: z.string(),
user: z
.object({
_id: z.string(),
avatarUrl: z.string(),
isPro: z.boolean(),
fullname: z.string(),
user: z.string(),
type: z.string(),
})
.optional(),
name: z.string(),
status: z.string().optional(),
statusLastChangedAt: z.string().optional(),
hidden: z.boolean(),
});
const paperSchema = z.object({
id: z.string(),
authors: z.array(authorSchema),
publishedAt: z.string(),
submittedOnDailyAt: z.string().optional(),
title: z.string(),
submittedOnDailyBy: z
.object({
_id: z.string(),
avatarUrl: z.string(),
isPro: z.boolean(),
fullname: z.string(),
user: z.string(),
type: z.string(),
})
.optional(),
summary: z.string(),
upvotes: z.number().optional(),
discussionId: z.string().optional(),
projectPage: z.string().optional(),
githubRepo: z.string().optional(),
mediaUrls: z.array(z.string()).optional(),
ai_keywords: z.array(z.string()).optional(),
});
const paperEntrySchema = z.object({
paper: paperSchema,
publishedAt: z.string(),
title: z.string(),
summary: z.string(),
mediaUrls: z.array(z.string()).optional(),
thumbnail: z.string().optional(),
numComments: z.number().optional(),
submittedBy: z
.object({
_id: z.string(),
avatarUrl: z.string(),
fullname: z.string(),
name: z.string(),
type: z.string(),
isPro: z.boolean(),
isHf: z.boolean().optional(),
isHfAdmin: z.boolean().optional(),
isMod: z.boolean().optional(),
followerCount: z.number().optional(),
})
.optional(),
isAuthorParticipating: z.boolean().optional(),
});
const formatedPaperSchema = z.object({
id: z.string(),
authors: z.array(z.string()),
publishedAt: z.string(),
title: z.string(),
summary: z.string(),
upvotes: z.number().optional(),
discussionId: z.string().optional(),
projectPage: z.string().optional(),
githubRepo: z.string().optional(),
mediaUrls: z.array(z.string()).optional(),
ai_keywords: z.array(z.string()).optional(),
});
const summaryPaperSchema = z.object({
id: z.string(),
authors: z.array(z.string()),
publishedAt: z.string(),
title: z.string(),
summary: z.string(),
projectPage: z.string().optional(),
githubRepo: z.string().optional(),
mediaUrls: z.array(z.string()).optional(),
ai_keywords: z.array(z.string()).optional(),
aiSummary: z.object({
summary: z.string(),
comparison: z.string(),
importantPoints: z.string(),
effectiveness: z.string(),
discussion: z.string(),
nextPaper: z.string(),
}),
});
const papersListSchema = z.array(paperEntrySchema);
const formatedPapersListSchema = z.array(formatedPaperSchema);
export {
authorSchema,
paperSchema,
paperEntrySchema,
formatedPaperSchema,
papersListSchema,
formatedPapersListSchema,
summaryPaperSchema,
};
================
File: src/mastra/workflows/daily-papers-workflow.ts
================
import { Step, Workflow } from "@mastra/core/workflows";
import { z } from "zod";
import { google } from "@ai-sdk/google";
import { generateObject, generateText } from "ai";
// 型をインポート
import {
papersListSchema,
PapersList,
formatedPapersListSchema,
FormatedPapersList,
summaryPaperSchema,
SummaryPaper,
} from "../types/daily-papers";
const summaryModel = google("gemini-2.0-flash-001", {});
// huggingface.coからdailyの論文を取得する関数
const fetchDailyPapers = async (date: string) => {
const requestUrl = `https://huggingface.co/api/daily_papers?date=${date}`;
const dailyPapersResponse = await fetch(requestUrl);
const dailyPaperData = await dailyPapersResponse.json();
if (!dailyPaperData) {
throw new Error(`Date '${date}' not found`);
}
return dailyPaperData as PapersList;
};
// 論文を取得するステップ
const getDailyPapers = new Step({
id: "get-daily-papers",
description: "Fetches daily papers from Hugging Face",
inputSchema: z.object({
date: z.string().describe("Date for getting papers"),
}),
outputSchema: z.object({
papers: papersListSchema,
}),
execute: async ({ context }) => {
const triggerData = context?.getStepResult<{ date: string }>("trigger");
if (!triggerData) {
throw new Error("Trigger data not found");
}
const papers = await fetchDailyPapers(triggerData.date);
return { papers };
},
});
// 論文に関するデータを整形する関数
const formatPapers = (papers: PapersList): FormatedPapersList => {
return papers.map((paper) => ({
id: paper.paper.id,
authors: paper.paper.authors
? paper.paper.authors.map((author) => author.name || "")
: [],
publishedAt: paper.publishedAt,
title: paper.title,
summary: paper.summary,
projectPage: paper.paper.projectPage,
githubRepo: paper.paper.githubRepo,
mediaUrls: paper.paper.mediaUrls,
ai_keywords: paper.paper.ai_keywords || [],
}));
};
// 論文のデータを整形するステップ
const formatDailyPapers = new Step({
id: "format-daily-papers",
description: "Format daily papers",
inputSchema: z.object({
papers: papersListSchema,
}),
outputSchema: z.object({
papers: formatedPapersListSchema,
}),
execute: async ({ context }) => {
const result = context?.getStepResult<{ papers: PapersList }>(
"get-daily-papers"
);
if (!result || !result.papers) {
throw new Error("Papers data not found");
}
const formattedPapers = formatPapers(result.papers);
return { papers: formattedPapers };
},
});
// 論文の要約を落合フォーマットで生成する関数
const generateSummary = async (id: string) => {
const pdfUrl = `https://arxiv.org/pdf/${id}`;
const response = await generateObject({
model: summaryModel,
messages: [
{
role: "user",
content: [
{
type: "text",
text: `# Task
Please create a summary of the given paper.
When summarizing, please include the following perspectives:
* What is it in one sentence?
* What is impressive about it compared to previous research?
* What are the important points of the technology or methodology?
* How was the technology or methodology determined to be effective?
* Are there any points that should be discussed or criticized?
* What paper should be read next?
# Output
The output will be in JSON format including the above items.
{
summary: What is it in one sentence?
comparison: What is impressive about it compared to previous research?
importantPoints: What are the important points of the technology or methodology?
effectiveness: How was the technology or methodology determined to be effective?
discussion: Are there any points that should be discussed or criticized?
nextPaper: What paper should be read next? (paper reference)
}
# Important Notice
Please provide the output in Japanese.
Is there anything else you'd like me to adjust in the translation?
}`,
},
{
type: "file",
data: new URL(pdfUrl),
mimeType: "application/pdf",
},
],
},
],
schema: z.object({
summary: z.string().describe("ひとことでまとめるとどんなものですか?"),
comparison: z
.string()
.describe("先行研究と比較してどの点がすごいのですか?"),
importantPoints: z
.string()
.describe("技術や手法の重要な点はどこにありますか?"),
effectiveness: z
.string()
.describe("技術や手法はどのように有効だと判断されましたか?"),
discussion: z.string().describe("何か議論や批判すべき点はありますか?"),
nextPaper: z.string().describe("次に読むべき論文は何ですか?"),
}),
});
const summary = response.object;
if (!summary) {
throw new Error("Summary generation failed");
}
return summary;
};
// 論文の要約を落合フォーマットで生成するステップ
const generateSummaryStep = new Step({
id: "generate-summary",
description: "Generate summary for each paper",
inputSchema: z.object({
papers: formatedPapersListSchema,
}),
outputSchema: z.object({
summaries: z.array(summaryPaperSchema),
}),
execute: async ({ context }) => {
const result = context?.getStepResult<{ papers: FormatedPapersList }>(
"format-daily-papers"
);
if (!result || !result.papers) {
throw new Error("Papers data not found");
}
const summaries = await Promise.all(
result.papers.map(async (paper) => {
const summary = await generateSummary(paper.id);
return {
aiSummary: summary,
...paper,
} as SummaryPaper;
})
);
return { summaries };
},
});
// マークダウンで記事を作成する関数
const generateMarkdownArticle = async (
papers: SummaryPaper[]
): Promise<string> => {
const paperInfoString = papers
.map((paper) => {
const {
title,
authors,
publishedAt,
summary,
projectPage,
githubRepo,
mediaUrls,
aiSummary,
} = paper;
const authorsString = authors.join(", ");
const summaryString = aiSummary.summary;
const comparisonString = aiSummary.comparison;
const importantPointsString = aiSummary.importantPoints;
const effectivenessString = aiSummary.effectiveness;
const discussionString = aiSummary.discussion;
const nextPaperString = aiSummary.nextPaper;
const markdownString = `
# ${title}
## Authors
${authorsString}
## Summary
${summary}
## Published At
${publishedAt}
## Project Page
${projectPage || "N/A"}
## GitHub Repo
${githubRepo || "N/A"}
## Media URLs
${mediaUrls?.length ? mediaUrls.join(", ") : "N/A"}
## Summary (AI)
${summaryString}
## Comparison (AI)
${comparisonString}
## Important Points (AI)
${importantPointsString}
## Effectiveness (AI)
${effectivenessString}
## Discussion (AI)
${discussionString}
## Next Paper (AI)
${nextPaperString}
`;
return markdownString;
})
.join("
---
");
const response = await generateText({
model: summaryModel,
messages: [
{
role: "user",
content: [
{
type: "text",
text: `# タスク
あなたには、本日のフィーチャーされた論文が複数与えられます。
その論文をもとに、マークダウン形式で記事を作成します。
記事には、以下の情報を含めてください。
`,
},
{
type: "text",
text: paperInfoString,
},
],
},
],
});
if (!response.text) {
throw new Error("Markdown generation failed");
}
return response.text;
};
// マークダウン記事を生成するステップ
const generateMarkdownArticleStep = new Step({
id: "generate-markdown-article",
description: "Generate markdown article from papers",
inputSchema: z.object({
summaries: z.array(summaryPaperSchema),
}),
outputSchema: z.object({
article: z.string().describe("Generated markdown article"),
}),
execute: async ({ context }) => {
const result = context?.getStepResult<{ summaries: SummaryPaper[] }>(
"generate-summary"
);
if (!result || !result.summaries) {
throw new Error("Summaries data not found");
}
const article = await generateMarkdownArticle(result.summaries);
return { article };
},
});
// ワークフローを定義
// 1. get-daily-papers ステップを実行して, 日付に基づいて論文を取得
// 2. format-daily-papers ステップを実行して, 取得した論文を整形
// 3. 論文のsummaryとpdfから, 落合陽一フォーマットで要約を作成
// 4. 論文のsummary, 要約から, マークダウンで記事を作成する
// 5. 作成した記事をfirebaseに保存する
const dailyPapersWorkflow = new Workflow({
name: "daily-papers-workflow",
triggerSchema: z.object({
date: z.string().describe("Date for getting papers"),
}),
})
.step(getDailyPapers)
.then(formatDailyPapers)
.then(generateSummaryStep)
.then(generateMarkdownArticleStep)
.commit();
export { dailyPapersWorkflow };
================
File: src/mastra/workflows/weather-workflow.ts
================
import { google } from "@ai-sdk/google";
import { Agent } from "@mastra/core/agent";
import { Step, Workflow } from "@mastra/core/workflows";
import { z } from "zod";
const llm = google("gemini-1.5-pro-latest");
const agent = new Agent({
name: "Weather Agent",
model: llm,
instructions: `
You are a local activities and travel expert who excels at weather-based planning. Analyze the weather data and provide practical activity recommendations.
For each day in the forecast, structure your response exactly as follows:
📅 [Day, Month Date, Year]
═══════════════════════════
🌡️ WEATHER SUMMARY
• Conditions: [brief description]
• Temperature: [X°C/Y°F to A°C/B°F]
• Precipitation: [X% chance]
🌅 MORNING ACTIVITIES
Outdoor:
• [Activity Name] - [Brief description including specific location/route]
Best timing: [specific time range]
Note: [relevant weather consideration]
🌞 AFTERNOON ACTIVITIES
Outdoor:
• [Activity Name] - [Brief description including specific location/route]
Best timing: [specific time range]
Note: [relevant weather consideration]
🏠 INDOOR ALTERNATIVES
• [Activity Name] - [Brief description including specific venue]
Ideal for: [weather condition that would trigger this alternative]
⚠️ SPECIAL CONSIDERATIONS
• [Any relevant weather warnings, UV index, wind conditions, etc.]
Guidelines:
- Suggest 2-3 time-specific outdoor activities per day
- Include 1-2 indoor backup options
- For precipitation >50%, lead with indoor activities
- All activities must be specific to the location
- Include specific venues, trails, or locations
- Consider activity intensity based on temperature
- Keep descriptions concise but informative
Maintain this exact formatting for consistency, using the emoji and section headers as shown.
`,
});
const forecastSchema = z.array(
z.object({
date: z.string(),
maxTemp: z.number(),
minTemp: z.number(),
precipitationChance: z.number(),
condition: z.string(),
location: z.string(),
})
);
const fetchWeather = new Step({
id: "fetch-weather",
description: "Fetches weather forecast for a given city",
inputSchema: z.object({
city: z.string().describe("The city to get the weather for"),
}),
outputSchema: forecastSchema,
execute: async ({ context }) => {
const triggerData = context?.getStepResult<{ city: string }>("trigger");
if (!triggerData) {
throw new Error("Trigger data not found");
}
const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(triggerData.city)}&count=1`;
const geocodingResponse = await fetch(geocodingUrl);
const geocodingData = (await geocodingResponse.json()) as {
results: { latitude: number; longitude: number; name: string }[];
};
if (!geocodingData.results?.[0]) {
throw new Error(`Location '${triggerData.city}' not found`);
}
const { latitude, longitude, name } = geocodingData.results[0];
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=temperature_2m_max,temperature_2m_min,precipitation_probability_mean,weathercode&timezone=auto`;
const response = await fetch(weatherUrl);
const data = (await response.json()) as {
daily: {
time: string[];
temperature_2m_max: number[];
temperature_2m_min: number[];
precipitation_probability_mean: number[];
weathercode: number[];
};
};
const forecast = data.daily.time.map((date: string, index: number) => ({
date,
maxTemp: data.daily.temperature_2m_max[index],
minTemp: data.daily.temperature_2m_min[index],
precipitationChance: data.daily.precipitation_probability_mean[index],
condition: getWeatherCondition(data.daily.weathercode[index]!),
location: name,
}));
return forecast;
},
});
const planActivities = new Step({
id: "plan-activities",
description: "Suggests activities based on weather conditions",
execute: async ({ context, mastra }) => {
const forecast = context?.getStepResult(fetchWeather);
if (!forecast || forecast.length === 0) {
throw new Error("Forecast data not found");
}
const prompt = `Based on the following weather forecast for ${forecast[0]?.location}, suggest appropriate activities:
${JSON.stringify(forecast, null, 2)}
`;
const response = await agent.stream([
{
role: "user",
content: prompt,
},
]);
let activitiesText = "";
for await (const chunk of response.textStream) {
process.stdout.write(chunk);
activitiesText += chunk;
}
return {
activities: activitiesText,
};
},
});
function getWeatherCondition(code: number): string {
const conditions: Record<number, string> = {
0: "Clear sky",
1: "Mainly clear",
2: "Partly cloudy",
3: "Overcast",
45: "Foggy",
48: "Depositing rime fog",
51: "Light drizzle",
53: "Moderate drizzle",
55: "Dense drizzle",
61: "Slight rain",
63: "Moderate rain",
65: "Heavy rain",
71: "Slight snow fall",
73: "Moderate snow fall",
75: "Heavy snow fall",
95: "Thunderstorm",
};
return conditions[code] || "Unknown";
}
const weatherWorkflow = new Workflow({
name: "weather-workflow",
triggerSchema: z.object({
city: z.string().describe("The city to get the weather for"),
}),
})
.step(fetchWeather)
.then(planActivities);
weatherWorkflow.commit();
export { weatherWorkflow };
================
File: src/post/ai-future.md
================
---
title: "AIとデータサイエンスの未来"
description: "人工知能(AI)とデータサイエンスの分野は急速に発展しており、私たちの生活や仕事に大きな影響を与えています。この記事では、AIとデータサイエンスの未来について考察します。"
date: "2023-10-01"
author: "hirakawa"
tags: ["AI", "データサイエンス", "機械学習", "将来展望"]
---
## はじめに
人工知能(AI)とデータサイエンスの分野は急速に発展しており、私たちの生活や仕事に大きな影響を与えています。この記事では、AI とデータサイエンスの未来について考察します。
## 現在のトレンド
現在、以下のようなトレンドが AI とデータサイエンスの分野で見られます:
- **大規模言語モデル (LLM)**: GPT-4 などの大規模言語モデルは、テキスト生成や理解の能力を大幅に向上させています。
- **生成 AI**: 画像、音声、テキストなどを生成できる AI の発展が目覚ましいです。
- **エッジ AI**: デバイス上で直接 AI を実行することで、プライバシーやレイテンシの問題を解決する取り組みが進んでいます。
## 将来の展望
今後 5〜10 年の間に、以下のような発展が予想されます:
1. **AI とヒトの協働**: AI はヒトの能力を拡張し、より創造的なタスクに集中できるようにサポートするツールとして進化していくでしょう。
2. **特化型 AI**: 特定のドメインに特化した AI モデルがより普及し、専門的なタスクでの性能が向上します。
3. **倫理とガバナンス**: AI の発展に伴い、倫理的な問題やガバナンスの重要性がさらに高まるでしょう。
## まとめ
AI とデータサイエンスの未来は非常に明るく、様々な可能性を秘めています。しかし、技術の発展と同時に、社会的な影響や倫理的な側面も考慮することが重要です。
================
File: src/post/generative-models.md
================
---
title: "生成モデルの基礎"
description: "生成モデル(Generative Models)は、機械学習の中でも特に注目されている分野です。この記事では、生成モデルの基本的な概念と応用について説明します。"
date: "2023-10-05"
author: "平川"
tags: ["生成モデル", "機械学習", "GAN", "VAE", "Diffusion Models"]
---
## はじめに
生成モデル(Generative Models)は、機械学習の中でも特に注目されている分野です。この記事では、生成モデルの基本的な概念と応用について説明します。
## 生成モデルとは
生成モデルとは、データの分布を学習し、その分布に従った新しいデータを生成することができるモデルのことです。対照的に、識別モデル(Discriminative Models)は入力データから直接ラベルや値を予測します。
## 主な生成モデルの種類
### GAN (Generative Adversarial Networks)
GAN は 2014 年に Ian Goodfellow によって提案された生成モデルで、Generator(生成器)と Discriminator(識別器)の 2 つのネットワークが敵対的に学習を行います。
```
Generator: 偽のデータを生成
Discriminator: 本物と偽物を識別
```
この敵対的な学習によって、より本物に近いデータが生成できるようになります。
### VAE (Variational Autoencoders)
VAE は、データの潜在表現(latent representation)を学習し、その潜在空間から新しいデータを生成するモデルです。エンコーダとデコーダの構造を持ちます。
### Diffusion Models
最近注目を集めている Diffusion Models は、ノイズを少しずつ除去していくことでデータを生成するアプローチです。Stable Diffusion や DALL-E 2 などの画像生成モデルに利用されています。
## 生成モデルの応用例
- **画像生成**: 写実的な画像や芸術作品の生成
- **テキスト生成**: 文章やコードの自動生成
- **音声合成**: 人間の声や音楽の生成
- **動画生成**: 短い動画やアニメーションの生成
- **薬剤設計**: 新しい分子構造の提案
## まとめ
生成モデルは急速に発展しており、様々な分野で革新的な応用が進んでいます。基本的な概念を理解することで、これらのテクノロジーをより深く理解し、活用することができるでしょう.
================
File: postcss.config.mjs
================
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;
================
File: README.md
================
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
================
File: tsconfig.json
================
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
================
File: src/app/globals.css
================
@import "tailwindcss";
@tailwind base;
@tailwind components;
@tailwind utilities;
@custom-variant dark (&:where(.dark, .dark *));
@keyframes movement {
0% {
top: 0px;
left: -20px;
}
15% {
top: 10px;
left: 10px;
}
60% {
top: 5px;
left: -10px;
}
75% {
top: -5px;
left: 20px;
}
100% {
top: 10px;
left: 5px;
}
}
.glitch {
/* font-size: 5rem; */
font-weight: bold;
text-transform: uppercase;
position: relative;
text-shadow:
0.05em 0 0 #00fffc,
-0.03em -0.04em 0 #fc00ff,
0.025em 0.04em 0 #fffc00;
animation: glitch 725ms infinite;
}
.glitch span {
position: absolute;
top: 0;
left: 0;
}
.glitch span:first-child {
animation: glitch 500ms infinite;
clip-path: polygon(0 0, 100% 0, 100% 35%, 0 35%);
transform: translate(-0.04em, -0.03em);
opacity: 0.75;
}
.glitch span:last-child {
animation: glitch 375ms infinite;
clip-path: polygon(0 65%, 100% 65%, 100% 100%, 0 100%);
transform: translate(0.04em, 0.03em);
opacity: 0.75;
}
@keyframes glitch {
0% {
text-shadow:
0.05em 0 0 #00fffc,
-0.03em -0.04em 0 #fc00ff,
0.025em 0.04em 0 #fffc00;
}
15% {
text-shadow:
0.05em 0 0 #00fffc,
-0.03em -0.04em 0 #fc00ff,
0.025em 0.04em 0 #fffc00;
}
16% {
text-shadow:
-0.05em -0.025em 0 #00fffc,
0.025em 0.035em 0 #fc00ff,
-0.05em -0.05em 0 #fffc00;
}
49% {
text-shadow:
-0.05em -0.025em 0 #00fffc,
0.025em 0.035em 0 #fc00ff,
-0.05em -0.05em 0 #fffc00;
}
50% {
text-shadow:
0.05em 0.035em 0 #00fffc,
0.03em 0 0 #fc00ff,
0 -0.04em 0 #fffc00;
}
99% {
text-shadow:
0.05em 0.035em 0 #00fffc,
0.03em 0 0 #fc00ff,
0 -0.04em 0 #fffc00;
}
100% {
text-shadow:
-0.05em 0 0 #00fffc,
-0.025em -0.04em 0 #fc00ff,
-0.04em -0.025em 0 #fffc00;
}
}
@plugin '@tailwindcss/typography';
================
File: src/app/layout.tsx
================
import type { Metadata } from "next";
import "./globals.css";
import { Header, Footer, ThemeToggle } from "./components/layout";
import { ThemeProvider } from "@/app/contexts/theme";
import { TextBackground } from "./components/background/text-background";
export const metadata: Metadata = {
title: "Reibniz.com",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ja" suppressHydrationWarning>
<body className="antialiased h-screen w-dvw overflow-x-hidden">
<ThemeProvider>
<ThemeToggle />
<TextBackground />
<Header />
{children}
<Footer />
</ThemeProvider>
</body>
</html>
);
}
================
File: src/app/page.tsx
================
import { getAllPosts } from "@/app/utils/post";
import { Card } from "./post/card";
export default function Home() {
const posts = getAllPosts();
return (
<div className="min-h-screen pb-16 flex flex-col items-center">
{/* ヒーローセクション */}
<div className="w-11/12 mx-auto h-screen flex flex-col justify-center items-center">
<div className="font-mono text-7xl glitch mb-4" data-text="ASOBIBA">
ASOBIBA
</div>
<div className="text-xl mb-16">
A playground of my thoughts and ideas.
</div>
<div className="animate-bounce mt-8">
<svg
className="w-6 h-6"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path d="M19 14l-7 7m0 0l-7-7m7 7V3"></path>
</svg>
</div>
</div>
{/* 記事一覧セクション */}
<div className="w-11/12 flex flex-col justify-center px-4 py-12">
<h2 className="text-3xl font-bold mb-8 text-center">Latest Articles</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{posts.map((post) => (
<Card
key={post.slug}
title={post.title}
description={post.description}
link={`/post/${post.slug}`}
date={post.date}
tags={post.tags}
thumbnail={post.thumbnail}
/>
))}
</div>
</div>
</div>
);
}
================
File: src/mastra/workflows/index.ts
================
export * from "./weather-workflow";
export * from "./daily-papers-workflow";
================
File: src/mastra/index.ts
================
import { Mastra } from "@mastra/core/mastra";
import { createLogger } from "@mastra/core/logger";
import { LibSQLStore } from "@mastra/libsql";
import { weatherWorkflow, dailyPapersWorkflow } from "./workflows";
import { weatherAgent } from "./agents";
export const mastra = new Mastra({
workflows: { weatherWorkflow, dailyPapersWorkflow },
agents: { weatherAgent },
storage: new LibSQLStore({
// stores telemetry, evals, ... into memory storage, if it needs to persist, change to file:../mastra.db
url: ":memory:",
}),
logger: createLogger({
name: "Mastra",
level: "info",
}),
});
================
File: .gitignore
================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.mastra/
================
File: next.config.ts
================
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
serverExternalPackages: ["@mastra/*"],
};
export default nextConfig;
================
File: package.json
================
{
"name": "reibniz-site",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"dev:mastra": "mastra dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@ai-sdk/google": "^1.2.14",
"@mastra/core": "^0.9.1",
"@mastra/libsql": "^0.0.1",
"@mastra/memory": "^0.3.1",
"@tailwindcss/typography": "^0.5.16",
"ai": "^4.3.12",
"gray-matter": "^4.0.3",
"lucide-react": "^0.503.0",
"mastra": "^0.6.0",
"next": "15.3.1",
"next-themes": "^0.4.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"typescript": "^5"
}
}
================================================================
End of Codebase
================================================================
This file is a merged representation of the entire codebase, combined into a single document by Repomix.
================================================================
File Summary
================================================================
Purpose:
--------
This file contains a packed representation of the entire repository's contents.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.
File Format:
------------
The content is organized as follows:
1. This summary section
2. Repository information
3. Directory structure
4. Repository files (if enabled)
4. Multiple file entries, each consisting of:
a. A separator line (================)
b. The file path (File: path/to/file)
c. Another separator line
d. The full contents of the file
e. A blank line
Usage Guidelines:
-----------------
- This file should be treated as read-only. Any changes should be made to the
original repository files, not this packed version.
- When processing this file, use the file path to distinguish
between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
the same level of security as you would the original repository.
Notes:
------
- Some files may have been excluded based on .gitignore rules and Repomix's configuration
- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Files are sorted by Git change count (files with more changes are at the bottom)
Additional Info:
----------------
================================================================
Directory Structure
================================================================
public/
file.svg
globe.svg
next.svg
vercel.svg
window.svg
src/
app/
about/
page.tsx
ai-post/
page.tsx
components/
background/
text-background.tsx
layout/
footer.tsx
header.tsx
index.ts
theme-toggle.tsx
contact/
page.tsx
contexts/
theme.tsx
post/
[slug]/
page.tsx
card.tsx
markdown-content.tsx
page.tsx
utils/
post.ts
globals.css
layout.tsx
page.tsx
mastra/
agents/
index.ts
tools/
index.ts
types/
daily-papers.ts
workflows/
daily-papers-workflow.ts
index.ts
weather-workflow.ts
index.ts
post/
ai-future.md
generative-models.md
.gitignore
next.config.ts
package.json
postcss.config.mjs
README.md
tsconfig.json
================================================================
Files
================================================================
================
File: public/file.svg
================
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
================
File: public/globe.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
================
File: public/next.svg
================
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
================
File: public/vercel.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
================
File: public/window.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
================
File: src/app/about/page.tsx
================
export default function About() {
return (
<div className="h-screen w-screen">
<div className="w-8/12 mx-auto h-full flex flex-col justify-center items-center">
<div className="w-full flex flex-col mt-8">
<h2 className="text-xl font-bold glitch">About Me</h2>
<ul className="mt-2">
<li>名前: Reibniz</li>
<li>職業: データサイエンティストとか, いろいろ。</li>
<li>趣味: プログラミング, 3D制作</li>
<li>興味: 生成モデル, ベイズ推論, MMM</li>
</ul>
<h2 className="text-xl font-bold glitch mt-4">About This Site</h2>
<p className="mt-2">
このサイトは、思考やアイデアを試す, 遊び場として制作していますが,
ある種のポートフォリオとしても機能しています。
日々の学びや考えを記録し, 他の人と共有することで,
自分自身の成長を促進することを目的としています。
どこかの誰かの役に立てれば嬉しいです。
</p>
</div>
</div>
</div>
);
}
================
File: src/app/ai-post/page.tsx
================
export default function AiPostPage() {
return (
<div className="container max-w-11/12 mx-auto px-4 py-8">
<h1 className="text-2xl font-bold">AI Post</h1>
<p className="mt-4">
This is a placeholder for the AI post page. You can add your content
here.
</p>
</div>
);
}
================
File: src/app/components/background/text-background.tsx
================
const text = `This file is a merged representation of the entire codebase, combined into a single document by Repomix.
================================================================
File Summary
================================================================
Purpose:
--------
This file contains a packed representation of the entire repository's contents.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.
File Format:
------------
The content is organized as follows:
1. This summary section
2. Repository information
3. Directory structure
4. Repository files (if enabled)
4. Multiple file entries, each consisting of:
a. A separator line (================)
b. The file path (File: path/to/file)
c. Another separator line
d. The full contents of the file
e. A blank line
Usage Guidelines:
-----------------
- This file should be treated as read-only. Any changes should be made to the
original repository files, not this packed version.
- When processing this file, use the file path to distinguish
between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
the same level of security as you would the original repository.
Notes:
------
- Some files may have been excluded based on .gitignore rules and Repomix's configuration
- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Files are sorted by Git change count (files with more changes are at the bottom)
Additional Info:
----------------
================================================================
Directory Structure
================================================================
app/
components/
layout/
footer.tsx
header.tsx
index.ts
theme-toggle.tsx
post/
card.tsx
page.tsx
globals.css
layout.tsx
page.tsx
contexts/
theme.tsx
public/
file.svg
globe.svg
next.svg
vercel.svg
window.svg
.gitignore
.repomixignore
next.config.ts
package.json
postcss.config.mjs
README.md
tailwind.config.js
tsconfig.json
================================================================
Files
================================================================
================
File: app/components/layout/footer.tsx
================
"use client";
export function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="fixed top-0 left-0 h-screen flex flex-row [writing-mode:vertical-rl] justify-end text-xs">
<p className="font-serif text-gray-500 dark:text-gray-400">
©2024-{currentYear} Tomoya Hirakawa All rights reserved.
</p>
</footer>
);
}
================
File: app/components/layout/header.tsx
================
"use client";
import Link from "next/link";
export function Header() {
return (
<header className="fixed top-0 right-0 h-screen px-4 py-1 flex flex-row gap-5 items-center [writing-mode:vertical-rl] font-serif">
<Link href="/" className="text-lg font-bold">
Tomoya<span className="text-green-400">. </span>
Hirakawa
</Link>
<ul className="flex flex-row gap-3 grow justify-end">
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/post">Articles</a>
</li>
<li>
<a href="/">About</a>
</li>
</ul>
</header>
);
}
================
File: app/components/layout/index.ts
================
export * from "./header";
export * from "./footer";
export * from "./theme-toggle";
================
File: app/components/layout/theme-toggle.tsx
================
"use client";
import { Sun, Moon } from "lucide-react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
useEffect(() => {
console.log("Current theme:", theme);
setMounted(true);
}, [theme]);
if (!mounted) {
return null;
}
return (
<button className="fixed top-0 left-0 z-10 p-2">
{theme === "dark" ? (
<Sun
className="text-gray-300"
onClick={() => setTheme("light")}
size={24}
/>
) : (
<Moon className="text-gray-600" onClick={() => setTheme("dark")} />
)}
</button>
);
}
================
File: app/post/card.tsx
================
export function Card() {
return (
<div className="card">
<h2>Card Title</h2>
<p>This is a card component.</p>
</div>
);
}
================
File: app/post/page.tsx
================
import { Card } from "./card";
export default function Page() {
return (
<div className="@container mx-auto border">
<h1>Post List</h1>
<h1>Post</h1>
<Card />
</div>
);
}
================
File: app/globals.css
================
@import "tailwindcss";
@tailwind base;
@tailwind components;
@tailwind utilities;
@custom-variant dark (&:where(.dark, .dark *));
================
File: app/layout.tsx
================
import type { Metadata } from "next";
import "./globals.css";
import { Header, Footer, ThemeToggle } from "./components/layout";
import { ThemeProvider } from "@/contexts/theme";
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ja" suppressHydrationWarning>
<body className="antialiased">
<ThemeProvider>
<ThemeToggle />
<Header />
{children}
<Footer />
</ThemeProvider>
</body>
</html>
);
}
================
File: app/page.tsx
================
import { TextBackground } from "./components/background/text-background";
export default function Home() {
return <TextBackground />;
}
================
File: contexts/theme.tsx
================
"use client";
import { ThemeProvider as NextThemeProvider } from "next-themes";
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const defaultTheme = "system";
console.log("ThemeProvider initialized with defaultTheme:", defaultTheme);
return (
<NextThemeProvider
attribute="class"
defaultTheme={defaultTheme}
enableSystem
enableColorScheme
>
{children}
</NextThemeProvider>
);
}
================
File: public/file.svg
================
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
================
File: public/globe.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
================
File: public/next.svg
================
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
================
File: public/vercel.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
================
File: public/window.svg
================
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
================
File: .gitignore
================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
================
File: .repomixignore
================
app/components/background/text-background.tsx
================
File: next.config.ts
================
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;
================
File: package.json
================
{
"name": "hirakawa-site",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"lucide-react": "^0.503.0",
"next": "15.3.1",
"next-themes": "^0.4.6",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"typescript": "^5"
}
}
================
File: postcss.config.mjs
================
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;
================
File: README.md
================
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
================
File: tailwind.config.js
================
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: "class",
content: [
"./app/**/*.{js,jsx,ts,tsx}",
"./contexts/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [
function ({ addVariant }) {
addVariant('debug', '&');
},
],
};
================
File: tsconfig.json
================
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
================================================================
End of Codebase
================================================================`;
export function TextBackground() {
return (
<div className="fixed top-0 left-0 w-full h-full text-xs -z-50 text-gray-200 dark:text-gray-700 select-none overflow-y-hidden">
{text}
</div>
);
}
================
File: src/app/components/layout/footer.tsx
================
"use client";
export function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="fixed top-0 left-0 h-screen flex flex-row [writing-mode:vertical-rl] justify-end text-xs">
<p className="font-serif text-gray-500 dark:text-gray-400">
©2024-{currentYear} Reibniz All rights reserved.
</p>
</footer>
);
}
================
File: src/app/components/layout/header.tsx
================
"use client";
import Link from "next/link";
export function Header() {
return (
<header className="fixed top-0 right-0 h-screen px-4 py-1 flex flex-row gap-5 items-center [writing-mode:vertical-rl] font-serif">
<Link href="/" className="text-lg font-bold">
遊び場 | ASOBIBA
</Link>
<ul className="flex flex-row gap-3 grow justify-end">
<li>
<a href="/about">About</a>
</li>
<li>
<a href="/contact">Contact</a>
</li>
<li>
<a href="/post">Post</a>
</li>
<li>
<a href="/ai-post">AI Post</a>
</li>
</ul>
</header>
);
}
================
File: src/app/components/layout/index.ts
================
export * from "./header";
export * from "./footer";
export * from "./theme-toggle";
================
File: src/app/components/layout/theme-toggle.tsx
================
"use client";
import { Sun, Moon } from "lucide-react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
useEffect(() => {
console.log("Current theme:", theme);
setMounted(true);
}, [theme]);
if (!mounted) {
return null;
}
return (
<button className="fixed top-0 left-0 z-10 p-2">
{theme === "dark" ? (
<Sun
className="text-gray-300"
onClick={() => setTheme("light")}
size={24}
/>
) : (
<Moon className="text-gray-600" onClick={() => setTheme("dark")} />
)}
</button>
);
}
================
File: src/app/contact/page.tsx
================
export default function ContactPage() {
return (
<>
<div className="container mx-auto px-4 py-20">
<h1 className="glitch text-3xl text-center mb-10">Contact</h1>
<div className="max-w-2xl mx-auto bg-white/50 dark:bg-black/50 p-8 rounded-lg shadow">
<p className="mb-6">お問い合わせは以下からお願いします。</p>
<div className="space-y-6">
<div>
<h2 className="text-xl mb-2">Email</h2>
<p>reibniz.tech[at]gmail.com</p>
<p className="text-sm text-gray-500">
※[at]を@に置き換えてください。
</p>
</div>
<div>
<h2 className="text-xl mb-2">Twitter</h2>
<p>@your_handle</p>
</div>
<div>
<h2 className="text-xl mb-2">GitHub</h2>
<p>github.com/yourusername</p>
</div>
</div>
</div>
</div>
</>
);
}
================
File: src/app/contexts/theme.tsx
================
"use client";
import { ThemeProvider as NextThemeProvider } from "next-themes";
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const defaultTheme = "dark";
console.log("ThemeProvider initialized with defaultTheme:", defaultTheme);
return (
<NextThemeProvider
attribute="class"
defaultTheme={defaultTheme}
enableSystem
enableColorScheme
>
{children}
</NextThemeProvider>
);
}
================
File: src/app/post/[slug]/page.tsx
================
import { MarkdownContent } from "../markdown-content";
import { TextBackground } from "../../components/background/text-background";
export default function PostPage({ params }: { params: { slug: string } }) {
return (
<>
<TextBackground />
<div className="container max-w-11/12 mx-auto px-4 py-8">
<MarkdownContent slug={params.slug} />
</div>
</>
);
}
================
File: src/app/post/card.tsx
================
import Link from "next/link";
import Image from "next/image";
export function Card({
title,
description,
link,
date,
tags,
thumbnail,
}: {
title: string;
description: string;
link: string;
date?: string;
tags?: string[];
thumbnail?: string;
}) {
return (
<div className="bg-white/50 dark:bg-black/50 shadow-lg rounded-lg overflow-hidden hover:shadow-xl transition-shadow duration-300">
{/* サムネイル画像 */}
{thumbnail ? (
<div className="relative h-40 w-full">
<Image src={thumbnail} alt={title} fill className="object-cover" />
</div>
) : (
<div className="relative h-40 w-full">
<Image
src={"/images/asobi-16_9-001.webp"}
alt={title}
fill
className="object-cover"
/>
</div>
)}
<div className="p-4">
{/* タグ */}
{tags && tags.length > 0 && (
<div className="flex flex-wrap gap-1 mb-2">
{tags.map((tag) => (
<span
key={tag}
className="px-2 py-0.5 text-xs bg-gray-200 dark:bg-gray-700 rounded-full"
>
{tag}
</span>
))}
</div>
)}
<h2 className="text-xl font-bold">{title}</h2>
<p className="mt-2 text-sm line-clamp-2">{description}</p>
{/* 公開日 */}
{date && (
<p className="text-gray-500 text-xs mt-2">
公開日: {new Date(date).toLocaleDateString("ja-JP")}
</p>
)}
<Link
href={link}
className="text-blue-500 hover:underline mt-3 block text-sm"
>
記事を読む →
</Link>
</div>
</div>
);
}
================
File: src/app/post/markdown-content.tsx
================
import React from "react";
import ReactMarkdown from "react-markdown";
import fs from "fs";
import path from "path";
import { notFound } from "next/navigation";
import matter from "gray-matter";
import remarkGfm from "remark-gfm";
interface MarkdownContentProps {
slug: string;
}
export async function MarkdownContent({ slug }: MarkdownContentProps) {
// マークダウンファイルのパスを構築
const filePath = path.join(process.cwd(), "src", "post", `${slug}.md`);
// ファイルが存在するか確認
let fileContents;
try {
fileContents = fs.readFileSync(filePath, "utf8");
} catch (error) {
notFound();
}
// フロントマターを解析
const { data, content } = matter(fileContents);
return (
<article className="prose prose-base sm:prose-sm lg:prose-lg prose-slate dark:prose-invert rounded-lg p-4 sm:p-6 lg:p-8 mx-auto max-w-none sm:max-w-3xl lg:max-w-4xl bg-white/50 dark:bg-black/50">
{data.title && (
<h1 className="text-2xl sm:text-3xl lg:text-4xl mb-4">{data.title}</h1>
)}
{data.date && (
<p className="text-xs sm:text-sm text-gray-500 dark:text-gray-400 mb-3">
公開日: {new Date(data.date).toLocaleDateString("ja-JP")}
{data.author && ` • 著者: ${data.author}`}
</p>
)}
{data.tags && data.tags.length > 0 && (
<div className="flex flex-wrap gap-1 sm:gap-2 mb-4">
{data.tags.map((tag: string) => (
<span
key={tag}
className="px-2 py-0.5 sm:py-1 text-xs bg-gray-200 dark:bg-gray-700 rounded-full"
>
{tag}
</span>
))}
</div>
)}
<div className="mt-6">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
</div>
</article>
);
}
================
File: src/app/post/page.tsx
================
import { Card } from "./card";
import { getAllPosts } from "@/app/utils/post";
export default function Page() {
const posts = getAllPosts();
return (
<>
<h1 className="glitch text-2xl text-center p-4">Articles</h1>
<div className="w-10/12 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mx-auto">
{posts.map((post) => (
<Card
key={post.slug}
title={post.title}
description={post.description}
link={`/post/${post.slug}`}
date={post.date}
tags={post.tags}
thumbnail={post.thumbnail}
/>
))}
</div>
</>
);
}
================
File: src/app/utils/post.ts
================
import fs from "fs";
import path from "path";
import matter from "gray-matter";
export interface Post {
slug: string;
title: string;
description: string;
date: string;
tags?: string[];
author?: string;
[key: string]: any; // その他のフロントマターデータを許可
}
export function getAllPosts(): Post[] {
const postsDirectory = path.join(process.cwd(), "src", "post");
const filenames = fs.readdirSync(postsDirectory);
console.log("filenames", filenames);
const posts = filenames
.filter((filename) => filename.endsWith(".md"))
.map((filename) => {
// ファイル名からスラッグを取得(拡張子なし)
const slug = filename.replace(/.md$/, "");
// ファイルの内容を読み込む
const fullPath = path.join(postsDirectory, filename);
const fileContents = fs.readFileSync(fullPath, "utf8");
// gray-matterを使ってフロントマターを解析
const { data, content } = matter(fileContents);
// フロントマターからメタデータを取得または代替値を使用
const post: Post = {
slug,
title: data.title || getTitle(content, slug),
description: data.description || getDescription(content),
date: data.date ? data.date.toString() : "",
...data, // その他のフロントマターデータを含める
};
return post;
})
.sort((a, b) => {
// 日付でソート(降順)
const dateA = a.date ? new Date(a.date).getTime() : 0;
const dateB = b.date ? new Date(b.date).getTime() : 0;
return dateB - dateA;
});
return posts;
}
// マークダウン本文からタイトルを抽出(フロントマターがない場合のフォールバック)
function getTitle(content: string, slug: string): string {
const titleMatch = content.match(/^# (.*)/m);
return titleMatch ? titleMatch[1] : slug;
}
// マークダウン本文から説明を抽出(フロントマターがない場合のフォールバック)
function getDescription(content: string): string {
let description = "";
const introMatch = content.match(/## はじめにs+([^
]+)/);
if (introMatch) {
description = introMatch[1];
} else {
// はじめにセクションがなければ、最初の段落を使用
const paragraphs = content.split("
");
if (paragraphs.length > 1) {
description = paragraphs[1].replace(/*.**/, "").trim();
}
}
return description;
}
================
File: src/mastra/agents/index.ts
================
import { google } from '@ai-sdk/google';
import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { weatherTool } from '../tools';
export const weatherAgent = new Agent({
name: 'Weather Agent',
instructions: `
You are a helpful weather assistant that provides accurate weather information.
Your primary function is to help users get weather details for specific locations. When responding:
- Always ask for a location if none is provided
- If the location name isn’t in English, please translate it
- If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
- Include relevant details like humidity, wind conditions, and precipitation
- Keep responses concise but informative
Use the weatherTool to fetch current weather data.
`,
model: google('gemini-1.5-pro-latest'),
tools: { weatherTool },
memory: new Memory({
options: {
lastMessages: 10,
semanticRecall: false,
threads: {
generateTitle: false,
},
},
}),
});
================
File: src/mastra/tools/index.ts
================
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';
interface GeocodingResponse {
results: {
latitude: number;
longitude: number;
name: string;
}[];
}
interface WeatherResponse {
current: {
time: string;
temperature_2m: number;
apparent_temperature: number;
relative_humidity_2m: number;
wind_speed_10m: number;
wind_gusts_10m: number;
weather_code: number;
};
}
export const weatherTool = createTool({
id: 'get-weather',
description: 'Get current weather for a location',
inputSchema: z.object({
location: z.string().describe('City name'),
}),
outputSchema: z.object({
temperature: z.number(),
feelsLike: z.number(),
humidity: z.number(),
windSpeed: z.number(),
windGust: z.number(),
conditions: z.string(),
location: z.string(),
}),
execute: async ({ context }) => {
return await getWeather(context.location);
},
});
const getWeather = async (location: string) => {
const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(location)}&count=1`;
const geocodingResponse = await fetch(geocodingUrl);
const geocodingData = (await geocodingResponse.json()) as GeocodingResponse;
if (!geocodingData.results?.[0]) {
throw new Error(`Location '${location}' not found`);
}
const { latitude, longitude, name } = geocodingData.results[0];
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code`;
const response = await fetch(weatherUrl);
const data = (await response.json()) as WeatherResponse;
return {
temperature: data.current.temperature_2m,
feelsLike: data.current.apparent_temperature,
humidity: data.current.relative_humidity_2m,
windSpeed: data.current.wind_speed_10m,
windGust: data.current.wind_gusts_10m,
conditions: getWeatherCondition(data.current.weather_code),
location: name,
};
};
function getWeatherCondition(code: number): string {
const conditions: Record<number, string> = {
0: 'Clear sky',
1: 'Mainly clear',
2: 'Partly cloudy',
3: 'Overcast',
45: 'Foggy',
48: 'Depositing rime fog',
51: 'Light drizzle',
53: 'Moderate drizzle',
55: 'Dense drizzle',
56: 'Light freezing drizzle',
57: 'Dense freezing drizzle',
61: 'Slight rain',
63: 'Moderate rain',
65: 'Heavy rain',
66: 'Light freezing rain',
67: 'Heavy freezing rain',
71: 'Slight snow fall',
73: 'Moderate snow fall',
75: 'Heavy snow fall',
77: 'Snow grains',
80: 'Slight rain showers',
81: 'Moderate rain showers',
82: 'Violent rain showers',
85: 'Slight snow showers',
86: 'Heavy snow showers',
95: 'Thunderstorm',
96: 'Thunderstorm with slight hail',
99: 'Thunderstorm with heavy hail',
};
return conditions[code] || 'Unknown';
}
================
File: src/mastra/types/daily-papers.ts
================
import { z } from "zod";
export interface Author {
_id: string;
user?: {
_id: string;
avatarUrl: string;
isPro: boolean;
fullname: string;
user: string;
type: string;
};
name: string;
status?: string;
statusLastChangedAt?: string;
hidden: boolean;
}
export interface Paper {
id: string;
authors: Author[];
publishedAt: string;
submittedOnDailyAt?: string;
title: string;
submittedOnDailyBy?: {
_id: string;
avatarUrl: string;
isPro: boolean;
fullname: string;
user: string;
type: string;
};
summary: string;
upvotes?: number;
discussionId?: string;
projectPage?: string;
githubRepo?: string;
mediaUrls?: string[];
ai_keywords?: string[];
}
export interface PaperEntry {
paper: Paper;
publishedAt: string;
title: string;
summary: string;
mediaUrls?: string[];
thumbnail?: string;
numComments?: number;
submittedBy?: {
_id: string;
avatarUrl: string;
fullname: string;
name: string;
type: string;
isPro: boolean;
isHf?: boolean;
isHfAdmin?: boolean;
isMod?: boolean;
followerCount?: number;
};
isAuthorParticipating?: boolean;
}
export interface FormatedPaper {
id: string;
authors: string[];
publishedAt: string;
title: string;
summary: string;
projectPage?: string;
githubRepo?: string;
mediaUrls?: string[];
ai_keywords?: string[];
}
export interface SummaryPaper extends FormatedPaper {
aiSummary: {
summary: string;
comparison: string;
importantPoints: string;
effectiveness: string;
discussion: string;
nextPaper: string;
};
}
export interface PapersList extends Array<PaperEntry> {}
export interface FormatedPapersList extends Array<FormatedPaper> {}
const authorSchema = z.object({
_id: z.string(),
user: z
.object({
_id: z.string(),
avatarUrl: z.string(),
isPro: z.boolean(),
fullname: z.string(),
user: z.string(),
type: z.string(),
})
.optional(),
name: z.string(),
status: z.string().optional(),
statusLastChangedAt: z.string().optional(),
hidden: z.boolean(),
});
const paperSchema = z.object({
id: z.string(),
authors: z.array(authorSchema),
publishedAt: z.string(),
submittedOnDailyAt: z.string().optional(),
title: z.string(),
submittedOnDailyBy: z
.object({
_id: z.string(),
avatarUrl: z.string(),
isPro: z.boolean(),
fullname: z.string(),
user: z.string(),
type: z.string(),
})
.optional(),
summary: z.string(),
upvotes: z.number().optional(),
discussionId: z.string().optional(),
projectPage: z.string().optional(),
githubRepo: z.string().optional(),
mediaUrls: z.array(z.string()).optional(),
ai_keywords: z.array(z.string()).optional(),
});
const paperEntrySchema = z.object({
paper: paperSchema,
publishedAt: z.string(),
title: z.string(),
summary: z.string(),
mediaUrls: z.array(z.string()).optional(),
thumbnail: z.string().optional(),
numComments: z.number().optional(),
submittedBy: z
.object({
_id: z.string(),
avatarUrl: z.string(),
fullname: z.string(),
name: z.string(),
type: z.string(),
isPro: z.boolean(),
isHf: z.boolean().optional(),
isHfAdmin: z.boolean().optional(),
isMod: z.boolean().optional(),
followerCount: z.number().optional(),
})
.optional(),
isAuthorParticipating: z.boolean().optional(),
});
const formatedPaperSchema = z.object({
id: z.string(),
authors: z.array(z.string()),
publishedAt: z.string(),
title: z.string(),
summary: z.string(),
upvotes: z.number().optional(),
discussionId: z.string().optional(),
projectPage: z.string().optional(),
githubRepo: z.string().optional(),
mediaUrls: z.array(z.string()).optional(),
ai_keywords: z.array(z.string()).optional(),
});
const summaryPaperSchema = z.object({
id: z.string(),
authors: z.array(z.string()),
publishedAt: z.string(),
title: z.string(),
summary: z.string(),
projectPage: z.string().optional(),
githubRepo: z.string().optional(),
mediaUrls: z.array(z.string()).optional(),
ai_keywords: z.array(z.string()).optional(),
aiSummary: z.object({
summary: z.string(),
comparison: z.string(),
importantPoints: z.string(),
effectiveness: z.string(),
discussion: z.string(),
nextPaper: z.string(),
}),
});
const papersListSchema = z.array(paperEntrySchema);
const formatedPapersListSchema = z.array(formatedPaperSchema);
export {
authorSchema,
paperSchema,
paperEntrySchema,
formatedPaperSchema,
papersListSchema,
formatedPapersListSchema,
summaryPaperSchema,
};
================
File: src/mastra/workflows/daily-papers-workflow.ts
================
import { Step, Workflow } from "@mastra/core/workflows";
import { z } from "zod";
import { google } from "@ai-sdk/google";
import { generateObject, generateText } from "ai";
// 型をインポート
import {
papersListSchema,
PapersList,
formatedPapersListSchema,
FormatedPapersList,
summaryPaperSchema,
SummaryPaper,
} from "../types/daily-papers";
const summaryModel = google("gemini-2.0-flash-001", {});
// huggingface.coからdailyの論文を取得する関数
const fetchDailyPapers = async (date: string) => {
const requestUrl = `https://huggingface.co/api/daily_papers?date=${date}`;
const dailyPapersResponse = await fetch(requestUrl);
const dailyPaperData = await dailyPapersResponse.json();
if (!dailyPaperData) {
throw new Error(`Date '${date}' not found`);
}
return dailyPaperData as PapersList;
};
// 論文を取得するステップ
const getDailyPapers = new Step({
id: "get-daily-papers",
description: "Fetches daily papers from Hugging Face",
inputSchema: z.object({
date: z.string().describe("Date for getting papers"),
}),
outputSchema: z.object({
papers: papersListSchema,
}),
execute: async ({ context }) => {
const triggerData = context?.getStepResult<{ date: string }>("trigger");
if (!triggerData) {
throw new Error("Trigger data not found");
}
const papers = await fetchDailyPapers(triggerData.date);
return { papers };
},
});
// 論文に関するデータを整形する関数
const formatPapers = (papers: PapersList): FormatedPapersList => {
return papers.map((paper) => ({
id: paper.paper.id,
authors: paper.paper.authors
? paper.paper.authors.map((author) => author.name || "")
: [],
publishedAt: paper.publishedAt,
title: paper.title,
summary: paper.summary,
projectPage: paper.paper.projectPage,
githubRepo: paper.paper.githubRepo,
mediaUrls: paper.paper.mediaUrls,
ai_keywords: paper.paper.ai_keywords || [],
}));
};
// 論文のデータを整形するステップ
const formatDailyPapers = new Step({
id: "format-daily-papers",
description: "Format daily papers",
inputSchema: z.object({
papers: papersListSchema,
}),
outputSchema: z.object({
papers: formatedPapersListSchema,
}),
execute: async ({ context }) => {
const result = context?.getStepResult<{ papers: PapersList }>(
"get-daily-papers"
);
if (!result || !result.papers) {
throw new Error("Papers data not found");
}
const formattedPapers = formatPapers(result.papers);
return { papers: formattedPapers };
},
});
// 論文の要約を落合フォーマットで生成する関数
const generateSummary = async (id: string) => {
const pdfUrl = `https://arxiv.org/pdf/${id}`;
const response = await generateObject({
model: summaryModel,
messages: [
{
role: "user",
content: [
{
type: "text",
text: `# Task
Please create a summary of the given paper.
When summarizing, please include the following perspectives:
* What is it in one sentence?
* What is impressive about it compared to previous research?
* What are the important points of the technology or methodology?
* How was the technology or methodology determined to be effective?
* Are there any points that should be discussed or criticized?
* What paper should be read next?
# Output
The output will be in JSON format including the above items.
{
summary: What is it in one sentence?
comparison: What is impressive about it compared to previous research?
importantPoints: What are the important points of the technology or methodology?
effectiveness: How was the technology or methodology determined to be effective?
discussion: Are there any points that should be discussed or criticized?
nextPaper: What paper should be read next? (paper reference)
}
# Important Notice
Please provide the output in Japanese.
Is there anything else you'd like me to adjust in the translation?
}`,
},
{
type: "file",
data: new URL(pdfUrl),
mimeType: "application/pdf",
},
],
},
],
schema: z.object({
summary: z.string().describe("ひとことでまとめるとどんなものですか?"),
comparison: z
.string()
.describe("先行研究と比較してどの点がすごいのですか?"),
importantPoints: z
.string()
.describe("技術や手法の重要な点はどこにありますか?"),
effectiveness: z
.string()
.describe("技術や手法はどのように有効だと判断されましたか?"),
discussion: z.string().describe("何か議論や批判すべき点はありますか?"),
nextPaper: z.string().describe("次に読むべき論文は何ですか?"),
}),
});
const summary = response.object;
if (!summary) {
throw new Error("Summary generation failed");
}
return summary;
};
// 論文の要約を落合フォーマットで生成するステップ
const generateSummaryStep = new Step({
id: "generate-summary",
description: "Generate summary for each paper",
inputSchema: z.object({
papers: formatedPapersListSchema,
}),
outputSchema: z.object({
summaries: z.array(summaryPaperSchema),
}),
execute: async ({ context }) => {
const result = context?.getStepResult<{ papers: FormatedPapersList }>(
"format-daily-papers"
);
if (!result || !result.papers) {
throw new Error("Papers data not found");
}
const summaries = await Promise.all(
result.papers.map(async (paper) => {
const summary = await generateSummary(paper.id);
return {
aiSummary: summary,
...paper,
} as SummaryPaper;
})
);
return { summaries };
},
});
// マークダウンで記事を作成する関数
const generateMarkdownArticle = async (
papers: SummaryPaper[]
): Promise<string> => {
const paperInfoString = papers
.map((paper) => {
const {
title,
authors,
publishedAt,
summary,
projectPage,
githubRepo,
mediaUrls,
aiSummary,
} = paper;
const authorsString = authors.join(", ");
const summaryString = aiSummary.summary;
const comparisonString = aiSummary.comparison;
const importantPointsString = aiSummary.importantPoints;
const effectivenessString = aiSummary.effectiveness;
const discussionString = aiSummary.discussion;
const nextPaperString = aiSummary.nextPaper;
const markdownString = `
# ${title}
## Authors
${authorsString}
## Summary
${summary}
## Published At
${publishedAt}
## Project Page
${projectPage || "N/A"}
## GitHub Repo
${githubRepo || "N/A"}
## Media URLs
${mediaUrls?.length ? mediaUrls.join(", ") : "N/A"}
## Summary (AI)
${summaryString}
## Comparison (AI)
${comparisonString}
## Important Points (AI)
${importantPointsString}
## Effectiveness (AI)
${effectivenessString}
## Discussion (AI)
${discussionString}
## Next Paper (AI)
${nextPaperString}
`;
return markdownString;
})
.join("
---
");
const response = await generateText({
model: summaryModel,
messages: [
{
role: "user",
content: [
{
type: "text",
text: `# タスク
あなたには、本日のフィーチャーされた論文が複数与えられます。
その論文をもとに、マークダウン形式で記事を作成します。
記事には、以下の情報を含めてください。
`,
},
{
type: "text",
text: paperInfoString,
},
],
},
],
});
if (!response.text) {
throw new Error("Markdown generation failed");
}
return response.text;
};
// マークダウン記事を生成するステップ
const generateMarkdownArticleStep = new Step({
id: "generate-markdown-article",
description: "Generate markdown article from papers",
inputSchema: z.object({
summaries: z.array(summaryPaperSchema),
}),
outputSchema: z.object({
article: z.string().describe("Generated markdown article"),
}),
execute: async ({ context }) => {
const result = context?.getStepResult<{ summaries: SummaryPaper[] }>(
"generate-summary"
);
if (!result || !result.summaries) {
throw new Error("Summaries data not found");
}
const article = await generateMarkdownArticle(result.summaries);
return { article };
},
});
// ワークフローを定義
// 1. get-daily-papers ステップを実行して, 日付に基づいて論文を取得
// 2. format-daily-papers ステップを実行して, 取得した論文を整形
// 3. 論文のsummaryとpdfから, 落合陽一フォーマットで要約を作成
// 4. 論文のsummary, 要約から, マークダウンで記事を作成する
// 5. 作成した記事をfirebaseに保存する
const dailyPapersWorkflow = new Workflow({
name: "daily-papers-workflow",
triggerSchema: z.object({
date: z.string().describe("Date for getting papers"),
}),
})
.step(getDailyPapers)
.then(formatDailyPapers)
.then(generateSummaryStep)
.then(generateMarkdownArticleStep)
.commit();
export { dailyPapersWorkflow };
================
File: src/mastra/workflows/weather-workflow.ts
================
import { google } from "@ai-sdk/google";
import { Agent } from "@mastra/core/agent";
import { Step, Workflow } from "@mastra/core/workflows";
import { z } from "zod";
const llm = google("gemini-1.5-pro-latest");
const agent = new Agent({
name: "Weather Agent",
model: llm,
instructions: `
You are a local activities and travel expert who excels at weather-based planning. Analyze the weather data and provide practical activity recommendations.
For each day in the forecast, structure your response exactly as follows:
📅 [Day, Month Date, Year]
═══════════════════════════
🌡️ WEATHER SUMMARY
• Conditions: [brief description]
• Temperature: [X°C/Y°F to A°C/B°F]
• Precipitation: [X% chance]
🌅 MORNING ACTIVITIES
Outdoor:
• [Activity Name] - [Brief description including specific location/route]
Best timing: [specific time range]
Note: [relevant weather consideration]
🌞 AFTERNOON ACTIVITIES
Outdoor:
• [Activity Name] - [Brief description including specific location/route]
Best timing: [specific time range]
Note: [relevant weather consideration]
🏠 INDOOR ALTERNATIVES
• [Activity Name] - [Brief description including specific venue]
Ideal for: [weather condition that would trigger this alternative]
⚠️ SPECIAL CONSIDERATIONS
• [Any relevant weather warnings, UV index, wind conditions, etc.]
Guidelines:
- Suggest 2-3 time-specific outdoor activities per day
- Include 1-2 indoor backup options
- For precipitation >50%, lead with indoor activities
- All activities must be specific to the location
- Include specific venues, trails, or locations
- Consider activity intensity based on temperature
- Keep descriptions concise but informative
Maintain this exact formatting for consistency, using the emoji and section headers as shown.
`,
});
const forecastSchema = z.array(
z.object({
date: z.string(),
maxTemp: z.number(),
minTemp: z.number(),
precipitationChance: z.number(),
condition: z.string(),
location: z.string(),
})
);
const fetchWeather = new Step({
id: "fetch-weather",
description: "Fetches weather forecast for a given city",
inputSchema: z.object({
city: z.string().describe("The city to get the weather for"),
}),
outputSchema: forecastSchema,
execute: async ({ context }) => {
const triggerData = context?.getStepResult<{ city: string }>("trigger");
if (!triggerData) {
throw new Error("Trigger data not found");
}
const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(triggerData.city)}&count=1`;
const geocodingResponse = await fetch(geocodingUrl);
const geocodingData = (await geocodingResponse.json()) as {
results: { latitude: number; longitude: number; name: string }[];
};
if (!geocodingData.results?.[0]) {
throw new Error(`Location '${triggerData.city}' not found`);
}
const { latitude, longitude, name } = geocodingData.results[0];
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=temperature_2m_max,temperature_2m_min,precipitation_probability_mean,weathercode&timezone=auto`;
const response = await fetch(weatherUrl);
const data = (await response.json()) as {
daily: {
time: string[];
temperature_2m_max: number[];
temperature_2m_min: number[];
precipitation_probability_mean: number[];
weathercode: number[];
};
};
const forecast = data.daily.time.map((date: string, index: number) => ({
date,
maxTemp: data.daily.temperature_2m_max[index],
minTemp: data.daily.temperature_2m_min[index],
precipitationChance: data.daily.precipitation_probability_mean[index],
condition: getWeatherCondition(data.daily.weathercode[index]!),
location: name,
}));
return forecast;
},
});
const planActivities = new Step({
id: "plan-activities",
description: "Suggests activities based on weather conditions",
execute: async ({ context, mastra }) => {
const forecast = context?.getStepResult(fetchWeather);
if (!forecast || forecast.length === 0) {
throw new Error("Forecast data not found");
}
const prompt = `Based on the following weather forecast for ${forecast[0]?.location}, suggest appropriate activities:
${JSON.stringify(forecast, null, 2)}
`;
const response = await agent.stream([
{
role: "user",
content: prompt,
},
]);
let activitiesText = "";
for await (const chunk of response.textStream) {
process.stdout.write(chunk);
activitiesText += chunk;
}
return {
activities: activitiesText,
};
},
});
function getWeatherCondition(code: number): string {
const conditions: Record<number, string> = {
0: "Clear sky",
1: "Mainly clear",
2: "Partly cloudy",
3: "Overcast",
45: "Foggy",
48: "Depositing rime fog",
51: "Light drizzle",
53: "Moderate drizzle",
55: "Dense drizzle",
61: "Slight rain",
63: "Moderate rain",
65: "Heavy rain",
71: "Slight snow fall",
73: "Moderate snow fall",
75: "Heavy snow fall",
95: "Thunderstorm",
};
return conditions[code] || "Unknown";
}
const weatherWorkflow = new Workflow({
name: "weather-workflow",
triggerSchema: z.object({
city: z.string().describe("The city to get the weather for"),
}),
})
.step(fetchWeather)
.then(planActivities);
weatherWorkflow.commit();
export { weatherWorkflow };
================
File: src/post/ai-future.md
================
---
title: "AIとデータサイエンスの未来"
description: "人工知能(AI)とデータサイエンスの分野は急速に発展しており、私たちの生活や仕事に大きな影響を与えています。この記事では、AIとデータサイエンスの未来について考察します。"
date: "2023-10-01"
author: "hirakawa"
tags: ["AI", "データサイエンス", "機械学習", "将来展望"]
---
## はじめに
人工知能(AI)とデータサイエンスの分野は急速に発展しており、私たちの生活や仕事に大きな影響を与えています。この記事では、AI とデータサイエンスの未来について考察します。
## 現在のトレンド
現在、以下のようなトレンドが AI とデータサイエンスの分野で見られます:
- **大規模言語モデル (LLM)**: GPT-4 などの大規模言語モデルは、テキスト生成や理解の能力を大幅に向上させています。
- **生成 AI**: 画像、音声、テキストなどを生成できる AI の発展が目覚ましいです。
- **エッジ AI**: デバイス上で直接 AI を実行することで、プライバシーやレイテンシの問題を解決する取り組みが進んでいます。
## 将来の展望
今後 5〜10 年の間に、以下のような発展が予想されます:
1. **AI とヒトの協働**: AI はヒトの能力を拡張し、より創造的なタスクに集中できるようにサポートするツールとして進化していくでしょう。
2. **特化型 AI**: 特定のドメインに特化した AI モデルがより普及し、専門的なタスクでの性能が向上します。
3. **倫理とガバナンス**: AI の発展に伴い、倫理的な問題やガバナンスの重要性がさらに高まるでしょう。
## まとめ
AI とデータサイエンスの未来は非常に明るく、様々な可能性を秘めています。しかし、技術の発展と同時に、社会的な影響や倫理的な側面も考慮することが重要です。
================
File: src/post/generative-models.md
================
---
title: "生成モデルの基礎"
description: "生成モデル(Generative Models)は、機械学習の中でも特に注目されている分野です。この記事では、生成モデルの基本的な概念と応用について説明します。"
date: "2023-10-05"
author: "平川"
tags: ["生成モデル", "機械学習", "GAN", "VAE", "Diffusion Models"]
---
## はじめに
生成モデル(Generative Models)は、機械学習の中でも特に注目されている分野です。この記事では、生成モデルの基本的な概念と応用について説明します。
## 生成モデルとは
生成モデルとは、データの分布を学習し、その分布に従った新しいデータを生成することができるモデルのことです。対照的に、識別モデル(Discriminative Models)は入力データから直接ラベルや値を予測します。
## 主な生成モデルの種類
### GAN (Generative Adversarial Networks)
GAN は 2014 年に Ian Goodfellow によって提案された生成モデルで、Generator(生成器)と Discriminator(識別器)の 2 つのネットワークが敵対的に学習を行います。
```
Generator: 偽のデータを生成
Discriminator: 本物と偽物を識別
```
この敵対的な学習によって、より本物に近いデータが生成できるようになります。
### VAE (Variational Autoencoders)
VAE は、データの潜在表現(latent representation)を学習し、その潜在空間から新しいデータを生成するモデルです。エンコーダとデコーダの構造を持ちます。
### Diffusion Models
最近注目を集めている Diffusion Models は、ノイズを少しずつ除去していくことでデータを生成するアプローチです。Stable Diffusion や DALL-E 2 などの画像生成モデルに利用されています。
## 生成モデルの応用例
- **画像生成**: 写実的な画像や芸術作品の生成
- **テキスト生成**: 文章やコードの自動生成
- **音声合成**: 人間の声や音楽の生成
- **動画生成**: 短い動画やアニメーションの生成
- **薬剤設計**: 新しい分子構造の提案
## まとめ
生成モデルは急速に発展しており、様々な分野で革新的な応用が進んでいます。基本的な概念を理解することで、これらのテクノロジーをより深く理解し、活用することができるでしょう.
================
File: postcss.config.mjs
================
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;
================
File: README.md
================
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
================
File: tsconfig.json
================
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
================
File: src/app/globals.css
================
@import "tailwindcss";
@tailwind base;
@tailwind components;
@tailwind utilities;
@custom-variant dark (&:where(.dark, .dark *));
@keyframes movement {
0% {
top: 0px;
left: -20px;
}
15% {
top: 10px;
left: 10px;
}
60% {
top: 5px;
left: -10px;
}
75% {
top: -5px;
left: 20px;
}
100% {
top: 10px;
left: 5px;
}
}
.glitch {
/* font-size: 5rem; */
font-weight: bold;
text-transform: uppercase;
position: relative;
text-shadow:
0.05em 0 0 #00fffc,
-0.03em -0.04em 0 #fc00ff,
0.025em 0.04em 0 #fffc00;
animation: glitch 725ms infinite;
}
.glitch span {
position: absolute;
top: 0;
left: 0;
}
.glitch span:first-child {
animation: glitch 500ms infinite;
clip-path: polygon(0 0, 100% 0, 100% 35%, 0 35%);
transform: translate(-0.04em, -0.03em);
opacity: 0.75;
}
.glitch span:last-child {
animation: glitch 375ms infinite;
clip-path: polygon(0 65%, 100% 65%, 100% 100%, 0 100%);
transform: translate(0.04em, 0.03em);
opacity: 0.75;
}
@keyframes glitch {
0% {
text-shadow:
0.05em 0 0 #00fffc,
-0.03em -0.04em 0 #fc00ff,
0.025em 0.04em 0 #fffc00;
}
15% {
text-shadow:
0.05em 0 0 #00fffc,
-0.03em -0.04em 0 #fc00ff,
0.025em 0.04em 0 #fffc00;
}
16% {
text-shadow:
-0.05em -0.025em 0 #00fffc,
0.025em 0.035em 0 #fc00ff,
-0.05em -0.05em 0 #fffc00;
}
49% {
text-shadow:
-0.05em -0.025em 0 #00fffc,
0.025em 0.035em 0 #fc00ff,
-0.05em -0.05em 0 #fffc00;
}
50% {
text-shadow:
0.05em 0.035em 0 #00fffc,
0.03em 0 0 #fc00ff,
0 -0.04em 0 #fffc00;
}
99% {
text-shadow:
0.05em 0.035em 0 #00fffc,
0.03em 0 0 #fc00ff,
0 -0.04em 0 #fffc00;
}
100% {
text-shadow:
-0.05em 0 0 #00fffc,
-0.025em -0.04em 0 #fc00ff,
-0.04em -0.025em 0 #fffc00;
}
}
@plugin '@tailwindcss/typography';
================
File: src/app/layout.tsx
================
import type { Metadata } from "next";
import "./globals.css";
import { Header, Footer, ThemeToggle } from "./components/layout";
import { ThemeProvider } from "@/app/contexts/theme";
import { TextBackground } from "./components/background/text-background";
export const metadata: Metadata = {
title: "Reibniz.com",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ja" suppressHydrationWarning>
<body className="antialiased h-screen w-dvw overflow-x-hidden">
<ThemeProvider>
<ThemeToggle />
<TextBackground />
<Header />
{children}
<Footer />
</ThemeProvider>
</body>
</html>
);
}
================
File: src/app/page.tsx
================
import { getAllPosts } from "@/app/utils/post";
import { Card } from "./post/card";
export default function Home() {
const posts = getAllPosts();
return (
<div className="min-h-screen pb-16 flex flex-col items-center">
{/* ヒーローセクション */}
<div className="w-11/12 mx-auto h-screen flex flex-col justify-center items-center">
<div className="font-mono text-7xl glitch mb-4" data-text="ASOBIBA">
ASOBIBA
</div>
<div className="text-xl mb-16">
A playground of my thoughts and ideas.
</div>
<div className="animate-bounce mt-8">
<svg
className="w-6 h-6"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path d="M19 14l-7 7m0 0l-7-7m7 7V3"></path>
</svg>
</div>
</div>
{/* 記事一覧セクション */}
<div className="w-11/12 flex flex-col justify-center px-4 py-12">
<h2 className="text-3xl font-bold mb-8 text-center">Latest Articles</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{posts.map((post) => (
<Card
key={post.slug}
title={post.title}
description={post.description}
link={`/post/${post.slug}`}
date={post.date}
tags={post.tags}
thumbnail={post.thumbnail}
/>
))}
</div>
</div>
</div>
);
}
================
File: src/mastra/workflows/index.ts
================
export * from "./weather-workflow";
export * from "./daily-papers-workflow";
================
File: src/mastra/index.ts
================
import { Mastra } from "@mastra/core/mastra";
import { createLogger } from "@mastra/core/logger";
import { LibSQLStore } from "@mastra/libsql";
import { weatherWorkflow, dailyPapersWorkflow } from "./workflows";
import { weatherAgent } from "./agents";
export const mastra = new Mastra({
workflows: { weatherWorkflow, dailyPapersWorkflow },
agents: { weatherAgent },
storage: new LibSQLStore({
// stores telemetry, evals, ... into memory storage, if it needs to persist, change to file:../mastra.db
url: ":memory:",
}),
logger: createLogger({
name: "Mastra",
level: "info",
}),
});
================
File: .gitignore
================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.mastra/
================
File: next.config.ts
================
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
serverExternalPackages: ["@mastra/*"],
};
export default nextConfig;
================
File: package.json
================
{
"name": "reibniz-site",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"dev:mastra": "mastra dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@ai-sdk/google": "^1.2.14",
"@mastra/core": "^0.9.1",
"@mastra/libsql": "^0.0.1",
"@mastra/memory": "^0.3.1",
"@tailwindcss/typography": "^0.5.16",
"ai": "^4.3.12",
"gray-matter": "^4.0.3",
"lucide-react": "^0.503.0",
"mastra": "^0.6.0",
"next": "15.3.1",
"next-themes": "^0.4.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"typescript": "^5"
}
}
================================================================
End of Codebase
================================================================
生成モデルの基礎
公開日: 2023/10/5 • 著者: Reibniz
生成モデル機械学習GANVAEDiffusion Models
はじめに
生成モデル(Generative Models)は、機械学習の中でも特に注目されている分野です。この記事では、生成モデルの基本的な概念と応用について説明します。
生成モデルとは
生成モデルとは、データの分布を学習し、その分布に従った新しいデータを生成することができるモデルのことです。対照的に、識別モデル(Discriminative Models)は入力データから直接ラベルや値を予測します。
主な生成モデルの種類
GAN (Generative Adversarial Networks)
GAN は 2014 年に Ian Goodfellow によって提案された生成モデルで、Generator(生成器)と Discriminator(識別器)の 2 つのネットワークが敵対的に学習を行います。
Generator: 偽のデータを生成
Discriminator: 本物と偽物を識別
この敵対的な学習によって、より本物に近いデータが生成できるようになります。
VAE (Variational Autoencoders)
VAE は、データの潜在表現(latent representation)を学習し、その潜在空間から新しいデータを生成するモデルです。エンコーダとデコーダの構造を持ちます。
Diffusion Models
最近注目を集めている Diffusion Models は、ノイズを少しずつ除去していくことでデータを生成するアプローチです。Stable Diffusion や DALL-E 2 などの画像生成モデルに利用されています。
生成モデルの応用例
- 画像生成: 写実的な画像や芸術作品の生成
- テキスト生成: 文章やコードの自動生成
- 音声合成: 人間の声や音楽の生成
- 動画生成: 短い動画やアニメーションの生成
- 薬剤設計: 新しい分子構造の提案
まとめ
生成モデルは急速に発展しており、様々な分野で革新的な応用が進んでいます。基本的な概念を理解することで、これらのテクノロジーをより深く理解し、活用することができるでしょう.