564039112a
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
168 lines
4.1 KiB
Go
168 lines
4.1 KiB
Go
package cmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/lerko/nib/internal/db"
|
|
"github.com/lerko/nib/internal/export"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
exportOutput string
|
|
exportFormat string
|
|
exportTag string
|
|
exportTitle string
|
|
)
|
|
|
|
var exportCmd = &cobra.Command{
|
|
Use: "export",
|
|
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)
|
|
}
|
|
|
|
type exportEntity struct {
|
|
ID string `json:"id"`
|
|
CreatedAt string `json:"created_at"`
|
|
ModifiedAt string `json:"modified_at"`
|
|
Body string `json:"body"`
|
|
Title *string `json:"title,omitempty"`
|
|
Description *string `json:"description,omitempty"`
|
|
Glyph string `json:"glyph"`
|
|
TimeAnchor *string `json:"time_anchor,omitempty"`
|
|
CompletedAt *string `json:"completed_at,omitempty"`
|
|
Pinned bool `json:"pinned"`
|
|
DeletedAt *string `json:"deleted_at,omitempty"`
|
|
Tags []string `json:"tags"`
|
|
CardType *string `json:"card_type,omitempty"`
|
|
CardData *string `json:"card_data,omitempty"`
|
|
UseCount int `json:"use_count"`
|
|
LastUsedAt *string `json:"last_used_at,omitempty"`
|
|
}
|
|
|
|
func runExport(cmd *cobra.Command, _ []string) error {
|
|
store, err := openStore()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer store.Close()
|
|
|
|
p := db.DefaultListParams()
|
|
p.Limit = 10000
|
|
|
|
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
|
|
}
|
|
|
|
out := make([]exportEntity, len(entities))
|
|
for i, e := range entities {
|
|
out[i] = exportEntity{
|
|
ID: e.ID,
|
|
CreatedAt: e.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
|
ModifiedAt: e.ModifiedAt.Format("2006-01-02T15:04:05Z07:00"),
|
|
Body: e.Body,
|
|
Title: e.Title,
|
|
Glyph: string(e.Glyph),
|
|
TimeAnchor: e.TimeAnchor,
|
|
Pinned: e.Pinned,
|
|
Tags: e.Tags,
|
|
CardData: e.CardData,
|
|
UseCount: e.UseCount,
|
|
}
|
|
if e.Description != nil {
|
|
out[i].Description = e.Description
|
|
}
|
|
if e.CompletedAt != nil {
|
|
s := e.CompletedAt.Format("2006-01-02T15:04:05Z07:00")
|
|
out[i].CompletedAt = &s
|
|
}
|
|
if e.DeletedAt != nil {
|
|
s := e.DeletedAt.Format("2006-01-02T15:04:05Z07:00")
|
|
out[i].DeletedAt = &s
|
|
}
|
|
if e.CardType != nil {
|
|
s := string(*e.CardType)
|
|
out[i].CardType = &s
|
|
}
|
|
if e.LastUsedAt != nil {
|
|
s := e.LastUsedAt.Format("2006-01-02T15:04:05Z07:00")
|
|
out[i].LastUsedAt = &s
|
|
}
|
|
}
|
|
|
|
data, err := json.MarshalIndent(out, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if exportOutput != "" {
|
|
if err := os.WriteFile(exportOutput, data, 0o600); err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintf(cmd.ErrOrStderr(), "exported %d entities to %s\n", len(out), exportOutput)
|
|
return nil
|
|
}
|
|
|
|
fmt.Println(string(data))
|
|
return nil
|
|
}
|