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
13 changes: 12 additions & 1 deletion astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import tailwindcss from "@tailwindcss/vite";
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
import mdx from "@astrojs/mdx";
import rehypeExternalLinks from "rehype-external-links";
import sitemap from "@astrojs/sitemap";
import icon from "astro-icon";
import path from "node:path";
Expand Down Expand Up @@ -41,7 +42,17 @@ export default defineConfig({
},

integrations: [
mdx(),
// MDX is only used by the /docs guides: links to GitHub and other sites open in a new tab
// (announced to screen readers), so the guide stays open beside the file being edited.
mdx({
rehypePlugins: [
[rehypeExternalLinks, {
target: '_blank',
rel: ['noopener'],
content: { type: 'element', tagName: 'span', properties: { className: ['sr-only'] }, children: [{ type: 'text', value: ' (opens in a new tab)' }] },
}],
],
}),
preact(),
icon(),
sitemap({
Expand Down
31 changes: 31 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"daisyui": "^5.5.14",
"katex": "^0.16.47",
"preact": "^10.28.1",
"rehype-external-links": "^3.0.0",
"rehype-katex": "^7.0.1",
"remark-math": "^6.0.0",
"tailwindcss": "^4.1.18"
Expand Down
67 changes: 67 additions & 0 deletions src/components/ProfileFormLink.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
// "Use the profile form" box for the documentation guides (forms.profile in src/data/site.ts).
import Button from "./ui/Button.astro";
import Icon from "./ui/Icon.astro";
import Todo from "./ui/Todo.astro";
import { forms, site } from "../data/site";
---

<div class="form-link surface-tonal not-prose">
<span class="form-link__icon"><Icon name="material-symbols:assignment-outline" /></span>
<div class="flex-1">
<p class="form-link__title">No GitHub account needed</p>
<p class="mt-1 text-[0.9375rem]">
{
forms.profile ? (
"Add or update your profile, photo and projects with one form."
) : (
<>
Ask the MARC website maintainers for the profile form link at{" "}
<a href={`mailto:${site.email}?subject=${encodeURIComponent("Profile form link")}`} class="text-primary underline">
{site.email}
</a>
.
</>
)
}
</p>
</div>
{
forms.profile && (
<Button href={forms.profile} external icon="material-symbols:arrow-outward">
Open the profile form
</Button>
)
}
</div>
{!forms.profile && <Todo>Set forms.profile in src/data/site.ts to the form's share link (marc_people/google-form/README.md, step 7).</Todo>}

<style>
.form-link {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 1rem 1.25rem;
margin: 1.5rem 0;
padding: 1.25rem 1.5rem;
}
.form-link__icon {
display: grid;
place-items: center;
flex: none;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--color-base-100);
color: var(--color-primary);
}
.form-link__icon :global(svg) {
width: 1.375rem;
height: 1.375rem;
}
.form-link__title {
font-family: var(--font-head);
font-weight: 600;
color: var(--color-heading);
}
</style>
8 changes: 7 additions & 1 deletion src/data/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,14 @@ export const contentRepos = {
website: "https://github.com/pdnMARC/pdnmarc.github.io",
};

// Google Forms used by the documentation. Set `profile` to the form's share link once it exists
// (marc_people/google-form/README.md, step 7); until then the guides ask people to contact MARC.
export const forms = {
profile: "https://docs.google.com/forms/d/e/1FAIpQLSeXOCqNmVEKqgairmF4IRnbU388iqUDekWeDIOYbHRi7tNWUQ/viewform" as string | null,
};

export const docs = [
{ href: "/docs/profiles/", label: "People profiles", icon: "material-symbols:person-outline", text: "Add yourself or update your photo, role and biography." },
{ href: "/docs/profiles/", label: "People profiles", icon: "material-symbols:person-outline", text: "Use the profile form, or edit on GitHub: photo, role, biography and links." },
{ href: "/docs/projects/", label: "Projects", icon: "material-symbols:science-outline", text: "Add a project, its team, cover image and description." },
{ href: "/docs/news/", label: "News & vacancies", icon: "material-symbols:newspaper", text: "Post a news article, a vacancy with a deadline, or a social feed item." },
{ href: "/docs/publications/", label: "Publications", icon: "material-symbols:menu-book-outline", text: "Add papers to the publications sheet and link them to projects and people." },
Expand Down
60 changes: 60 additions & 0 deletions src/layouts/DocsLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ const current = Astro.url.pathname.replace(/\/?$/, "/");
</div>
</BaseLayout>

<script>
import "../scripts/copy-code";
</script>

<style>
.docs {
display: grid;
Expand Down Expand Up @@ -113,4 +117,60 @@ const current = Astro.url.pathname.replace(/\/?$/, "/");
display: block;
overflow-x: auto;
}

/* Links that leave the site (GitHub etc.) open in a new tab: mark them with ↗ (astro.config.mjs). */
.docs__body :global(a[target="_blank"])::after {
content: "";
display: inline-block;
width: 0.75em;
height: 0.75em;
margin-left: 0.15em;
vertical-align: 0.05em;
background: currentColor;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6.4 18L5 16.6L14.6 7H6V5h12v12h-2V8.4z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* Copy button on code blocks (src/scripts/copy-code.ts). Code blocks use a dark theme in both modes. */
.docs__body :global(.code-copy) {
position: relative;
}
.docs__body :global(.code-copy pre) {
padding-right: 6.5rem;
}
.docs__body :global(.code-copy__btn) {
position: absolute;
top: 0.5rem;
right: 0.5rem;
display: inline-flex;
align-items: center;
gap: 0.375rem;
min-height: 32px;
padding: 0 0.75rem;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.25);
background: rgba(255, 255, 255, 0.08);
color: #e6edf3;
font-family: var(--font-sans);
font-size: 0.8125rem;
font-weight: 500;
cursor: pointer;
transition:
background-color var(--t-fast),
border-color var(--t-fast);
}
.docs__body :global(.code-copy__btn:hover) {
background: rgba(255, 255, 255, 0.16);
}
.docs__body :global(.code-copy__btn:focus-visible) {
outline: 2px solid #fff;
outline-offset: 2px;
}
.docs__body :global(.code-copy__btn.is-done) {
border-color: #7ee2a8;
color: #7ee2a8;
}
.docs__body :global(.code-copy__btn svg) {
width: 1rem;
height: 1rem;
}
</style>
10 changes: 5 additions & 5 deletions src/pages/docs/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Icon from "../../components/ui/Icon.astro";
import { docs, contentRepos, site } from "../../data/site";

const where = [
{ what: "People profiles", place: "marc_people repository, profiles/ folder", href: contentRepos.people, guide: "/docs/profiles/" },
{ what: "People profiles", place: "Profile form, or marc_people repository, profiles/ folder", href: contentRepos.people, guide: "/docs/profiles/" },
{ what: "Projects", place: "marc_projects repository, projects/ folder", href: contentRepos.projects, guide: "/docs/projects/" },
{ what: "News articles and vacancies", place: "marc_news repository, articles/ folder", href: contentRepos.news, guide: "/docs/news/" },
{ what: "Social feed items", place: "Website data Google Sheet, News tab", guide: "/docs/news/#social-feed-items" },
Expand All @@ -18,8 +18,8 @@ const tasks = [
{
title: "A new member joins MARC and a project",
steps: [
{ where: "marc_people", href: "/docs/profiles/#add-a-new-profile", text: "add their profile, with their email and name, plus any other addresses (otherEmails) and the forms of their name used in their papers (otherNames)." },
{ where: "marc_projects", href: "/docs/profiles/#add-yourself-to-your-projects", text: "add the same email to each project they work on." },
{ where: "Profile form", href: "/docs/profiles/#use-the-profile-form", text: "they fill it in once: profile, photo, other emails and names, and the projects they work on." },
{ where: "Or on GitHub", href: "/docs/profiles/#add-a-new-profile", text: "add the profile in marc_people and their email to each project file in marc_projects." },
{ where: "Their papers", href: "/docs/publications/#linking-papers-to-people", text: "check each paper appears on their profile; if not, add the author-list form of their name to otherNames." },
],
},
Expand All @@ -32,13 +32,13 @@ const tasks = [
{
title: "Someone's email address changes",
steps: [
{ where: "marc_people", href: "/docs/profiles/#changing-your-email-or-name", text: "put the new address in email and move the old one to otherEmails. Project files stay as they are." },
{ where: "Profile form", href: "/docs/profiles/#use-the-profile-form", text: "enter the new address under Other email addresses. Project files stay as they are." },
],
},
{
title: "Someone leaves MARC",
steps: [
{ where: "marc_people", href: "/docs/profiles/#when-you-leave-marc", text: "set status: past and, if known, currentPosition. Don't delete the profile." },
{ where: "Profile form", href: "/docs/profiles/#use-the-profile-form", text: "choose Past member and say where they are now (or set status: past on GitHub). Don't delete the profile." },
{ where: "marc_projects", href: "/docs/profiles/#when-you-leave-marc", text: "nothing to change: they stay on their projects, marked as a past member." },
],
},
Expand Down
30 changes: 27 additions & 3 deletions src/pages/docs/profiles.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,34 @@ description: Add yourself to the People page, or update your photo, role and bio
---

import Callout from "../../components/ui/Callout.astro";
import ProfileFormLink from "../../components/ProfileFormLink.astro";

Each person at MARC keeps their own profile up to date. Profiles live in the
[marc_people repository](https://github.com/pdnMARC/marc_people) on GitHub: one Markdown file per person
in the `profiles/` folder, with photos in `profiles/photos/`.
Each person at MARC keeps their own profile up to date, either with the **profile form** (no GitHub
account needed) or by editing it on GitHub.

## Use the profile form

<ProfileFormLink />

The form adds a new profile or updates yours, and keeps every link intact:

- **Updating?** Fill in only what should change. Blank answers keep what is already there.
- **Other email addresses and other forms of your name** are added to your profile, never removed, so
projects and papers that use them stay linked to you.
- **Photo:** upload a JPG, PNG or WebP image. It is resized and its camera and location data removed.
- **Projects:** tick the projects you work on and you are added to their teams.
- **Leaving MARC?** Choose *Past member* and say where you are now.

Every night at midnight (Sri Lanka time) your answers become a change request. It goes live once a MARC
maintainer approves it. If something can't be used, for example an update from an address that is not in
any profile, a maintainer will be in touch.

The rest of this guide is for editing on GitHub, and explains what each field means.

## Edit on GitHub

Profiles live in the [marc_people repository](https://github.com/pdnMARC/marc_people) on GitHub: one
Markdown file per person in the `profiles/` folder, with photos in `profiles/photos/`.

```text
marc_people/
Expand Down
18 changes: 18 additions & 0 deletions src/pages/docs/site.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ A few values here point at content in the other repositories and must match it e

</Callout>

## Profile form

MARC members can add or update their profile with a Google Form instead of GitHub. Every night at 00:00
Sri Lanka time, the **Profile Form Sync** workflow in marc_people and in marc_projects reads the form's
response sheet and opens pull requests:

- in **marc_people**, one per response, with the new or updated profile and its photo;
- in **marc_projects**, adding the person to the projects they ticked.

Responses that can't be used become an issue instead. Each pull request lists what changed, who sent it
and the validator result, with a short review checklist. **Merge** to publish or **close** to reject;
a response is never proposed twice. To process new responses straight away, run the workflow from the
repository's **Actions** tab.

Setup (the form, sharing, the `GOOGLE_FORM_CSV_LINK` secret) and maintenance are described in
[marc_people/google-form/README.md](https://github.com/pdnMARC/marc_people/blob/main/google-form/README.md).
Once the form exists, put its share link in `forms.profile` in `src/data/site.ts` so the guides show it.

## Where things are

| What | Where |
Expand Down
55 changes: 55 additions & 0 deletions src/scripts/copy-code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// "Copy" buttons for the code blocks in the documentation guides (DocsLayout). Folder trees
// (```text) are left alone. Added by script, so without JavaScript the blocks are simply shown.
const blocks = document.querySelectorAll<HTMLPreElement>(".docs__body pre[data-language]:not([data-language='text'])");

async function copyText(text: string): Promise<boolean> {
try {
await navigator.clipboard.writeText(text);
return true;
} catch {
// Older browsers / insecure contexts: copy through a temporary selection.
const area = document.createElement("textarea");
area.value = text;
area.setAttribute("readonly", "");
area.style.position = "fixed";
area.style.opacity = "0";
document.body.append(area);
area.select();
const ok = document.execCommand("copy");
area.remove();
return ok;
}
}

blocks.forEach((pre) => {
const wrap = document.createElement("div");
wrap.className = "code-copy";
pre.before(wrap);
wrap.append(pre);

const button = document.createElement("button");
button.type = "button";
button.className = "code-copy__btn";
button.setAttribute("aria-label", "Copy code to clipboard");
button.innerHTML =
'<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M9 18q-.825 0-1.412-.587T7 16V4q0-.825.588-1.412T9 2h9q.825 0 1.413.588T20 4v12q0 .825-.587 1.413T18 18zm0-2h9V4H9zm-4 6q-.825 0-1.412-.587T3 20V6h2v14h11v2zm4-6V4z"/></svg><span>Copy</span>';
const label = button.querySelector("span")!;
const status = document.createElement("span");
status.className = "sr-only";
status.setAttribute("role", "status");

let timer: number | undefined;
button.addEventListener("click", async () => {
const ok = await copyText(pre.querySelector("code")?.textContent ?? pre.textContent ?? "");
label.textContent = ok ? "Copied" : "Copy failed";
status.textContent = ok ? "Code copied to clipboard" : "Could not copy the code";
button.classList.toggle("is-done", ok);
window.clearTimeout(timer);
timer = window.setTimeout(() => {
label.textContent = "Copy";
status.textContent = "";
button.classList.remove("is-done");
}, 2000);
});
wrap.append(button, status);
});
Loading