Update frontend UI/UX
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* How a bot names a crew member in a message: `[bot:3]`. The chat shows it
|
||||
* as a link to that bot's conversation.
|
||||
*/
|
||||
export default function botRef({ id }: { id: number | string }) {
|
||||
return `[bot:${id}]`;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import isSecureRequest from "./is-secure-request";
|
||||
|
||||
describe("isSecureRequest", () => {
|
||||
test("plain HTTP on a VPN address is not secure", () => {
|
||||
const req = new Request("http://10.8.0.1:7247/api/auth/password-login");
|
||||
expect(isSecureRequest({ req })).toBe(false);
|
||||
});
|
||||
|
||||
test("an HTTPS request is secure", () => {
|
||||
const req = new Request("https://cobble.example.com/");
|
||||
expect(isSecureRequest({ req })).toBe(true);
|
||||
});
|
||||
|
||||
test("trusts the proxy's forwarded scheme", () => {
|
||||
const req = new Request("http://localhost:7247/", {
|
||||
headers: { "X-Forwarded-Proto": "https" },
|
||||
});
|
||||
expect(isSecureRequest({ req })).toBe(true);
|
||||
});
|
||||
|
||||
test("reads the first of several forwarded schemes", () => {
|
||||
const req = new Request("https://cobble.example.com/", {
|
||||
headers: { "X-Forwarded-Proto": "http, https" },
|
||||
});
|
||||
expect(isSecureRequest({ req })).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
type Params = {
|
||||
req: Request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the browser reached the app over HTTPS: the request's own scheme,
|
||||
* or the first `X-Forwarded-Proto` a proxy (Cloudflare, Caddy) set in front.
|
||||
* Plain HTTP (a WireGuard address, a LAN IP) gets cookies without `Secure`,
|
||||
* which the browser would otherwise drop.
|
||||
*/
|
||||
export default function isSecureRequest({ req }: Params): boolean {
|
||||
const forwarded = req.headers
|
||||
.get("x-forwarded-proto")
|
||||
?.split(",")[0]
|
||||
?.trim()
|
||||
.toLowerCase();
|
||||
|
||||
if (forwarded) return forwarded === "https";
|
||||
|
||||
return new URL(req.url).protocol === "https:";
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import botRef from "./bot-ref";
|
||||
import computerRef from "./computer-ref";
|
||||
import linkComputerRefs from "./link-computer-refs";
|
||||
import takeOverRef from "./take-over-ref";
|
||||
@@ -25,4 +26,12 @@ describe("linkComputerRefs", () => {
|
||||
}),
|
||||
).toBe("Please log in to Gmail. [take over](#take-over-3-browser)");
|
||||
});
|
||||
|
||||
test("links every bot a message names", () => {
|
||||
expect(
|
||||
linkComputerRefs({
|
||||
text: `${botRef({ id: 4 })} is on it. See [bot:x] and [bot:5](https://example.com).`,
|
||||
}),
|
||||
).toBe("[bot 4](#bot-4) is on it. See [bot:x] and [bot:5](https://example.com).");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,9 +12,15 @@ export const ComputerRefHrefPrefix = "#computer-";
|
||||
*/
|
||||
export const TakeOverRefHrefPrefix = "#take-over-";
|
||||
|
||||
/**
|
||||
* The link target a bot ref (`botRef`) becomes, e.g. `#bot-3`. The chat
|
||||
* renders these as links to that bot's conversation.
|
||||
*/
|
||||
export const BotRefHrefPrefix = "#bot-";
|
||||
|
||||
/**
|
||||
* Turns every computer ref (`[computer:3]`) and take-over ref
|
||||
* (`[take-over:3:browser]`) in a message into a markdown link, so it
|
||||
* (`[take-over:3:browser]`) and bot ref (`[bot:3]`) in a message into a markdown link, so it
|
||||
* survives markdown rendering as a link the chat can swap for a button.
|
||||
*/
|
||||
export default function linkComputerRefs({ text }: { text: string }) {
|
||||
@@ -26,5 +32,6 @@ export default function linkComputerRefs({ text }: { text: string }) {
|
||||
.replace(
|
||||
/\[take-over:(\d+):([a-z]+)\](?!\()/g,
|
||||
(_, id: string, mode: string) => `[take over](${TakeOverRefHrefPrefix}${id}-${mode})`,
|
||||
);
|
||||
)
|
||||
.replace(/\[bot:(\d+)\](?!\()/g, (_, id: string) => `[bot ${id}](${BotRefHrefPrefix}${id})`);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ describe("messagePreview", () => {
|
||||
).toBe("Found it Your GitHub session is on a computer. See the docs and search_computers. one two");
|
||||
});
|
||||
|
||||
test("names bots plainly", () => {
|
||||
expect(messagePreview({ text: "[bot:4] is on it." })).toBe("a bot is on it.");
|
||||
});
|
||||
|
||||
test("keeps code text without its fences", () => {
|
||||
expect(messagePreview({ text: "Run:\n```bash\nls ~\n```" })).toBe("Run: ls ~");
|
||||
});
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/**
|
||||
* A bot message as one line of plain text, for the rail's preview: no
|
||||
* markdown syntax, links as their text, computer refs (`computerRef`) as
|
||||
* "a computer", whitespace collapsed.
|
||||
* "a computer", bot refs (`botRef`) as "a bot", whitespace collapsed.
|
||||
*/
|
||||
export default function messagePreview({ text }: { text: string }) {
|
||||
return text
|
||||
.replace(/```[^\n]*\n?/g, "")
|
||||
.replace(/\[computer:\d+\](?!\()/g, "a computer")
|
||||
.replace(/\[bot:\d+\](?!\()/g, "a bot")
|
||||
.replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1")
|
||||
.replace(/^\s{0,3}(#{1,6}\s+|>\s?|[-*+]\s+|\d+\.\s+)/gm, "")
|
||||
.replace(/(\*\*|__|~~|`)/g, "")
|
||||
|
||||
Reference in New Issue
Block a user