goofmint 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +17 -0
- package/.github/workflows/deploy.yml +44 -0
- package/.nvmrc +1 -0
- package/.ruby-version +1 -0
- package/DEPLOYMENT.md +111 -0
- package/Gemfile +10 -0
- package/README.md +118 -0
- package/_config.yml +77 -0
- package/_data/i18n.yml +86 -0
- package/_data/social.yml +19 -0
- package/_includes/footer.html +18 -0
- package/_includes/header.html +42 -0
- package/_includes/i18n.html +2 -0
- package/_layouts/default.html +25 -0
- package/_layouts/post.html +18 -0
- package/_layouts/project.html +37 -0
- package/_posts/2025-01-03-welcome.md +62 -0
- package/_posts/en/2025-01-03-welcome.md +61 -0
- package/_projects/bug-sniper.md +28 -0
- package/_projects/caiz.md +29 -0
- package/_projects/drowl.md +28 -0
- package/_projects/incho-app.md +32 -0
- package/_projects/moongift.md +33 -0
- package/_projects/review-game.md +28 -0
- package/_projects/sheetdb.md +30 -0
- package/_projects/text2cal.md +49 -0
- package/_projects_en/bug-sniper.md +28 -0
- package/_projects_en/caiz.md +29 -0
- package/_projects_en/drowl.md +28 -0
- package/_projects_en/incho-app.md +32 -0
- package/_projects_en/moongift.md +33 -0
- package/_projects_en/review-game.md +28 -0
- package/_projects_en/sheetdb.md +30 -0
- package/_projects_en/text2cal.md +49 -0
- package/_sass/_base.scss +58 -0
- package/_sass/_components.scss +113 -0
- package/_sass/_layout.scss +140 -0
- package/_sass/_syntax.scss +137 -0
- package/_sass/_theme.scss +261 -0
- package/about.md +182 -0
- package/assets/css/main.scss +68 -0
- package/assets/images/atsushi.jpg +0 -0
- package/assets/images/daruma.jpeg +0 -0
- package/assets/images/icons/email.svg +1 -0
- package/assets/images/icons/github.svg +1 -0
- package/assets/images/icons/linkedin.svg +1 -0
- package/assets/images/icons/x.svg +1 -0
- package/assets/images/moveum.jpeg +0 -0
- package/assets/images/notes.jpeg +0 -0
- package/assets/js/lang-toggle.js +89 -0
- package/assets/js/theme-toggle.js +69 -0
- package/blog.html +21 -0
- package/en/about.md +180 -0
- package/en/blog.html +21 -0
- package/en/index.html +45 -0
- package/en/projects.html +26 -0
- package/index.html +45 -0
- package/index.js +321 -0
- package/package.json +43 -0
- package/projects.html +29 -0
package/en/index.html
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Home
|
|
4
|
+
lang: en
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
{% include i18n.html %}
|
|
8
|
+
|
|
9
|
+
<section class="hero">
|
|
10
|
+
<h1>{{ t.home.hero_title }}</h1>
|
|
11
|
+
<p>{{ t.home.hero_description }}</p>
|
|
12
|
+
</section>
|
|
13
|
+
|
|
14
|
+
<section class="intro">
|
|
15
|
+
<h2>{{ t.home.about_title }}</h2>
|
|
16
|
+
<p>{{ t.home.about_description }}</p>
|
|
17
|
+
</section>
|
|
18
|
+
|
|
19
|
+
<section class="projects-preview">
|
|
20
|
+
<h2>{{ t.home.featured_projects }}</h2>
|
|
21
|
+
<div class="project-grid">
|
|
22
|
+
{% for project in site.projects_en limit:3 %}
|
|
23
|
+
<div class="project-card">
|
|
24
|
+
<h3><a href="{{ project.url | relative_url }}">{{ project.title }}</a></h3>
|
|
25
|
+
<p>{{ project.excerpt }}</p>
|
|
26
|
+
</div>
|
|
27
|
+
{% endfor %}
|
|
28
|
+
</div>
|
|
29
|
+
<a href="{{ "/en/projects/" | relative_url }}" class="btn">{{ t.home.view_all_projects }}</a>
|
|
30
|
+
</section>
|
|
31
|
+
|
|
32
|
+
<section class="blog-preview">
|
|
33
|
+
<h2>{{ t.home.latest_posts }}</h2>
|
|
34
|
+
<ul class="post-list">
|
|
35
|
+
{% assign en_posts = site.posts | where: "lang", "en" %}
|
|
36
|
+
{% for post in en_posts limit:3 %}
|
|
37
|
+
<li>
|
|
38
|
+
<h3><a href="{{ post.url | relative_url }}">{{ post.title }}</a></h3>
|
|
39
|
+
<p class="post-meta">{{ post.date | date: "%B %d, %Y" }}</p>
|
|
40
|
+
<p>{{ post.excerpt }}</p>
|
|
41
|
+
</li>
|
|
42
|
+
{% endfor %}
|
|
43
|
+
</ul>
|
|
44
|
+
<a href="{{ "/en/blog/" | relative_url }}" class="btn">{{ t.home.view_all_posts }}</a>
|
|
45
|
+
</section>
|
package/en/projects.html
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Projects
|
|
4
|
+
permalink: /en/projects/
|
|
5
|
+
lang: en
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
{% include i18n.html %}
|
|
9
|
+
|
|
10
|
+
<h1>{{ t.projects.title }}</h1>
|
|
11
|
+
|
|
12
|
+
<div class="project-grid">
|
|
13
|
+
{% for project in site.projects_en %}
|
|
14
|
+
<div class="project-card">
|
|
15
|
+
<h2><a href="{{ project.url | relative_url }}">{{ project.title }}</a></h2>
|
|
16
|
+
{% if project.tech %}
|
|
17
|
+
<div class="project-tech">
|
|
18
|
+
{% for tech in project.tech %}
|
|
19
|
+
<span class="tech-tag">{{ tech }}</span>
|
|
20
|
+
{% endfor %}
|
|
21
|
+
</div>
|
|
22
|
+
{% endif %}
|
|
23
|
+
<p>{{ project.excerpt }}</p>
|
|
24
|
+
</div>
|
|
25
|
+
{% endfor %}
|
|
26
|
+
</div>
|
package/index.html
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Home
|
|
4
|
+
lang: ja
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
{% include i18n.html %}
|
|
8
|
+
|
|
9
|
+
<section class="hero">
|
|
10
|
+
<h1>{{ t.home.hero_title }}</h1>
|
|
11
|
+
<p>{{ t.home.hero_description }}</p>
|
|
12
|
+
</section>
|
|
13
|
+
|
|
14
|
+
<section class="intro">
|
|
15
|
+
<h2>{{ t.home.about_title }}</h2>
|
|
16
|
+
<p>{{ t.home.about_description }}</p>
|
|
17
|
+
</section>
|
|
18
|
+
|
|
19
|
+
<section class="projects-preview">
|
|
20
|
+
<h2>{{ t.home.featured_projects }}</h2>
|
|
21
|
+
<div class="project-grid">
|
|
22
|
+
{% for project in site.projects limit:3 %}
|
|
23
|
+
<div class="project-card">
|
|
24
|
+
<h3><a href="{{ project.url | relative_url }}">{{ project.title }}</a></h3>
|
|
25
|
+
<p>{{ project.excerpt }}</p>
|
|
26
|
+
</div>
|
|
27
|
+
{% endfor %}
|
|
28
|
+
</div>
|
|
29
|
+
<a href="{{ "/projects/" | relative_url }}" class="btn">{{ t.home.view_all_projects }}</a>
|
|
30
|
+
</section>
|
|
31
|
+
|
|
32
|
+
<section class="blog-preview">
|
|
33
|
+
<h2>{{ t.home.latest_posts }}</h2>
|
|
34
|
+
<ul class="post-list">
|
|
35
|
+
{% assign ja_posts = site.posts | where: "lang", "ja" %}
|
|
36
|
+
{% for post in ja_posts limit:3 %}
|
|
37
|
+
<li>
|
|
38
|
+
<h3><a href="{{ post.url | relative_url }}">{{ post.title }}</a></h3>
|
|
39
|
+
<p class="post-meta">{{ post.date | date: "%Y年%m月%d日" }}</p>
|
|
40
|
+
<p>{{ post.excerpt }}</p>
|
|
41
|
+
</li>
|
|
42
|
+
{% endfor %}
|
|
43
|
+
</ul>
|
|
44
|
+
<a href="{{ "/blog/" | relative_url }}" class="btn">{{ t.home.view_all_posts }}</a>
|
|
45
|
+
</section>
|
package/index.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const matter = require('gray-matter');
|
|
8
|
+
const { unified } = require('unified');
|
|
9
|
+
const remarkParse = require('remark-parse');
|
|
10
|
+
const remarkGfm = require('remark-gfm');
|
|
11
|
+
const remarkStringify = require('remark-stringify');
|
|
12
|
+
|
|
13
|
+
const program = new Command();
|
|
14
|
+
|
|
15
|
+
// Process markdown for terminal display
|
|
16
|
+
function processMarkdown(markdown) {
|
|
17
|
+
// Remove Liquid template tags
|
|
18
|
+
let processed = markdown.replace(/\{%.*?%\}/g, '');
|
|
19
|
+
processed = processed.replace(/\{\{.*?\}\}/g, '');
|
|
20
|
+
|
|
21
|
+
// Split into lines for processing
|
|
22
|
+
const lines = processed.split('\n');
|
|
23
|
+
const output = [];
|
|
24
|
+
|
|
25
|
+
let inCodeBlock = false;
|
|
26
|
+
let codeBlockLang = '';
|
|
27
|
+
|
|
28
|
+
for (let line of lines) {
|
|
29
|
+
// Code blocks
|
|
30
|
+
if (line.startsWith('```')) {
|
|
31
|
+
inCodeBlock = !inCodeBlock;
|
|
32
|
+
if (inCodeBlock) {
|
|
33
|
+
codeBlockLang = line.substring(3);
|
|
34
|
+
output.push(chalk.gray('┌─ ' + (codeBlockLang || 'code')));
|
|
35
|
+
} else {
|
|
36
|
+
output.push(chalk.gray('└─'));
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (inCodeBlock) {
|
|
42
|
+
output.push(chalk.cyan('│ ' + line));
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Headers
|
|
47
|
+
if (line.startsWith('#### ')) {
|
|
48
|
+
output.push('\n' + chalk.yellow.bold(line.substring(5)));
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (line.startsWith('### ')) {
|
|
52
|
+
output.push('\n' + chalk.green.bold(line.substring(4)));
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (line.startsWith('## ')) {
|
|
56
|
+
output.push('\n' + chalk.cyan.bold(line.substring(3)));
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (line.startsWith('# ')) {
|
|
60
|
+
output.push('\n' + chalk.magenta.bold(line.substring(2)));
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Lists
|
|
65
|
+
if (line.match(/^[\s]*[-*+]\s/)) {
|
|
66
|
+
const indent = line.match(/^[\s]*/)[0].length;
|
|
67
|
+
const content = line.replace(/^[\s]*[-*+]\s/, '');
|
|
68
|
+
output.push(' '.repeat(Math.floor(indent / 2)) + chalk.green('• ') + content);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Ordered lists
|
|
73
|
+
if (line.match(/^[\s]*\d+\.\s/)) {
|
|
74
|
+
const match = line.match(/^([\s]*)(\d+)\.\s(.*)$/);
|
|
75
|
+
if (match) {
|
|
76
|
+
const [, indent, num, content] = match;
|
|
77
|
+
output.push(' '.repeat(Math.floor(indent.length / 2)) + chalk.yellow(num + '. ') + content);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Blockquotes
|
|
83
|
+
if (line.startsWith('> ')) {
|
|
84
|
+
output.push(chalk.gray('┃ ') + chalk.italic(line.substring(2)));
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Horizontal rules
|
|
89
|
+
if (line.match(/^[-*_]{3,}$/)) {
|
|
90
|
+
output.push(chalk.gray('─'.repeat(50)));
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Links - simple format [text](url)
|
|
95
|
+
line = line.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, url) => {
|
|
96
|
+
return chalk.blue.underline(text) + chalk.gray(' (' + url + ')');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Bold
|
|
100
|
+
line = line.replace(/\*\*([^*]+)\*\*/g, (match, text) => chalk.bold(text));
|
|
101
|
+
line = line.replace(/__([^_]+)__/g, (match, text) => chalk.bold(text));
|
|
102
|
+
|
|
103
|
+
// Italic
|
|
104
|
+
line = line.replace(/\*([^*]+)\*/g, (match, text) => chalk.italic(text));
|
|
105
|
+
line = line.replace(/_([^_]+)_/g, (match, text) => chalk.italic(text));
|
|
106
|
+
|
|
107
|
+
// Inline code
|
|
108
|
+
line = line.replace(/`([^`]+)`/g, (match, code) => chalk.cyan.bgBlack(' ' + code + ' '));
|
|
109
|
+
|
|
110
|
+
// Images - just show alt text
|
|
111
|
+
line = line.replace(/!\[([^\]]*)\]\([^)]+\)/g, (match, alt) => {
|
|
112
|
+
return chalk.gray('🖼️ ' + (alt || 'Image'));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Skip empty HTML iframes
|
|
116
|
+
if (line.match(/^<iframe/)) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
output.push(line);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return output.join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Determine language based on options
|
|
127
|
+
function getLang(options) {
|
|
128
|
+
return options.lang || 'en';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Read and display about.md
|
|
132
|
+
function showAbout(options) {
|
|
133
|
+
const lang = getLang(options);
|
|
134
|
+
const aboutPath = lang === 'ja'
|
|
135
|
+
? path.join(__dirname, 'about.md')
|
|
136
|
+
: path.join(__dirname, 'en', 'about.md');
|
|
137
|
+
|
|
138
|
+
if (!fs.existsSync(aboutPath)) {
|
|
139
|
+
console.error(chalk.red(`About file not found: ${aboutPath}`));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const content = fs.readFileSync(aboutPath, 'utf8');
|
|
144
|
+
const { data, content: markdown } = matter(content);
|
|
145
|
+
|
|
146
|
+
console.log(chalk.cyan.bold('\n' + data.title + '\n'));
|
|
147
|
+
console.log(processMarkdown(markdown));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// List projects
|
|
151
|
+
function listProjects(options) {
|
|
152
|
+
const lang = getLang(options);
|
|
153
|
+
const projectsDir = lang === 'ja'
|
|
154
|
+
? path.join(__dirname, '_projects')
|
|
155
|
+
: path.join(__dirname, '_projects_en');
|
|
156
|
+
|
|
157
|
+
if (!fs.existsSync(projectsDir)) {
|
|
158
|
+
console.error(chalk.red(`Projects directory not found: ${projectsDir}`));
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const files = fs.readdirSync(projectsDir).filter(f => f.endsWith('.md'));
|
|
163
|
+
|
|
164
|
+
console.log(chalk.cyan.bold('\n📁 Projects\n'));
|
|
165
|
+
|
|
166
|
+
files.forEach(file => {
|
|
167
|
+
const filePath = path.join(projectsDir, file);
|
|
168
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
169
|
+
const { data } = matter(content);
|
|
170
|
+
const slug = file.replace('.md', '');
|
|
171
|
+
|
|
172
|
+
console.log(chalk.green(' • ') + chalk.bold(data.title) + chalk.gray(` (${slug})`));
|
|
173
|
+
if (data.description) {
|
|
174
|
+
console.log(chalk.gray(' ' + data.description));
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
console.log();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Show project detail
|
|
182
|
+
function showProject(slug, options) {
|
|
183
|
+
const lang = getLang(options);
|
|
184
|
+
const projectsDir = lang === 'ja'
|
|
185
|
+
? path.join(__dirname, '_projects')
|
|
186
|
+
: path.join(__dirname, '_projects_en');
|
|
187
|
+
|
|
188
|
+
const projectPath = path.join(projectsDir, `${slug}.md`);
|
|
189
|
+
|
|
190
|
+
if (!fs.existsSync(projectPath)) {
|
|
191
|
+
console.error(chalk.red(`Project not found: ${slug}`));
|
|
192
|
+
console.log(chalk.yellow('\nAvailable projects:'));
|
|
193
|
+
listProjects(options);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const content = fs.readFileSync(projectPath, 'utf8');
|
|
198
|
+
const { data, content: markdown } = matter(content);
|
|
199
|
+
|
|
200
|
+
console.log(chalk.cyan.bold('\n' + data.title + '\n'));
|
|
201
|
+
|
|
202
|
+
if (data.description) {
|
|
203
|
+
console.log(chalk.gray(data.description) + '\n');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (data.tech) {
|
|
207
|
+
console.log(chalk.yellow('Tech: ') + data.tech.join(', ') + '\n');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (data.github) {
|
|
211
|
+
console.log(chalk.blue('GitHub: ') + data.github + '\n');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.log(processMarkdown(markdown));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// List blog posts
|
|
218
|
+
function listBlogPosts(options) {
|
|
219
|
+
const lang = getLang(options);
|
|
220
|
+
const postsDir = lang === 'ja'
|
|
221
|
+
? path.join(__dirname, '_posts')
|
|
222
|
+
: path.join(__dirname, '_posts', 'en');
|
|
223
|
+
|
|
224
|
+
if (!fs.existsSync(postsDir)) {
|
|
225
|
+
console.error(chalk.red(`Posts directory not found: ${postsDir}`));
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const files = fs.readdirSync(postsDir)
|
|
230
|
+
.filter(f => f.endsWith('.md'))
|
|
231
|
+
.sort()
|
|
232
|
+
.reverse();
|
|
233
|
+
|
|
234
|
+
console.log(chalk.cyan.bold('\n📝 Blog Posts\n'));
|
|
235
|
+
|
|
236
|
+
files.forEach(file => {
|
|
237
|
+
const filePath = path.join(postsDir, file);
|
|
238
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
239
|
+
const { data } = matter(content);
|
|
240
|
+
|
|
241
|
+
console.log(chalk.green(' • ') + chalk.bold(data.title) + chalk.gray(` (${file.replace('.md', '')})`));
|
|
242
|
+
if (data.date) {
|
|
243
|
+
console.log(chalk.gray(' ' + new Date(data.date).toLocaleDateString()));
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
console.log();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Show blog post detail
|
|
251
|
+
function showBlogPost(id, options) {
|
|
252
|
+
const lang = getLang(options);
|
|
253
|
+
const postsDir = lang === 'ja'
|
|
254
|
+
? path.join(__dirname, '_posts')
|
|
255
|
+
: path.join(__dirname, '_posts', 'en');
|
|
256
|
+
|
|
257
|
+
const postPath = path.join(postsDir, `${id}.md`);
|
|
258
|
+
|
|
259
|
+
if (!fs.existsSync(postPath)) {
|
|
260
|
+
console.error(chalk.red(`Blog post not found: ${id}`));
|
|
261
|
+
console.log(chalk.yellow('\nAvailable posts:'));
|
|
262
|
+
listBlogPosts(options);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const content = fs.readFileSync(postPath, 'utf8');
|
|
267
|
+
const { data, content: markdown } = matter(content);
|
|
268
|
+
|
|
269
|
+
console.log(chalk.cyan.bold('\n' + data.title + '\n'));
|
|
270
|
+
|
|
271
|
+
if (data.date) {
|
|
272
|
+
console.log(chalk.gray('Published: ' + new Date(data.date).toLocaleDateString()) + '\n');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
console.log(processMarkdown(markdown));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Setup CLI
|
|
279
|
+
program
|
|
280
|
+
.name('goofmint')
|
|
281
|
+
.description('CLI tool for goofmint.dev portfolio')
|
|
282
|
+
.version('1.0.0')
|
|
283
|
+
.option('-l, --lang <language>', 'Language (ja or en)', 'en');
|
|
284
|
+
|
|
285
|
+
// Default command (about)
|
|
286
|
+
program
|
|
287
|
+
.action((options) => {
|
|
288
|
+
showAbout(options);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Projects command
|
|
292
|
+
program
|
|
293
|
+
.command('projects [slug]')
|
|
294
|
+
.description('List projects or show project detail')
|
|
295
|
+
.action((slug, options) => {
|
|
296
|
+
const parentOptions = program.opts();
|
|
297
|
+
const combinedOptions = { ...parentOptions, ...options };
|
|
298
|
+
|
|
299
|
+
if (slug) {
|
|
300
|
+
showProject(slug, combinedOptions);
|
|
301
|
+
} else {
|
|
302
|
+
listProjects(combinedOptions);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Blog command
|
|
307
|
+
program
|
|
308
|
+
.command('blog [id]')
|
|
309
|
+
.description('List blog posts or show post detail')
|
|
310
|
+
.action((id, options) => {
|
|
311
|
+
const parentOptions = program.opts();
|
|
312
|
+
const combinedOptions = { ...parentOptions, ...options };
|
|
313
|
+
|
|
314
|
+
if (id) {
|
|
315
|
+
showBlogPost(id, combinedOptions);
|
|
316
|
+
} else {
|
|
317
|
+
listBlogPosts(combinedOptions);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "goofmint",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "個人ポートフォリオサイト",
|
|
5
|
+
"homepage": "https://github.com/goofmint/goofmint.dev#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/goofmint/goofmint.dev/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/goofmint/goofmint.dev.git"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"goofmint": "index.js"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Atsushi Nakatsugawa",
|
|
18
|
+
"type": "commonjs",
|
|
19
|
+
"main": "index.js",
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
22
|
+
"start": "bundle exec jekyll serve",
|
|
23
|
+
"build": "bundle exec jekyll build",
|
|
24
|
+
"deploy": "npm run build & wrangler pages publish ./_site --project-name=goofmint-dev --branch=main"
|
|
25
|
+
},
|
|
26
|
+
"assets": {
|
|
27
|
+
"directory": "./_site"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"chalk": "^4.1.2",
|
|
31
|
+
"commander": "^12.1.0",
|
|
32
|
+
"gray-matter": "^4.0.3",
|
|
33
|
+
"remark": "^15.0.1",
|
|
34
|
+
"remark-gfm": "^4.0.0",
|
|
35
|
+
"strip-markdown": "^6.0.0",
|
|
36
|
+
"remark-parse": "^11.0.0",
|
|
37
|
+
"remark-stringify": "^11.0.0",
|
|
38
|
+
"unified": "^11.0.4"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"wrangler": "^4.54.0"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/projects.html
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Projects
|
|
4
|
+
permalink: /projects/
|
|
5
|
+
lang: ja
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
{% include i18n.html %}
|
|
9
|
+
|
|
10
|
+
<h1>{{ t.projects.title }}</h1>
|
|
11
|
+
|
|
12
|
+
<div class="project-grid">
|
|
13
|
+
{% for project in site.projects %}
|
|
14
|
+
<div class="project-card">
|
|
15
|
+
<h2><a href="{{ project.url | relative_url }}">{{ project.title }}</a></h2>
|
|
16
|
+
{% if project.tech %}
|
|
17
|
+
<div class="project-tech">
|
|
18
|
+
{% if project.status %}
|
|
19
|
+
<span class="tech-tag status-{{ project.status | downcase }}">{{ project.status }}</span>
|
|
20
|
+
{% endif %}
|
|
21
|
+
{% for tech in project.tech %}
|
|
22
|
+
<span class="tech-tag">{{ tech }}</span>
|
|
23
|
+
{% endfor %}
|
|
24
|
+
</div>
|
|
25
|
+
{% endif %}
|
|
26
|
+
<p>{{ project.excerpt }}</p>
|
|
27
|
+
</div>
|
|
28
|
+
{% endfor %}
|
|
29
|
+
</div>
|