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
7 changes: 7 additions & 0 deletions .devin/wiki.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"repo_notes": [
{
"content": "## Introduction\n\nGhostbird is a Thunderbird add-on (a MailExtension) that enables users to compose emails using their preferred external text editor (VS Code, Vim, Sublime Text, etc.) through the GhostText protocol. The extension acts as a bridge between Thunderbird's compose window and external editors, providing real-time text synchronization.\n\n## Points to describe\n\n* Use Case: Ghostbird deals with two external systems; Thunderbird and the GhostText Server (and the text editor behind it), both of which the user controls. We should note it when describing overview.\n* Code Structure: Ghostbird's modules can primarily executed in three contexts: Background Script, Compose Script, and Options Page, as in `design.md`. Also, they can be grouped into three layers: Infrastructure (`src/root/`, `src/thunderbird/`, `src/util/`), Application (`src/app-*`), Domain (`src/ghosttext-*`). We should first introduce each context and communications between these, then introduce internals focusing role of each layer.\n* Tooling: Aside of source code, the Ghostbird repo also contains documentation (`doc/*.md`), build tool (`package.json` for Yarn; `tools/tsdown_config.ts` for tsdown), CI configuration(`.github/workflow`), test harness (`src/test/*.ts` for Vitest), and WebExtension configuration (manifest template `manifest_template.json`, localization messages `locales.toml`, and assets such as `ext/blue.svg`)."
}
]
}
7 changes: 2 additions & 5 deletions .github/site/coverpage.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@

![logo](ext/blue.svg)

# Ghostbird

> Compose email in your favorite text editor
> Compose email using your favorite text editor in Thunderbird
>
> <video alt="logo" src="https://github.com/user-attachments/assets/150ef991-10b8-45e2-bb2c-690f1b45a7ea" controls muted style="max-width: min(30rem, 90vw)">
> <video alt="logo" src="https://github.com/user-attachments/assets/150ef991-10b8-45e2-bb2c-690f1b45a7ea" controls muted style="max-width: min(600px, 90vw)">

