feat(export): add HTML card deck export
CI / test (pull_request) Failing after 10s

Self-contained single-file HTML export for cards. Mobile-first,
dark theme, zero dependencies. Each card type gets its own
interactive treatment: snippet tap-to-copy, template slot filling,
checklist with progress bar, decision structured layout, link
tap targets. Filter chips by type, search across all cards.

Usage: nib export -f html -o deck.html
       nib export -f html -t triage -o triage.html
This commit is contained in:
2026-05-20 21:49:19 -04:00
parent eea59b3f3c
commit 564039112a
3 changed files with 596 additions and 6 deletions
+60 -6
View File
@@ -6,19 +6,28 @@ import (
"os"
"github.com/lerko/nib/internal/db"
"github.com/lerko/nib/internal/export"
"github.com/spf13/cobra"
)
var exportOutput string
var (
exportOutput string
exportFormat string
exportTag string
exportTitle string
)
var exportCmd = &cobra.Command{
Use: "export",
Short: "dump all entities to JSON",
Short: "export entities to JSON or HTML card deck",
RunE: runExport,
}
func init() {
exportCmd.Flags().StringVarP(&exportOutput, "output", "o", "", "write to file instead of stdout")
exportCmd.Flags().StringVarP(&exportFormat, "format", "f", "json", "output format: json or html")
exportCmd.Flags().StringVarP(&exportTag, "tag", "t", "", "filter by tag (used as deck name for HTML)")
exportCmd.Flags().StringVar(&exportTitle, "title", "", "deck title for HTML export")
rootCmd.AddCommand(exportCmd)
}
@@ -48,13 +57,58 @@ func runExport(cmd *cobra.Command, _ []string) error {
}
defer store.Close()
ctx := cmd.Context()
p := db.DefaultListParams()
p.IncludeDeleted = true
p.Limit = 10000
entities, err := store.List(ctx, p)
if exportTag != "" {
p.Tag = &exportTag
}
switch exportFormat {
case "html":
return runHTMLExport(cmd, store, p)
case "json":
p.IncludeDeleted = true
return runJSONExport(cmd, store, p)
default:
return fmt.Errorf("unknown format %q (use json or html)", exportFormat)
}
}
func runHTMLExport(cmd *cobra.Command, store *db.Store, p db.ListParams) error {
p.CardsOnly = true
entities, err := store.List(cmd.Context(), p)
if err != nil {
return err
}
title := exportTitle
if title == "" && exportTag != "" {
title = "#" + exportTag
}
if title == "" {
title = "nib cards"
}
if exportOutput != "" {
f, err := os.Create(exportOutput)
if err != nil {
return err
}
defer f.Close()
if err := export.RenderHTML(f, entities, title); err != nil {
return err
}
fmt.Fprintf(cmd.ErrOrStderr(), "exported %d cards to %s\n", len(entities), exportOutput)
return nil
}
return export.RenderHTML(cmd.OutOrStdout(), entities, title)
}
func runJSONExport(cmd *cobra.Command, store *db.Store, p db.ListParams) error {
entities, err := store.List(cmd.Context(), p)
if err != nil {
return err
}