fix: unignore shared/workspace package; add missing file to git
This commit is contained in:
parent
ea532659b0
commit
692d243157
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@ bin/
|
|||||||
# Workspace — conteúdo pessoal dos posts
|
# Workspace — conteúdo pessoal dos posts
|
||||||
workspace/
|
workspace/
|
||||||
workspace-test/
|
workspace-test/
|
||||||
|
!shared/workspace/
|
||||||
|
|
||||||
# Go build cache
|
# Go build cache
|
||||||
*.test
|
*.test
|
||||||
|
|||||||
200
shared/workspace/workspace.go
Normal file
200
shared/workspace/workspace.go
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
package workspace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ─── Path helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// PostPath returns the post root directory: <workspace>/<categoria>/<slug>
|
||||||
|
func PostPath(workspaceRoot, categoria, slug string) string {
|
||||||
|
return filepath.Join(workspaceRoot, categoria, slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputPath returns the input/ subdirectory of a post.
|
||||||
|
func InputPath(postPath string) string {
|
||||||
|
return filepath.Join(postPath, "input")
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorkPath returns the work/ subdirectory of a post.
|
||||||
|
func WorkPath(postPath string) string {
|
||||||
|
return filepath.Join(postPath, "work")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutputPath returns the output/ subdirectory of a post.
|
||||||
|
func OutputPath(postPath string) string {
|
||||||
|
return filepath.Join(postPath, "output")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtRawPath returns the work/art-raw/ subdirectory.
|
||||||
|
func ArtRawPath(postPath string) string {
|
||||||
|
return filepath.Join(postPath, "work", "art-raw")
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputTextoPath returns the path to input/texto.md.
|
||||||
|
func InputTextoPath(postPath string) string {
|
||||||
|
return filepath.Join(InputPath(postPath), "texto.md")
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutputPostPath returns the path to output/post.md.
|
||||||
|
func OutputPostPath(postPath string) string {
|
||||||
|
return filepath.Join(OutputPath(postPath), "post.md")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtPromptsPath returns work/art-prompts.json.
|
||||||
|
func ArtPromptsPath(postPath string) string {
|
||||||
|
return filepath.Join(WorkPath(postPath), "art-prompts.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
// InboxSugestoes returns the inbox sugestões file path.
|
||||||
|
func InboxSugestoes(workspaceRoot string) string {
|
||||||
|
return filepath.Join(workspaceRoot, "_inbox", "_sugestoes.md")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionedFile returns the path for a versioned file like redator-v1.md.
|
||||||
|
func VersionedFile(postPath, prefix string, n int) string {
|
||||||
|
return filepath.Join(WorkPath(postPath), fmt.Sprintf("%s-v%d.md", prefix, n))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Discovery ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// FindVariantsByBase returns all postPaths whose slug is exactly baseSlug OR
|
||||||
|
// starts with baseSlug+"--" (recycled variants like "rag-csharp--porque").
|
||||||
|
// Only paths that have a work/ subdirectory are included.
|
||||||
|
func FindVariantsByBase(workspaceRoot, baseSlug string) []string {
|
||||||
|
var found []string
|
||||||
|
cats, err := os.ReadDir(workspaceRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
prefix := baseSlug + "--"
|
||||||
|
for _, cat := range cats {
|
||||||
|
if !cat.IsDir() || strings.HasPrefix(cat.Name(), "_") || strings.HasPrefix(cat.Name(), ".") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
catPath := filepath.Join(workspaceRoot, cat.Name())
|
||||||
|
slugs, err := os.ReadDir(catPath)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, s := range slugs {
|
||||||
|
if !s.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := s.Name()
|
||||||
|
if name == baseSlug || strings.HasPrefix(name, prefix) {
|
||||||
|
candidate := filepath.Join(catPath, name)
|
||||||
|
if _, err := os.Stat(WorkPath(candidate)); err == nil {
|
||||||
|
found = append(found, candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindPostBySlug searches all category subdirectories for a post with the given
|
||||||
|
// slug. Returns the postPath on success.
|
||||||
|
func FindPostBySlug(workspaceRoot, slug string) (string, error) {
|
||||||
|
entries, err := os.ReadDir(workspaceRoot)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("ler workspace %s: %w", workspaceRoot, err)
|
||||||
|
}
|
||||||
|
for _, e := range entries {
|
||||||
|
if !e.IsDir() || strings.HasPrefix(e.Name(), "_") || strings.HasPrefix(e.Name(), ".") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
candidate := PostPath(workspaceRoot, e.Name(), slug)
|
||||||
|
if _, err := os.Stat(WorkPath(candidate)); err == nil {
|
||||||
|
return candidate, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("slug %q não encontrado em %s", slug, workspaceRoot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LatestVersionFile scans work/ for files matching <prefix>-vN.md and returns
|
||||||
|
// the path and version number of the highest version, or ("", 0) if none.
|
||||||
|
func LatestVersionFile(postPath, prefix string) (string, int) {
|
||||||
|
maxN := 0
|
||||||
|
for n := 1; n <= 99; n++ {
|
||||||
|
path := VersionedFile(postPath, prefix, n)
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
maxN = n
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if maxN == 0 {
|
||||||
|
return "", 0
|
||||||
|
}
|
||||||
|
return VersionedFile(postPath, prefix, maxN), maxN
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Directory setup ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// EnsureDirs creates input/, work/, output/, and work/art-raw/ for a post.
|
||||||
|
func EnsureDirs(postPath string) error {
|
||||||
|
dirs := []string{
|
||||||
|
InputPath(postPath),
|
||||||
|
WorkPath(postPath),
|
||||||
|
OutputPath(postPath),
|
||||||
|
ArtRawPath(postPath),
|
||||||
|
}
|
||||||
|
for _, d := range dirs {
|
||||||
|
if err := os.MkdirAll(d, 0755); err != nil {
|
||||||
|
return fmt.Errorf("criar %s: %w", d, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Slug ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// SlugFromTitle converts a human-readable title to a lowercase ASCII slug.
|
||||||
|
// "Como criar RAG com C#: guia completo" → "como-criar-rag-com-csharp-guia-completo"
|
||||||
|
func SlugFromTitle(title string) string {
|
||||||
|
// Replace common non-ASCII sequences before lowercasing
|
||||||
|
replacements := map[string]string{
|
||||||
|
"C#": "csharp",
|
||||||
|
"C++": "cpp",
|
||||||
|
".NET": "dotnet",
|
||||||
|
"@": "at",
|
||||||
|
}
|
||||||
|
for old, newVal := range replacements {
|
||||||
|
title = strings.ReplaceAll(title, old, newVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
title = strings.ToLower(title)
|
||||||
|
|
||||||
|
var result []rune
|
||||||
|
for _, r := range title {
|
||||||
|
switch {
|
||||||
|
case unicode.IsLetter(r) || unicode.IsDigit(r):
|
||||||
|
result = append(result, r)
|
||||||
|
case r == ' ' || r == '-' || r == '_' || r == ':' || r == '/':
|
||||||
|
if len(result) > 0 && result[len(result)-1] != '-' {
|
||||||
|
result = append(result, '-')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// All other chars (accents, punctuation) dropped
|
||||||
|
}
|
||||||
|
|
||||||
|
slug := strings.Trim(string(result), "-")
|
||||||
|
|
||||||
|
// Normalize accented chars with simple replacements
|
||||||
|
slug = strings.NewReplacer(
|
||||||
|
"ã", "a", "á", "a", "â", "a", "à", "a",
|
||||||
|
"é", "e", "ê", "e", "è", "e",
|
||||||
|
"í", "i", "î", "i",
|
||||||
|
"ó", "o", "ô", "o", "õ", "o",
|
||||||
|
"ú", "u", "û", "u",
|
||||||
|
"ç", "c",
|
||||||
|
"ñ", "n",
|
||||||
|
).Replace(slug)
|
||||||
|
|
||||||
|
return slug
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user