[GitHub](https://github.com/exteditor/ghostbird/#readme)
[Codeberg](https://codeberg.org/exteditor/ghostbird#readme)
Expand Down
16 changes: 10 additions & 6 deletions .github/site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4.13.1/lib/themes/vue.min.css">
<link rel="icon" href="ext/blue.svg">
<script type="module" src="index.mjs"></script>
<style>
section.cover blockquote { margin: 0 }
</style>
<meta property="og:image" content="https://repository-images.githubusercontent.com/1054373981/354c0de9-d238-4139-a9ed-ba116f3d439e">
<meta property="og:image:alt" content="Ghostbird: GhostText for Thunderbird">
<meta property="og:type" content="website">
<meta property="og:title" content="Ghostbird: GhostText for Thunderbird">
<meta property="og:url" content="https://exteditor.github.io/ghostbird/">
<meta property="og:description" content="Ghostbird: Compose emails using your favorite text editor in Thunderbird">
</head>
<body>
<nav>
<a href="https://github.com/exteditor/ghostbird/">&#x1faba; GitHub</a>
<a href="#/doc/faq">&#x2753; FAQ</a>
<a href="https://translate.kagi.com/exteditor.github.io/ghostbird/">&#x1f30d; Translation</a>
</nav>
<div id="app"></div>
<div id="app"><a href="https://github.com/exteditor/ghostbird/">&#x1faba; GitHub</a></div>
</body>
</html>
25 changes: 10 additions & 15 deletions .github/site/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mermaid.initialize({ startOnLoad: false });
* @param {string} path Absolute path (/ points to the repository root) to the resource
* @returns {string} The URL to the resource
*/
const urlFor = (type, path) => `https://github.com/exteditor/ghostbird/${type}/main/${encodeURIComponent(path.slice(1))}`;
const urlFor = (type, path) => `https://github.com/exteditor/ghostbird/${type}/main/${encodeURIComponent(path.replace(/^[/]+/, ''))}`;

/**
* Build a Markdown text that redirects to a URL
Expand All @@ -31,8 +31,10 @@ ____

[▲ Back to Top](#top)
<div align="right">
Last Update: <a href="${urlFor('blob', vm.route.file)}">{docsify-updated}</a><br>
Powered by <a href="https://docsify.js.org/">Docsify</a>

Last Update: [{docsify-updated}](${urlFor('blob', vm.route.file)})<br>
Powered by [Docsify](https://docsify.js.org/)

</div>
`;

Expand All @@ -47,22 +49,15 @@ window.$docsify = {
executeScript: true,
homepage: "homepage.md",
coverpage: "coverpage.md",
loadNavbar: "navbar.md",
mergeNavbar: true,
auto2top: true,
maxLevel: 3,
themeColor: '#0b9dd6',
routes: {
['/README'](route) {
let url = urlFor('blob', `${route}.md`)
return redirectTo(url)
},
['/[-._/a-zA-Z]*[.][a-zA-Z]+$'](route) {
let url = urlFor('blob', route)
return redirectTo(url)
},
['/[-._/a-zA-Z]+/$'](route) {
let url = urlFor('tree', route)
return redirectTo(url)
},
'/README': (route) => redirectTo(urlFor('blob', `${route}.md`)),
'/[-._/a-zA-Z]*[.][a-zA-Z]+$': (route) => redirectTo(urlFor('blob', route)),
'/[-._/a-zA-Z]+/$': (route) => redirectTo(urlFor('tree', route)),
},
search: [
'/',
Expand Down
10 changes: 10 additions & 0 deletions .github/site/navbar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- &#x1f4c2; Pages
- [&#x1faba; README](/)
- [&#x2753; FAQ](/doc/faq)
- [&#x1f4c4; BUILDING](/BUILDING)
- [&#x1f4c4; CONTRIBUTING](/CONTRIBUTING)
- [&#x1f4c4; doc/building](/doc/building)
- [&#x1f4c4; doc/design](/doc/design)
- [&#x1f4c4; doc/testing](/doc/testing)
- [&#x1f4c4; doc/faq-architectural](/doc/faq-architectural)
- [&#x1f30d; Translation](https://translate.kagi.com/exteditor.github.io/ghostbird/)
25 changes: 13 additions & 12 deletions doc/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,25 +299,26 @@ ghostbird_runner -->|uses| ghostbird_session

### About `startup_*.ts`

TL;DR: `root` module contains entry point and the [Composition Root][ploeh].
TL;DR: `root/` module contains entry point and the [Composition Root][ploeh].

* [`root/startup/startup_*.ts`][startupdir] are used at the toplevel modules, namely [`background.ts`][backgroundts], [`compose.ts`][composets], and [`options.ts`][optionsts]. It initializes classes.
* Each of [`root/startup/startup_*.ts`][startupdir] is used in corresponding top-level module, namely [`background.ts`][backgroundts], [`compose.ts`][composets], and [`options.ts`][optionsts]. These modules initialize classes.
* All the non-root classes in the codebase are either:
* A) instantiated by `startup*()`, or
* B) instantiated directly with `new` operator by instances of (A).
* B) instantiated directly using the `new` operator by instances of (A).
* All (A) classes have a property `static isSingleton: boolean`.
* `startup*()` returns a factory on steroids; it scans classes that have a `static isSingleton` property and instantiates them.
* Instantiated classes are passed to the constructors of dependent classes, which must also define `static isSingleton`.
* `static isSingleton: boolean` indicates whether and how the class should be instantiated:
* The instantiated classes are passed to the constructors of dependent classes, which must also define `static isSingleton`.
* `static isSingleton: boolean` indicates how the class should be instantiated:
* If `true`, only one instance is created and shared.
* If `false`, a new instance is created each time it is needed.
* If `undefined` or the property is missing, the class is not instantiated automatically. Attempting to request it from automatically instantiated classes will result in an error.
* If a class wants to use another class, it should have a constructor parameter and a property having the same name as the class name (case-insensitive).
* A class may also have `static wantArray = true` to allow duplicate entries:
* If `true`, each argument to the constructor will be an array of one or more instances that have the same name.
* If `false`, `undefined`, or the property is missing, each argument to the constructor will be an instance of the class that has the name uniquely.
* A class may define `static aliases: string[] | string` to have the other name.
* Name clashes will result in error at startup, except ones passed to classes with `wantArray = true`.
* If the property is missing or contains any other value like `undefined`, the class is not instantiated automatically. Attempting to request it from a class that is automatically instantiated will result in an error.
* If a class wants to use another class, that class should have a field and a constructor parameter with the same name as the exported class name (case-insensitive).
* That is, constructors are expected to be simple, like `constructor(foo) { this.foo = foo; }`
* A class may also define `static wantArray = true` to allow duplicate entries:
* If `true`, each argument to the constructor will be an array of one or more instances that share the same name.
* If the property is missing or contains any other value like `undefined`, each argument to the constructor will be an instance of a class that uniquely matches that name.
* A class may define `static aliases: string[] | string` to have alternative names.
* Name clashes will result in an error at startup, except for those passed to classes with `wantArray = true`.
* Use of automatic instantiation must be restricted to `root/` to make the code easier to follow.
* [`test/startup.test.ts`](../src/test/startup.test.ts) contains tests to verify that all classes registered can be instantiated successfully.
* See [FAQ](./faq-architectural.md) for some design decisions and justification.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@
"jsdom": "^26.1.0",
"rolldown": "latest",
"toposort-class": "^1.0.1",
"tsdown": "^0.15.5",
"tsdown": "^0.15.9",
"typedoc": "^0.28.12",
"typedoc-plugin-mdn-links": "^5.0.9",
"typescript": "^5.9.3",
"vitest": "^3.2.4",
"web-ext": "^8.10.0"
"web-ext": "^9.0.0"
},
"peerDependencies": {
"@vitest/ui": "*"
Expand Down
4 changes: 2 additions & 2 deletions tools/tsdown_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const esmConfig = {
// Generate _locales/*/messages.json
generateLocaleMessages({ path: "./locales.toml" }),
// Upload bundle size info to Codecov
isRelease && codecov({ env, bundleName: "background" }),
isRelease ? codecov({ env, bundleName: "background" }) : null,
],
} satisfies Options

Expand All @@ -115,7 +115,7 @@ const iifeConfig = {
format: "iife",
plugins: [
// Upload bundle size info to Codecov
isRelease && codecov({ env, bundleName: "compose" }),
isRelease ? codecov({ env, bundleName: "compose" }) : null,
],
} satisfies Options

Expand Down
Loading