Working on CLI templates
This commit is contained in:
parent
6b34c2d29b
commit
1a9c9e6b8a
17 changed files with 106 additions and 5184 deletions
8
cmd/common.go
Normal file
8
cmd/common.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
var logo = ` ____ _
|
||||||
|
| _ \ _ __ ___ | |__ ___
|
||||||
|
| |_) | '__/ _ \| '_ \ / _ \
|
||||||
|
| __/| | | (_) | |_) | (_) |
|
||||||
|
|_| |_| \___/|_.__/ \___/
|
||||||
|
`
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
|
||||||
*/
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// createCmd represents the create command
|
|
||||||
var createCmd = &cobra.Command{
|
|
||||||
Use: "create",
|
|
||||||
Short: "A brief description of your command",
|
|
||||||
Long: `A longer description that spans multiple lines and likely contains examples
|
|
||||||
and usage of using your command. For example:
|
|
||||||
|
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
|
||||||
This application is a tool to generate the needed files
|
|
||||||
to quickly create a Cobra application.`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("create called")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(createCmd)
|
|
||||||
|
|
||||||
// Here you will define your flags and configuration settings.
|
|
||||||
|
|
||||||
// Cobra supports Persistent Flags which will work for this command
|
|
||||||
// and all subcommands, e.g.:
|
|
||||||
|
|
||||||
createCmd.PersistentFlags().StringP("input", "i", "", "Specify an input file")
|
|
||||||
|
|
||||||
// Cobra supports local flags which will only run when this command
|
|
||||||
// is called directly, e.g.:
|
|
||||||
// createCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
|
||||||
}
|
|
|
@ -5,59 +5,110 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"git.andreafazzi.eu/andrea/probo/cmd/filter"
|
"git.andreafazzi.eu/andrea/probo/cmd/filter"
|
||||||
"git.andreafazzi.eu/andrea/probo/cmd/util"
|
"git.andreafazzi.eu/andrea/probo/cmd/util"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/glamour"
|
||||||
|
"github.com/charmbracelet/huh"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/muesli/termenv"
|
"github.com/muesli/termenv"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// filterCmd represents the filter command
|
var longDescription string = `
|
||||||
var filterCmd = &cobra.Command{
|
# Filters
|
||||||
Use: "filter",
|
|
||||||
Short: "Create a new filter",
|
**Filters can made selection over stores.**
|
||||||
Long: "Create a new filter for selecting quizzes and participants",
|
|
||||||
Run: createFilter,
|
Filters allow you to narrow down selections across various stores. By
|
||||||
}
|
using filters, you can select participants, quizzes, and
|
||||||
|
responses. The command triggers a Text User Interface (TUI) that runs
|
||||||
|
a jq filter, displaying the outcome in real-time. After you're content
|
||||||
|
with the filtered JSON, pressing ⏎ will present the result on
|
||||||
|
stdout, enabling you to further process it by piping it forward.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Filter over participants store.
|
||||||
|
|
||||||
|
**probo filter participants**
|
||||||
|
|
||||||
|
Filter over quizzes store using the jq filter in tags.jq file
|
||||||
|
|
||||||
|
**probo filter quizzes -i data/filters/tags.jq**
|
||||||
|
|
||||||
|
Filter over participants and pipe the result on the next filter. The result is then stored in a JSON file.
|
||||||
|
|
||||||
|
**probo filter participants | probo filter quizzes > data/json/selection.json**
|
||||||
|
`
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createCmd.AddCommand(filterCmd)
|
desc, err := glamour.Render(fmt.Sprintf("```\n%s```\n%s", logo, longDescription), "dark")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
filterCmd.Flags().StringP("type", "t", "participants", "Select the type of filter (participants or quizzes)")
|
filterCmd := &cobra.Command{
|
||||||
|
Use: "filter {participants,quizzes,responses}",
|
||||||
|
Short: "Filter the given store",
|
||||||
|
Long: desc,
|
||||||
|
Run: runFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd.AddCommand(filterCmd)
|
||||||
|
filterCmd.PersistentFlags().StringP("input", "i", "", "Specify an input file")
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFilter(cmd *cobra.Command, args []string) {
|
func runFilter(cmd *cobra.Command, args []string) {
|
||||||
f := util.LogToFile()
|
var storeType string
|
||||||
if f != nil {
|
|
||||||
defer f.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
path, err := cmd.Flags().GetString("input")
|
path, err := cmd.Flags().GetString("input")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
filterType, err := cmd.Flags().GetString("type")
|
if len(args) < 1 {
|
||||||
if err != nil {
|
f := util.LogToFile()
|
||||||
panic(err)
|
if f != nil {
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
lipgloss.SetColorProfile(termenv.TrueColor)
|
||||||
|
|
||||||
|
form := huh.NewForm(huh.NewGroup(
|
||||||
|
huh.NewSelect[string]().
|
||||||
|
Title("Choose the store you want to filter").
|
||||||
|
Options(
|
||||||
|
huh.NewOption("Participants", "participants"),
|
||||||
|
huh.NewOption("Quizzes", "quizzes"),
|
||||||
|
huh.NewOption("Responses", "responses"),
|
||||||
|
).Value(&storeType),
|
||||||
|
))
|
||||||
|
|
||||||
|
err = form.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
storeType = args[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
lipgloss.SetColorProfile(termenv.TrueColor)
|
|
||||||
|
|
||||||
model, err := tea.NewProgram(
|
model, err := tea.NewProgram(
|
||||||
filter.New(path, filterType, util.ReadStdin()),
|
filter.New(path, storeType, util.ReadStdin()),
|
||||||
tea.WithOutput(os.Stderr),
|
tea.WithOutput(os.Stderr),
|
||||||
).Run()
|
).Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error running program:", err)
|
fmt.Println("Error running program:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
result := model.(*filter.FilterModel)
|
result := model.(*filter.FilterModel)
|
||||||
|
|
||||||
if result.Result != "" {
|
if result.Result != "" {
|
||||||
fmt.Fprintf(os.Stdout, result.Result)
|
fmt.Fprintf(os.Stdout, result.Result)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,23 +176,6 @@ func (m *FilterModel) marshalJSON() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if m.InputJson != "" {
|
if m.InputJson != "" {
|
||||||
// result := make([]interface{}, 2)
|
|
||||||
// err := json.Unmarshal([]byte(m.InputJson), &result[0])
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// filtered := fmt.Sprintf("{\"%s\": %s}", m.filterType, m.FilteredJson)
|
|
||||||
// err = json.Unmarshal([]byte(filtered), &result[1])
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// resultJson, err := json.Marshal(result)
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// m.Result = string(resultJson)
|
|
||||||
m.Result = fmt.Sprintf("{%s, \"%s\": %s}", strings.Trim(m.InputJson, "{}"), m.filterType, m.FilteredJson)
|
m.Result = fmt.Sprintf("{%s, \"%s\": %s}", strings.Trim(m.InputJson, "{}"), m.filterType, m.FilteredJson)
|
||||||
} else {
|
} else {
|
||||||
var result interface{}
|
var result interface{}
|
||||||
|
@ -307,28 +290,39 @@ func (m *FilterModel) loadStore() tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
var jsonStore []byte
|
var jsonStore []byte
|
||||||
|
|
||||||
if m.filterType == "participants" {
|
switch m.filterType {
|
||||||
|
case "participants":
|
||||||
pStore, err := file.NewDefaultParticipantFileStore()
|
pStore, err := file.NewDefaultParticipantFileStore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorMsg{err}
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonStore, err = pStore.Storer.Json()
|
jsonStore, err = pStore.Storer.Json()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorMsg{err}
|
panic(err)
|
||||||
}
|
}
|
||||||
} else if m.filterType == "quizzes" {
|
case "quizzes":
|
||||||
qStore, err := file.NewDefaultQuizFileStore()
|
qStore, err := file.NewDefaultQuizFileStore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorMsg{err}
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonStore, err = qStore.Storer.Json()
|
jsonStore, err = qStore.Storer.Json()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorMsg{err}
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
case "responses":
|
||||||
|
qStore, err := file.NewDefaultResponseFileStore()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonStore, err = qStore.Storer.Json()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
panic("Unknown filter type!")
|
panic("Unknown filter type!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ var (
|
||||||
ErrorState: []string{"ERROR 📖", "%v", "STORE 🟢"},
|
ErrorState: []string{"ERROR 📖", "%v", "STORE 🟢"},
|
||||||
}
|
}
|
||||||
filterTypeFormats = map[string]string{
|
filterTypeFormats = map[string]string{
|
||||||
"participants": "👫👫 Participants filter 👫👫",
|
"participants": "👫 Participants filter 👫",
|
||||||
"quizzes": "❓❓ Quizzes filter ❓❓",
|
"quizzes": "❓ Quizzes filter ❓",
|
||||||
|
"responses": "📝 Responsesfilter 📝",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
|
||||||
*/
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.andreafazzi.eu/andrea/probo/pkg/store/file"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// participantsCmd represents the participants command
|
|
||||||
var participantsCmd = &cobra.Command{
|
|
||||||
Use: "participants",
|
|
||||||
Short: "A brief description of your command",
|
|
||||||
Long: `A longer description that spans multiple lines and likely contains examples
|
|
||||||
and usage of using your command. For example:
|
|
||||||
|
|
||||||
Cobra is a CLI library for Go that empowers applications.
|
|
||||||
This application is a tool to generate the needed files
|
|
||||||
to quickly create a Cobra application.`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
importCSV(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
importCmd.AddCommand(participantsCmd)
|
|
||||||
|
|
||||||
// Here you will define your flags and configuration settings.
|
|
||||||
|
|
||||||
// Cobra supports Persistent Flags which will work for this command
|
|
||||||
// and all subcommands, e.g.:
|
|
||||||
// participantsCmd.PersistentFlags().String("foo", "", "A help for foo")
|
|
||||||
|
|
||||||
// Cobra supports local flags which will only run when this command
|
|
||||||
// is called directly, e.g.:
|
|
||||||
// participantsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
|
||||||
}
|
|
||||||
|
|
||||||
func importCSV(cmd *cobra.Command, args []string) {
|
|
||||||
pStore, err := file.NewDefaultParticipantFileStore()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = pStore.ImportCSV(args[0])
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
3
cmd/templates/filter.tmpl
Normal file
3
cmd/templates/filter.tmpl
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{{define "description"}}
|
||||||
|
{{.Description}}
|
||||||
|
{{end}}
|
2
cmd/templates/layout.tmpl
Normal file
2
cmd/templates/layout.tmpl
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
{{.Logo}}
|
||||||
|
{{template "description" .}}
|
121
misc/logseq/.gitignore
vendored
121
misc/logseq/.gitignore
vendored
|
@ -1,121 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
.pnpm-debug.log*
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
|
||||||
web_modules/
|
|
||||||
|
|
||||||
# TypeScript cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Microbundle cache
|
|
||||||
.rpt2_cache/
|
|
||||||
.rts2_cache_cjs/
|
|
||||||
.rts2_cache_es/
|
|
||||||
.rts2_cache_umd/
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
.env.test
|
|
||||||
.env.production
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
.parcel-cache
|
|
||||||
|
|
||||||
# Next.js build output
|
|
||||||
.next
|
|
||||||
out
|
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
|
||||||
.nuxt
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
|
||||||
.cache/
|
|
||||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
||||||
# public
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
# TernJS port file
|
|
||||||
.tern-port
|
|
||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
|
||||||
.vscode-test
|
|
||||||
|
|
||||||
# yarn v2
|
|
||||||
.yarn/cache
|
|
||||||
.yarn/unplugged
|
|
||||||
.yarn/build-state.yml
|
|
||||||
.yarn/install-state.gz
|
|
||||||
.pnp.*
|
|
||||||
|
|
||||||
*~
|
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 Andrea Fazzi
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -1,21 +0,0 @@
|
||||||
Copyright (c) 2022 Andrea Fazzi
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -1,4 +0,0 @@
|
||||||
# What's that?
|
|
||||||
|
|
||||||
A very basic boilerplate useful to start devoloping a
|
|
||||||
[Logseq](https://logseq.com/) plugin.
|
|
|
@ -1,10 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-hierarchy" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="gray" fill="gray" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
|
||||||
<circle cx="12" cy="5" r="2" />
|
|
||||||
<circle cx="5" cy="19" r="2" />
|
|
||||||
<circle cx="19" cy="19" r="2" />
|
|
||||||
<path d="M6.5 17.5l5.5 -4.5l5.5 4.5" />
|
|
||||||
<line x1="12" y1="7" x2="12" y2="13" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 471 B |
4707
misc/logseq/package-lock.json
generated
4707
misc/logseq/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,29 +0,0 @@
|
||||||
{
|
|
||||||
"logseq": {
|
|
||||||
"id": "logseq-probo-plugin",
|
|
||||||
"title": "logseq-probo-plugin",
|
|
||||||
"icon": "./icon.svg"
|
|
||||||
},
|
|
||||||
"name": "logseq-probo-plugin",
|
|
||||||
"version": "1.2.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "dist/index.html",
|
|
||||||
"targets": {
|
|
||||||
"main": false
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
|
||||||
"build": "parcel build --no-source-maps src/index.html --public-url ./"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "Andrea Fazzi",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@logseq/libs": "^0.0.1-alpha.35",
|
|
||||||
"js-base64": "^3.7.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"buffer": "^6.0.3",
|
|
||||||
"parcel": "^2.2.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Document</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script src="index.ts" type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,121 +0,0 @@
|
||||||
import "@logseq/libs";
|
|
||||||
import { BlockEntity } from "@logseq/libs/dist/LSPlugin.user";
|
|
||||||
|
|
||||||
const endpoint = 'http://localhost:3000/quizzes';
|
|
||||||
|
|
||||||
const uniqueIdentifier = () =>
|
|
||||||
Math.random()
|
|
||||||
.toString(36)
|
|
||||||
.replace(/[^a-z]+/g, "");
|
|
||||||
|
|
||||||
const sanitizeBlockContent = (text: string) => text.replace(/((?<=::).*|.*::)/g, "").replace(/{.*}/, "").trim()
|
|
||||||
|
|
||||||
async function fetchQuizzes() {
|
|
||||||
const { status: status, content: quizzes } = await fetch(endpoint).then(res => res.json())
|
|
||||||
const ret = quizzes || []
|
|
||||||
|
|
||||||
return ret.map((quiz, i) => {
|
|
||||||
return `${i + 1}. ${quiz.Question.Text}`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const render = (id, slot, status: ("modified" | "saved" | "error")) => {
|
|
||||||
logseq.provideUI({
|
|
||||||
key: `${id}`,
|
|
||||||
slot,
|
|
||||||
reset: true,
|
|
||||||
template: `
|
|
||||||
${status === 'saved' ? '<button data-on-click="createOrUpdateQuiz" class="renderBtn">Save</button><span>saved</span>' : '<button data-on-click="createOrUpdateQuiz" class="renderBtn">Save</button>'}
|
|
||||||
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const main = () => {
|
|
||||||
console.log("logseq-probo-plugin LOADED!");
|
|
||||||
|
|
||||||
logseq.Editor.registerSlashCommand("Get All Quizzes", async () => {
|
|
||||||
const currBlock = await logseq.Editor.getCurrentBlock();
|
|
||||||
|
|
||||||
let blocks = await fetchQuizzes()
|
|
||||||
|
|
||||||
blocks = blocks.map((it: BlockEntity) => ({ content: it }))
|
|
||||||
|
|
||||||
await logseq.Editor.insertBatchBlock(currBlock.uuid, blocks, {
|
|
||||||
sibling: false
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
logseq.Editor.registerSlashCommand("Create a new Probo quiz", async () => {
|
|
||||||
await logseq.Editor.insertAtEditingCursor(
|
|
||||||
`{{renderer :probo_${uniqueIdentifier()}}}`
|
|
||||||
);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
logseq.App.onMacroRendererSlotted(async ({ slot, payload }) => {
|
|
||||||
const [type] = payload.arguments;
|
|
||||||
|
|
||||||
if (!type.startsWith(":probo")) return
|
|
||||||
|
|
||||||
const id = type.split("_")[1]?.trim();
|
|
||||||
const proboId = `probo_${id}`;
|
|
||||||
|
|
||||||
let status: ("modified" | "saved" | "error")
|
|
||||||
|
|
||||||
logseq.provideModel({
|
|
||||||
async createOrUpdateQuiz() {
|
|
||||||
const parentBlock = await logseq.Editor.getBlock(payload.uuid, { includeChildren: true });
|
|
||||||
const answers = parentBlock.children.map((answer: BlockEntity, i: number) => {
|
|
||||||
return { text: answer.content, correct: (i == 0) ? true : false }
|
|
||||||
})
|
|
||||||
|
|
||||||
const quiz = {
|
|
||||||
question: { text: sanitizeBlockContent(parentBlock.content) },
|
|
||||||
answers: answers
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentBlock.properties.proboQuizUuid) {
|
|
||||||
const res = await fetch(endpoint + `/update/${parentBlock.properties.proboQuizUuid}`, { method: 'PUT', body: JSON.stringify(quiz) })
|
|
||||||
const data = await res.json();
|
|
||||||
await logseq.Editor.upsertBlockProperty(parentBlock.uuid, `probo-quiz-uuid`, data.content.ID)
|
|
||||||
render(proboId, slot, "saved")
|
|
||||||
} else {
|
|
||||||
const res = await fetch(endpoint + '/create', { method: 'POST', body: JSON.stringify(quiz) })
|
|
||||||
const data = await res.json();
|
|
||||||
await logseq.Editor.upsertBlockProperty(parentBlock.uuid, `probo-quiz-uuid`, data.content.ID)
|
|
||||||
render(proboId, slot, "saved")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
logseq.provideStyle(`
|
|
||||||
.renderBtn {
|
|
||||||
border: 1px solid white;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 5px;
|
|
||||||
margin-right: 5px;
|
|
||||||
font-size: 80%;
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.renderBtn:hover {
|
|
||||||
background-color: white;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
logseq.provideUI({
|
|
||||||
key: `${proboId}`,
|
|
||||||
slot,
|
|
||||||
reset: true,
|
|
||||||
template: `<button data-on-click="createOrUpdateQuiz" class="renderBtn">Save</button>`,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
logseq.ready(main).catch(console.error);
|
|
Loading…
Reference in a new issue