lunerfox126 downloadsRender live, auto-refreshing Gitea issue cards in your notes and create issues without leaving the editor.
Render live, auto-refreshing Gitea issue cards inline in your Obsidian notes, and create issues without leaving the editor.
Status: feature-complete and being prepared for the Obsidian community directory. Cards, search, milestone progress, the dependency graph, inline references, autocomplete, quick actions, paste-to-card, per-repo accents, and Cloudflare-access support have all shipped.
gitea-issue code block with a single issue reference
and get a compact card: number (linked), title, truncated body, label chips,
comment count, state, author, milestone, and last-updated time — all from one
Gitea API call.comments: 3
line under the reference to auto-expand the thread with a given count; the default
count is configurable in settings.gitea-search lists issues by repo/state/
labels/milestone, as a compact chip-like table by default (layout: cards
for full cards); gitea-milestone renders a progress bar. A Build search
query command opens a wizard that fills the block's fields from dropdowns and
label chips with a live preview, so you never have to memorise the syntax.gitea-graph block scopes a milestone (or any search
filter) and renders its issues as a dependency DAG: one node per issue coloured
by state, edges drawn blocker → dependent from native Gitea dependencies (and
the blocked-by:#N label convention), with still-blocked issues dimmed so the
currently-unblocked work — what the issue processor picks up next — stands out.
Add direction: LR for a left-to-right layout; click a node to open it in Gitea.
A Build graph view command opens a wizard that scopes the graph to a set
of milestones (added from a dropdown, listed as removable chips), with the same
repo/labels/state/limit/direction controls and live preview, so you never have
to memorise the syntax.…/owner/repo/issues/123) into the
editor and it is replaced in place with a live gitea-issue card. Only links on
your configured base host are converted; other text and links paste untouched.GITEA:owner/repo:#123 with autocomplete at each
colon; on completion it is wrapped in backticks (`GITEA:owner/repo:#123`)
and the backtick-wrapped reference renders as a lightweight chip showing the
issue title and open/closed status. A bare, un-backticked reference in prose is
left as plain text.The plugin is a single TypeScript source, main.ts, bundled to main.js by
esbuild. There is no runtime framework beyond the Obsidian API; the design is
deliberately small so it stays inside the constraints of Obsidian's automated
community review (mobile-safe, no Node/Electron APIs, DOM built with createEl).
Entry point — GiteaCardsPlugin extends Plugin. onload() wires everything
and relies on Obsidian's component registration (registerInterval,
registerEvent, registerDomEvent, addCommand, registerMarkdownCodeBlockProcessor,
registerEditorExtension, registerMarkdownPostProcessor) so every timer,
listener, and view tears down automatically on unload.
Rendered blocks. Four fenced code-block languages each map to a
MarkdownRenderChild subclass that owns its own auto-refresh interval, tied to the
rendered element's lifecycle:
| Block | Render child | Renders |
|---|---|---|
gitea-issue |
IssueCardChild |
one issue card from a single owner/repo#index reference |
gitea-search |
SearchBlockChild |
a chip-like table (or list of cards) from a repo/state/label/milestone query |
gitea-milestone |
MilestoneBlockChild |
a progress bar from a milestone's open/closed counts |
gitea-graph |
GraphBlockChild |
a dependency DAG (SVG) of a milestone's/query's issues, coloured by state with blocker → dependent edges |
Inline references. A backtick-wrapped `GITEA:owner/repo:#123` renders as a
lightweight chip through two paths: a CodeMirror 6 editor extension
(inlineChipEditorExtension + InlineChipWidget) for Live Preview, and a
registerMarkdownPostProcessor (InlineChipChild) for Reading view. Typing the
reference is assisted by GiteaInlineSuggest extends EditorSuggest, which offers
autocomplete at each colon. Both render paths refresh on the same interval as cards.
Issue creation & insertion. NewIssueModal and InsertIssueModal (plus the
matching commands and the paste handler that rewrites pasted Gitea URLs) drive the
write/insert flows. SearchQueryWizardModal builds gitea-search blocks from a
form with a live preview (serialised by buildSearchBlock, the inverse of
parseSearchQuery). Repository fields use RepoInputSuggest extends AbstractInputSuggest for slug autocomplete.
Network layer. Every Gitea call goes through Obsidian's requestUrl() — never
fetch — so it works on mobile and bypasses Electron's CORS handling. Requests are
built from the configured base URL ({base}/api/v1/...); card and issue links are
built from the same base + /{owner}/{repo}/issues/{index}, ignoring the API's
html_url (Gitea's ROOT_URL is still the LAN IP). buildAuthHeaders() attaches
Authorization: token <PAT> and, when the base URL is a Cloudflare host, the
CF-Access-Client-Id / CF-Access-Client-Secret service-token pair. List endpoints
pass type=issues (and drop any pull_request item) so PRs never leak into results.
Caching & rate control. Gitea has no ETag and no rate limiter, so a shared
CoalescingCache / IssueCache, keyed by owner/repo#index, coalesces duplicate
in-flight fetches and serves a freshness window. Refresh intervals are floored at
MIN_REFRESH_SECONDS (30 s); transient failures back off exponentially up to
MAX_BACKOFF_MS (30 min), and unreachable cards render stale rather than erroring.
Settings & secrets. GiteaCardsSettingTab renders configuration; the Gitea PAT
and Cloudflare Access credentials live in app.secretStorage (the OS keychain),
never in data.json.
This plugin is not yet in the community directory. To build it locally:
npm install
npm run build # emits main.js
Then copy main.js, manifest.json, and styles.css into your vault under
.obsidian/plugins/gitea-cards/ and enable the plugin in Settings → Community
plugins.
| Setting | Default | Notes |
|---|---|---|
| Gitea base URL | — | Your Gitea instance, e.g. https://gitea.example.com. Card links are built from this URL; a local-network or remote address is a manual fallback. |
| Default repository | — | owner/repo used by the new-issue flow and bare references. |
| Inline keyword | GITEA |
Trigger for the inline reference syntax. |
| In-progress label | — | Open issues carrying this label show an amber dot and "in progress" badge on their inline chip. Empty disables it. |
| Refresh interval | 60 s |
Floored at 30 s. |
| Description truncation length | 160 |
Max characters of body shown on a card. |
| Comment preview count | 3 |
Recent comments shown when a card's thread is expanded; a comments: N block key overrides it per card. |
| Gitea API token | — | write:issue + read:repository; stored in the OS keychain. |
| Cloudflare Access client ID / secret | — | Service-token credentials; stored in the OS keychain. |
Tokens are stored via Obsidian's secret storage (OS keychain), never in
data.json.
npm run dev — esbuild watch build.npm run build — type-check + production build.npm run lint — eslint-plugin-obsidianmd (the official community-review lint).npm version patch|minor|major — bumps manifest.json + versions.json in
lockstep via version-bump.mjs.CI (.gitea/workflows/ci.yml) lints and builds on every push; the GitHub mirror
cuts draft releases via .github/workflows/release.yml.