Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .githooks/pre-commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func main() {
os.Exit(0)
}

fileList := strings.Join(files, " ")
fileList := strings.ReplaceAll(strings.Join(files, " "), `$`, `\$`)
eslintCmdStr := fmt.Sprintf("npx eslint --fix %s", fileList)
var eslintCmd *exec.Cmd
if runtime.GOOS == "windows" {
Expand Down
16 changes: 16 additions & 0 deletions _routes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"version": 1,
"include": ["/blog/*"],
"exclude": [
"/assets/*",
"/**/*.js",
"/**/*.css",
"/**/*.map",
"/**/*.woff",
"/**/*.woff2",
"/**/*.ttf",
"/**/*.svg",
"/**/*.webp",
"/**/*.ico"
]
}
9 changes: 5 additions & 4 deletions app/components/Tag/Tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import './tag.css';
import { tagColorMap } from './tag-color-map';
import {useTranslation} from "react-i18next";

export type TagKind = "hackathon" | "recruitment" | "urgent";
export type TagKind = "hackathon" | "recruitment" | "urgent" | string;

export interface TagProps {
kind: TagKind;
className?: string;
}

export const Tag = ({ kind }: TagProps) => {
export const Tag = ({ kind, className }: TagProps) => {
const {text,backgroundColor} = tagColorMap[kind] || '#9e9e9e';
const { t } = useTranslation();
return (
<span className="tag" style={{ backgroundColor }}>
{t(text)}
<span className={["tag", className].join(" ")} style={{ backgroundColor: backgroundColor ?? "#42f5b0" }}>
{ text ? t(text) : kind }
</span>
);
};
7 changes: 6 additions & 1 deletion app/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,10 @@ export default [
index("routes/home.tsx"),
{
path: "blog",
file: "routes/blog/index.tsx" }
file: "routes/blog/index.tsx",
},
{
path: "blog/:id",
file: "routes/blog/$id.tsx"
}
] satisfies RouteConfig;
112 changes: 112 additions & 0 deletions app/routes/blog/$id.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
.container {
width: 100%;
height: 100%;
min-height: 100vh;
display: flex;
flex-direction: row-reverse;
justify-content: center;
background-color: #F5F5F5;

.article {
padding-top: 64px;
width: 100%;
max-width: 1200px;
}

.side {
padding-top: 200px;
width: 20%;
}

ul {
top: 200px;
list-style: none;
position: sticky;
}

li {
font-size: 16px;
}

.h1-table {
color: #616161;
}
.h2-table {
color: #8a8a8a;
li {
padding-left: 24px;
}
}

.article-container {
padding-top: 100px;
padding-bottom: 200px;
width: 60%;
height: auto;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}

.header-img {
object-fit: cover;
width: 100%;
max-width: 1200px;
border-radius: 12px;
box-shadow: 0 10px 10px #20202011;
}

h1 {
color: var(--main-text-color);
font-weight: 900;
font-size: 52px;
}

h2 {
color: var(--main-text-color);
font-weight: 700;
font-size: 32px;
}

h3 {
color: var(--main-text-color);
font-weight: 600;
font-size: 24px;
}

h1, h2, h3, h4, h5, h6 {
padding-left: 8px;
padding-bottom: 4px;
margin-bottom: 24px;
margin-top: 24px;
position: relative;
color: var(--main-text-color);
text-box-edge: cap alphabetic;
}

h1::before, h2::before {
content: "";
position: absolute;
display: block;
left: -24px;
width: 12px;
height: 100%;
background-color: var(--primery-color);
}

p {
color: var(--main-text-color);
letter-spacing: 2px;
font-size: 20px;
margin: 20px 0;
}
}

@media screen and (max-width: 700px) {
.container {
.article-container {
width: 90% !important;
}
}
}
104 changes: 104 additions & 0 deletions app/routes/blog/$id.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import hljs from "highlight.js";
import 'highlight.js/styles/github-dark.css';
import './custom-highlight.scss'
import { useEffect, useState, type JSX } from "react";
import { Header } from "~/components/Header/Header";
import styles from "./$id.module.scss"
import { useParams } from "react-router";
import { useIsMobile } from "~/hooks/useIsMobile";
import { custom_markdown_convert } from "~/utils/convert";

export default function Blog(): JSX.Element {
const { id } = useParams();
const [thumbnail] = useState<string | null>(null);
const [markdownSource, setMarkdownSource] = useState<string>("");
const [table, setTable] = useState<JSX.Element[]>([]);
const isMobile = useIsMobile();

const handleAnchorClick = (id: string) => (e: React.MouseEvent) => {
e.preventDefault();
const target = document.getElementById(id);
if (target) {
window.scrollTo({
top: target.getBoundingClientRect().top + window.scrollY - 80,
behavior: 'smooth',
});
}
};

useEffect(() => {
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/gh/rsms/markdown-wasm@v1.1.2/dist/markdown.js";
script.async = true;

document.body.appendChild(script);

script.onload = async () => {
await markdown.ready;
await fetch(`https://raw.githubusercontent.com/object-t/object-t-blog/refs/heads/main/articles/${id}.md`)
.then((res) => res.text())
.then((md) => {
const html = markdown.parse(custom_markdown_convert(md));
const container = document.createElement("div");
container.innerHTML = html;

const headings = container.querySelectorAll("h1, h2");
headings.forEach((heading, i) => {
const text = heading.textContent ?? `heading-${i}`;
const id = text.replace(/\s+/g, "-").toLowerCase();
heading.id = id;
});
setTable(
Array.from(headings).map((d, i) => {
const id = (d.textContent ?? `heading-${i}`).replace(/\s+/g, "-").toLowerCase();
return (
<a
key={i}
className={styles[`${d.tagName.toLowerCase()}-table`]}
href={"#" + id}
onClick={handleAnchorClick(id)}
>
<li>{d.textContent}</li>
</a>
)
}
)
);

container.querySelectorAll("pre code").forEach((code) => {
hljs.highlightElement(code as HTMLElement);
});

setMarkdownSource(container.innerHTML);
})
.catch((err) => console.error("読み込みエラー:", err));
};
}, [id]);

return (
<div className={styles.container}>
<Header />
{
!isMobile &&
<div className={styles.side}>
<ul>
{
table
}
</ul>
</div>
}
<div className={styles["article-container"]}>
<img src={thumbnail ? thumbnail : "/assets/images/headers/blog.webp"} className={styles["header-img"]} />
<div
className={[styles.article, isMobile && styles.mobile].join(" ")}
dangerouslySetInnerHTML={
{
__html: markdownSource
}
}
/>
</div>
</div>
)
}
46 changes: 46 additions & 0 deletions app/routes/blog/custom-highlight.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
pre {
display: block;
border-radius: 8px;
overflow-x: hidden;
font-size: 20px;
width: 100%;

background-color: #0f1a2b;
box-shadow: 0 12px 30px #3a3a3a3f;
}

code.hljs {
width: calc(100% - 24px);
font-size: 16px;
font-family: Menlo, monospace;
line-height: 1.8em;
padding: 0 24px;

background-color: transparent;

span.hljs-addition {
line-height: 0 !important;
padding: 1em 0;
display: inline-block;
// width: 100%;
}
}

.filename-label {
background-color: #2a554c;
width: max-content;
padding: 4px 12px 4px 12px;
color: var(--accent-color);
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
letter-spacing: 2px;

border-top-left-radius: 4px;
border-top-right-radius: 4px;

& + pre {
border-top-left-radius: 0;
}
}
7 changes: 0 additions & 7 deletions app/routes/blog/index.module.css

This file was deleted.

Loading