Compare commits

...

7 Commits

Author SHA1 Message Date
c7e7651617 feat: announce new chat bots
All checks were successful
Node.js CI / Lint and Test (push) Successful in 1m24s
2025-02-10 23:57:45 -08:00
fc34847fbe feat: web chat for irc
All checks were successful
Node.js CI / Lint and Test (push) Successful in 1m40s
2025-01-28 16:59:14 -08:00
2a30268701 feat: setup actions (#1)
All checks were successful
Node.js CI / Lint and Test (push) Successful in 1m13s
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-01-23 02:04:03 -08:00
7ae8526071 chore: mention the code repos 2025-01-22 23:05:35 -08:00
fb43866a0a chore: ports 2025-01-22 22:46:58 -08:00
3a6a8fff18 fix: ports 2025-01-22 22:41:46 -08:00
fd4ce85d33 chore: gitea 2025-01-22 22:40:38 -08:00
13 changed files with 442 additions and 60 deletions

View 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

View File

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: "Discord"
url: "https://chat.nhcarrigan.com"
about: "Chat with us directly."

View 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

View 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

View 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
View 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

View File

@ -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
View 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!

View File

@ -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
View 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!

View File

@ -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;

View File

@ -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;

View File

@ -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 };