generated from nhcarrigan/template
Compare commits
7 Commits
9d81ce456b
...
main
Author | SHA1 | Date | |
---|---|---|---|
c7e7651617
|
|||
fc34847fbe
|
|||
2a30268701 | |||
7ae8526071
|
|||
fb43866a0a
|
|||
3a6a8fff18
|
|||
fd4ce85d33
|
69
.gitea/issue_template/bug_report.yaml
Normal file
69
.gitea/issue_template/bug_report.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
name: 🐛 Bug Report
|
||||
description: Something isn't working as expected? Let us know!
|
||||
title: '[BUG] - '
|
||||
labels:
|
||||
- "status/awaiting triage"
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: attestations
|
||||
attributes:
|
||||
label: Attestations
|
||||
description: "By checking the boxes below, I certify that:"
|
||||
options:
|
||||
- label: "I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)"
|
||||
validations:
|
||||
required: true
|
||||
- label: I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
|
||||
validations:
|
||||
required: true
|
||||
- label: I have confirmed that the issue I am opening is unique, and has not already been reported (whether closed or not).
|
||||
validations:
|
||||
required: true
|
||||
- label: I have reviewed the [Security Policy](https://docs.nhcarrigan.com/legal/security/) and have determined that this is not a security vulnerability.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: "Describe your Issue:"
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: Can you reproduce this issue?
|
||||
options:
|
||||
- Yes
|
||||
- No
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: "Steps to Reproduce:"
|
||||
description: Steps to reproduce the behavior.
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: "Operating System:"
|
||||
description: The operating system you are using, including the version/build number.
|
||||
validations:
|
||||
required: true
|
||||
# Remove this section for non-web apps.
|
||||
- type: input
|
||||
id: browser
|
||||
attributes:
|
||||
label: "Browser:"
|
||||
description: The browser you are using, including the version number.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Are you willing and able to contribute a fix?
|
||||
options:
|
||||
- Yes
|
||||
- No
|
||||
validations:
|
||||
required: true
|
||||
|
5
.gitea/issue_template/config.yml
Normal file
5
.gitea/issue_template/config.yml
Normal file
@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: "Discord"
|
||||
url: "https://chat.nhcarrigan.com"
|
||||
about: "Chat with us directly."
|
46
.gitea/issue_template/feature_proposal.yml
Normal file
46
.gitea/issue_template/feature_proposal.yml
Normal file
@ -0,0 +1,46 @@
|
||||
name: 💭 Feature Proposal
|
||||
description: Have an idea for how we can improve? Share it here!
|
||||
title: '[FEAT] - '
|
||||
labels:
|
||||
- "status/awaiting triage"
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: attestations
|
||||
attributes:
|
||||
label: Attestations
|
||||
description: "By checking the boxes below, I certify that:"
|
||||
options:
|
||||
- label: "I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)"
|
||||
validations:
|
||||
required: true
|
||||
- label: I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
|
||||
validations:
|
||||
required: true
|
||||
- label: I have confirmed that the issue I am opening is unique, and has not already been reported (whether closed or not).
|
||||
validations:
|
||||
required: true
|
||||
- label: I have reviewed the [Security Policy](https://docs.nhcarrigan.com/legal/security/) and have determined that this is not a security vulnerability.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: "Describe your Idea:"
|
||||
description: A clear and concise description of the feature you would like added.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: "What problem does this feature solve?"
|
||||
description: Why are you requesting this feature? How would it improve your experience with the product?
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Are you willing and able to contribute this feature?
|
||||
options:
|
||||
- Yes
|
||||
- No
|
||||
validations:
|
||||
required: true
|
34
.gitea/issue_template/other.yml
Normal file
34
.gitea/issue_template/other.yml
Normal file
@ -0,0 +1,34 @@
|
||||
name: ❓ Other Issue
|
||||
description: I have something that is neither a bug nor a feature request.
|
||||
title: '[OTHER] - '
|
||||
labels:
|
||||
- "status/awaiting triage"
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: attestations
|
||||
attributes:
|
||||
label: Attestations
|
||||
description: "By checking the boxes below, I certify that:"
|
||||
options:
|
||||
- label: "I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)"
|
||||
validations:
|
||||
required: true
|
||||
- label: I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
|
||||
validations:
|
||||
required: true
|
||||
- label: I have confirmed that the issue I am opening is unique, and has not already been reported (whether closed or not).
|
||||
validations:
|
||||
required: true
|
||||
- label: I have reviewed the [Security Policy](https://docs.nhcarrigan.com/legal/security/) and have determined that this is not a security vulnerability.
|
||||
validations:
|
||||
required: true
|
||||
- label: This is not a feature request or bug report that I am mis-filing to avoid the issue template.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: "Share your thoughts:"
|
||||
description: Why are you opening this issue?
|
||||
validations:
|
||||
required: true
|
91
.gitea/pull_request_template.yml
Normal file
91
.gitea/pull_request_template.yml
Normal file
@ -0,0 +1,91 @@
|
||||
name: "Pull Request Template"
|
||||
about: "Template for pulls"
|
||||
body:
|
||||
- type: textarea
|
||||
id: explain
|
||||
attributes:
|
||||
label: "Explanation"
|
||||
description: "Briefly explain WHY this pull request is necessary. Do not explain what it does, as that's evidenced in the changes."
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: issue
|
||||
attributes:
|
||||
label: "Issue"
|
||||
description: "My pull request relates to or resolves the following issue number:"
|
||||
validations:
|
||||
required: true
|
||||
is_number: true
|
||||
- type: checkboxes
|
||||
id: attestations
|
||||
attributes:
|
||||
label: Attestations
|
||||
description: "By checking the boxes below, I certify that:"
|
||||
options:
|
||||
- label: "I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)"
|
||||
validations:
|
||||
required: true
|
||||
- label: I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
|
||||
validations:
|
||||
required: true
|
||||
- label: My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: dependencies
|
||||
attributes:
|
||||
label: Dependencies
|
||||
description: "My pull request adds or updates dependencies, so:"
|
||||
options:
|
||||
- label: I have pinned the dependencies to a specific patch version.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: style
|
||||
attributes:
|
||||
label: Style
|
||||
description: "My contribution adheres to the following style guidelines:"
|
||||
options:
|
||||
- label: I have run the linter and resolved any errors.
|
||||
validations:
|
||||
required: true
|
||||
- label: My pull request uses an appropriate title, matching the conventional commit standards.
|
||||
validations:
|
||||
required: true
|
||||
- label: My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: tests
|
||||
attributes:
|
||||
label: Tests
|
||||
description: "My contribution includes the following tests:"
|
||||
options:
|
||||
- label: My contribution adds new code, and I have added tests to cover it.
|
||||
validations:
|
||||
required: false
|
||||
- label: My contribution modifies existing code, and I have updated the tests to reflect these changes.
|
||||
validations:
|
||||
required: false
|
||||
- label: All new and existing tests pass locally with my changes.
|
||||
validations:
|
||||
required: true
|
||||
- label: Code coverage remains at or above the configured threshold.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: docs
|
||||
attributes:
|
||||
label: Documentation
|
||||
description: "I have made the following PR to update the documentation site with my changes:"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Versioning
|
||||
description: "I believe my changes should be included in the following release:"
|
||||
options:
|
||||
- "Major - My pull request introduces a breaking change."
|
||||
- "Minor - My pull request introduces a new non-breaking feature."
|
||||
- "Patch - My pull request introduces bug fixes ONLY."
|
38
.gitea/workflows/ci.yml
Normal file
38
.gitea/workflows/ci.yml
Normal file
@ -0,0 +1,38 @@
|
||||
name: Node.js CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint and Test
|
||||
|
||||
steps:
|
||||
- name: Checkout Source Files
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js v22
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Lint Source Files
|
||||
run: pnpm run lint
|
||||
|
||||
- name: Verify Build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Run Tests
|
||||
run: pnpm run test
|
@ -3,10 +3,11 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"dev": "next dev -p 3002",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint src --max-warnings 0"
|
||||
"start": "next start -p 3002",
|
||||
"lint": "eslint src --max-warnings 0",
|
||||
"test": "echo \"No tests yet\" && exit 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"gray-matter": "4.0.3",
|
||||
|
17
posts/irc-web-chat.md
Normal file
17
posts/irc-web-chat.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
title: "Web Client for IRC"
|
||||
date: "2025-01-28"
|
||||
summary: "Making it easier to connect to our IRC network"
|
||||
---
|
||||
|
||||
We understand that IRC can be an intimidating platform for non-technical users, as well as for users who have not set up an IRC client in the past.
|
||||
|
||||
To help alleviate this, we are happy to announce that we now offer a simple web-based client~! Visit our IRC network address at https://irc.nhcarrigan.com in your favourite web browser and you'll be able to connect to our IRC channels.
|
||||
|
||||
## Matrix Federation
|
||||
|
||||
We've also enabled federation for our Matrix server! I have to admit, we aren't entirely sure how this bit works, but you *should* be able to join `#home:matrix.nhcarrigan.com` from any Matrix account (instead of having to sign up through our homeserver)?
|
||||
|
||||
## Custom Emotes
|
||||
|
||||
And just for a little bit of fun, we've added custom emotes to all of our platforms. Enjoy them, and feel free to let us know if there are more emotes you'd like!
|
@ -15,9 +15,10 @@ Well, there's a few things:
|
||||
- First and foremost, the bulk of our communication is being moved to our [forum](https://chat.nhcarrigan.com). If you need to get in touch with us for *anything*, this is probably the best place to start. Additionally, conversations here are searchable and indexed by many crawlers.
|
||||
- Our chat is migrating over to [IRC](https://irc.nhcarrigan.com). To protect the privacy of our community members, we do *not* run a bouncer. Which means anything you send can only be seen by users who are *currently online*. This ephemeral nature makes it nice for informal chatter, but leaves the forum as the best place for long-term conversation.
|
||||
- For personal updates, we maintain our own [blog](https://blog.nhcarrigan.com). We also have a self-hosted [Sharkey instance](https://fedi.nhcarrigan.com) for smaller posts. Sign-ups are restricted to our family and polycule, but you can connect with us via any Mastodon-compliant instance!
|
||||
- And finally, all of our code repositories are now on our own [Gitea instance](https://git.nhcarrigan.com). Note that anyone is welcome to sign up to file issues or comment on discussions, but repository creation is disabled so interested contributors will need to reach out for access.
|
||||
|
||||
## What if I don't want to join any of those?
|
||||
|
||||
That's fine! We'll still maintain a minimal presence on platforms like Discord and Bluesky. But our core focus for building our community is going to be on our self-hosted solutions, where we know our marginalised members are safe and can be protected.
|
||||
|
||||
We do hope to see you in our new home on the forum! 💜
|
||||
We do hope to see you in our new home on the forum! 💜
|
||||
|
36
posts/new-chat-bots.md
Normal file
36
posts/new-chat-bots.md
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
title: "New Chat Bots"
|
||||
date: "2025-02-10"
|
||||
summary: "Tools to improve your experience on Discord"
|
||||
---
|
||||
|
||||
We've released three new chat bots that are available to use right now!
|
||||
|
||||
First we have Aria Iuvo, a translation bot powered by our own self-hosted models! Translating messages couldn't be easier: All you need to do is right click on a message, select "Apps", then "Translate Message", and she'll translate the content of the message to whatever language you use for your Discord client!
|
||||
|
||||
Add Aria here: https://discord.com/oauth2/authorize?client_id=1338596130207957035
|
||||
|
||||
Second we have Cordelia Taryne. She's an AI-powered multi-faceted assistant. She's capable of quite a few things:
|
||||
|
||||
- Responding to general queries
|
||||
- Generating alt-text for images
|
||||
- Summarising text
|
||||
- Proofreading text
|
||||
- Sentiment/mood analysis
|
||||
- Code evaluation
|
||||
|
||||
Add Cordelia here: https://discord.com/oauth2/authorize?client_id=1338664192714211459
|
||||
|
||||
And finally we have Melody Iuvo, a powerful task management bot. You can create tasks, track due dates, categorise and prioritise them, and more!
|
||||
|
||||
Add Melody here: https://discord.com/oauth2/authorize?client_id=1338753576583041074
|
||||
|
||||
Please note that all of these are paid services, to help us fund our endeavours to self-host safe spaces online.
|
||||
|
||||
---
|
||||
|
||||
We'd also like to take a moment to remind you that our support channels are all 100% self-hosted. We offer these services so you can not only have options for getting in touch with us, but have platforms where we own your data and you don't need to worry about platforms that protect people whose actions are not in line with our Code of Conduct.
|
||||
|
||||
You can view the list at https://chat.nhcarrigan.com.
|
||||
|
||||
And our Matrix server now receives all of our development logs and errors!
|
@ -4,23 +4,30 @@
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import type { JSX } from "react";
|
||||
import { Rule } from "@/components/rule";
|
||||
import { getSortedPostsData } from "@/lib/posts";
|
||||
|
||||
export default function Home() {
|
||||
/**
|
||||
* Renders the home page.
|
||||
* @returns JSX component.
|
||||
*/
|
||||
const Home = (): JSX.Element => {
|
||||
const posts = getSortedPostsData();
|
||||
return (
|
||||
<main>
|
||||
<h1>{"Announcements"}</h1>
|
||||
<p>{"This page documents all of our organisation's announcements, in reverse chronological order."}</p>
|
||||
{posts.map((post) => {
|
||||
return <div key={post.slug}>
|
||||
<Rule />
|
||||
<h2><a className="underline" href={`/post/${post.slug}`}>{post.data.title}</a></h2>
|
||||
<p className="italic text-center">{post.data.date.toLocaleDateString("en-GB",{ year: "numeric", month: "long", day: "numeric"})}</p>
|
||||
<p>{post.data.summary}</p>
|
||||
</div>;
|
||||
})}
|
||||
{posts.map((post) => {
|
||||
return <div key={post.slug}>
|
||||
<Rule />
|
||||
<h2><a className="underline" href={`/post/${post.slug}`}>{post.data.title}</a></h2>
|
||||
<p className="italic text-center">{post.data.date.toLocaleDateString("en-GB", { day: "numeric", month: "long", year: "numeric" })}</p>
|
||||
<p>{post.data.summary}</p>
|
||||
</div>;
|
||||
})}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Home;
|
||||
|
@ -1,23 +1,38 @@
|
||||
import { Rule } from "@/components/rule";
|
||||
import { getPostData } from "@/lib/posts";
|
||||
/**
|
||||
* @copyright nhcarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import type { JSX } from "react";
|
||||
import { Rule } from "@/components/rule";
|
||||
import { getPostData } from "@/lib/posts";
|
||||
|
||||
export default async function Page({
|
||||
params
|
||||
/**
|
||||
* Renders a blog post.
|
||||
* @param props - The properties of the page.
|
||||
* @param props.params - The path parameters.
|
||||
* @returns The JSX component.
|
||||
*/
|
||||
const Page = async({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{slug: string}>
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
const post = getPostData(slug);
|
||||
return (
|
||||
<main>
|
||||
<h1>{post.data.title}</h1>
|
||||
<p className="italic text-center">{`Published ${post.data.date.toLocaleDateString("en-GB", { weekday: "long", year: "numeric", month: "long", day: "numeric"})}`}</p>
|
||||
<Rule />
|
||||
<Markdown remarkPlugins={[remarkGfm]}>{post.content}</Markdown>
|
||||
<Rule />
|
||||
<a href="/">{"← Back to home"}</a>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
readonly params: Promise<{ slug: string }>;
|
||||
}): Promise<JSX.Element> => {
|
||||
const { slug } = await params;
|
||||
const post = getPostData(slug);
|
||||
return (
|
||||
<main>
|
||||
<h1>{post.data.title}</h1>
|
||||
<p className="italic text-center">{`Published ${post.data.date.toLocaleDateString("en-GB", { day: "numeric", month: "long", weekday: "long", year: "numeric" })}`}</p>
|
||||
<Rule />
|
||||
<Markdown remarkPlugins={[ remarkGfm ]}>{post.content}</Markdown>
|
||||
<Rule />
|
||||
<a href="/">{"← Back to home"}</a>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
@ -10,49 +10,71 @@ import matter from "gray-matter";
|
||||
|
||||
const postsDirectory = join(process.cwd(), "posts");
|
||||
|
||||
export const getSortedPostsData = () => {
|
||||
/**
|
||||
* Reads the posts directory and returns an array of post data.
|
||||
* @returns An array of parsed frontmatter and file contents.
|
||||
*/
|
||||
const getSortedPostsData = (): Array<{
|
||||
content: string;
|
||||
data: { date: Date; summary: string; title: string };
|
||||
slug: string;
|
||||
}> => {
|
||||
const fileNames = readdirSync(postsDirectory);
|
||||
const allPostsData = fileNames.map((fileName) => {
|
||||
const slug = fileName.replace(/\.md$/, "");
|
||||
const fullPath = join(postsDirectory, fileName);
|
||||
const fileContents = readFileSync(fullPath, "utf8");
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- matter doesn't take a generic I guess?
|
||||
const matterResult = matter(fileContents) as unknown as {
|
||||
content: string;
|
||||
data: { title: string; date: string; summary: string };
|
||||
data: { title: string; date: string; summary: string };
|
||||
};
|
||||
return {
|
||||
slug: slug,
|
||||
data: {
|
||||
title: matterResult.data.title,
|
||||
date: new Date(matterResult.data.date),
|
||||
summary: matterResult.data.summary,
|
||||
},
|
||||
content: matterResult.content,
|
||||
data: {
|
||||
date: new Date(matterResult.data.date),
|
||||
summary: matterResult.data.summary,
|
||||
title: matterResult.data.title,
|
||||
},
|
||||
slug: slug,
|
||||
};
|
||||
});
|
||||
return allPostsData.sort((a, b) => {
|
||||
if (a.data.date < b.data.date) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
};
|
||||
|
||||
export const getPostData = (slug: string) => {
|
||||
const fullPath = join(postsDirectory, slug + ".md");
|
||||
const fileContents = readFileSync(fullPath, "utf8");
|
||||
const matterResult = matter(fileContents) as unknown as {
|
||||
content: string;
|
||||
data: { title: string; date: string; summary: string };
|
||||
};
|
||||
return {
|
||||
slug: slug,
|
||||
data: {
|
||||
title: matterResult.data.title,
|
||||
date: new Date(matterResult.data.date),
|
||||
summary: matterResult.data.summary,
|
||||
},
|
||||
content: matterResult.content,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Reads a single post file.
|
||||
* @param slug - The slug of the post to read.
|
||||
* @returns The parsed frontmatter and file contents.
|
||||
*/
|
||||
const getPostData = (
|
||||
slug: string,
|
||||
): {
|
||||
content: string;
|
||||
data: { date: Date; summary: string; title: string };
|
||||
slug: string;
|
||||
} => {
|
||||
const fullPath = join(postsDirectory, `${slug}.md`);
|
||||
const fileContents = readFileSync(fullPath, "utf8");
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- matter doesn't take a generic I guess?
|
||||
const matterResult = matter(fileContents) as unknown as {
|
||||
content: string;
|
||||
data: { title: string; date: string; summary: string };
|
||||
};
|
||||
return {
|
||||
content: matterResult.content,
|
||||
data: {
|
||||
date: new Date(matterResult.data.date),
|
||||
summary: matterResult.data.summary,
|
||||
title: matterResult.data.title,
|
||||
},
|
||||
slug: slug,
|
||||
};
|
||||
};
|
||||
|
||||
export { getSortedPostsData, getPostData };
|
||||
|
Reference in New Issue
Block a user