Zettelstore

Check-in Differences
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From v0.17.0 To trunk

2024-04-22
15:01
Rename package sxhtml to sxwebs/sxhtml; update some dependencies ... (Leaf check-in: 3e4f0da507 user: stern tags: trunk)
2024-04-18
13:30
Adapt to client change: api.URLBuilder ... (check-in: 85cb9d749b user: stern tags: trunk)
2024-03-06
15:02
Increase version to 0.18.0-dev to begin next development cycle
... (check-in: 51c141a192 user: stern tags: trunk)
2024-03-04
17:08
Version 0.17.0 ... (check-in: c863ee5f61 user: stern tags: trunk, release, v0.17.0)
13:47
Adapt to sx changes; add SPDX license identifiers ... (check-in: 5485ba3ce3 user: stern tags: trunk)

Changes to README.md.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
gradually, one major focus is a long-term store of these notes, hence the name
“Zettelstore”.

To get an initial impression, take a look at the
[manual](https://zettelstore.de/manual/). It is a live example of the
zettelstore software, running in read-only mode.

[Zettelstore Client](https://zettelstore.de/client) provides client
software to access Zettelstore via its API more easily, [Zettelstore
Contrib](https://zettelstore.de/contrib) contains contributed software, which
often connects to Zettelstore via its API. Some of the software packages may be
experimental.

The software, including the manual, is licensed
under the [European Union Public License 1.2 (or
later)](https://zettelstore.de/home/file?name=LICENSE.txt&ci=trunk).

[Stay tuned](https://mastodon.social/tags/Zettelstore) …







|
|









9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
gradually, one major focus is a long-term store of these notes, hence the name
“Zettelstore”.

To get an initial impression, take a look at the
[manual](https://zettelstore.de/manual/). It is a live example of the
zettelstore software, running in read-only mode.

[Zettelstore Client](https://t73f.de/r/zsc) provides client software to access
Zettelstore via its API more easily, [Zettelstore
Contrib](https://zettelstore.de/contrib) contains contributed software, which
often connects to Zettelstore via its API. Some of the software packages may be
experimental.

The software, including the manual, is licensed
under the [European Union Public License 1.2 (or
later)](https://zettelstore.de/home/file?name=LICENSE.txt&ci=trunk).

[Stay tuned](https://mastodon.social/tags/Zettelstore) …

Changes to VERSION.

1
0.17.0
|
1
0.18.0-dev

Changes to ast/block.go.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

package ast

import "zettelstore.de/client.fossil/attrs"

// Definition of Block nodes.

// BlockSlice is a slice of BlockNodes.
type BlockSlice []BlockNode

func (*BlockSlice) blockNode() { /* Just a marker */ }







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

package ast

import "t73f.de/r/zsc/attrs"

// Definition of Block nodes.

// BlockSlice is a slice of BlockNodes.
type BlockSlice []BlockNode

func (*BlockSlice) blockNode() { /* Just a marker */ }

Changes to ast/inline.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package ast

import (
	"unicode/utf8"

	"zettelstore.de/client.fossil/attrs"
)

// Definitions of inline nodes.

// InlineSlice is a list of BlockNodes.
type InlineSlice []InlineNode








|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package ast

import (
	"unicode/utf8"

	"t73f.de/r/zsc/attrs"
)

// Definitions of inline nodes.

// InlineSlice is a list of BlockNodes.
type InlineSlice []InlineNode

Changes to ast/ref.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package ast

import (
	"net/url"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/zettel/id"
)

// QueryPrefix is the prefix that denotes a query expression.
const QueryPrefix = api.QueryPrefix

// ParseReference parses a string and returns a reference.







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package ast

import (
	"net/url"
	"strings"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/zettel/id"
)

// QueryPrefix is the prefix that denotes a query expression.
const QueryPrefix = api.QueryPrefix

// ParseReference parses a string and returns a reference.

Changes to ast/walk_test.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package ast_test

import (
	"testing"

	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/z/ast"
)

func BenchmarkWalk(b *testing.B) {
	root := ast.BlockSlice{
		&ast.HeadingNode{
			Inlines: ast.CreateInlineSliceFromWords("A", "Simple", "Heading"),







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package ast_test

import (
	"testing"

	"t73f.de/r/zsc/attrs"
	"zettelstore.de/z/ast"
)

func BenchmarkWalk(b *testing.B) {
	root := ast.BlockSlice{
		&ast.HeadingNode{
			Inlines: ast.CreateInlineSliceFromWords("A", "Simple", "Heading"),

Changes to auth/impl/digest.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import (
	"bytes"
	"crypto"
	"crypto/hmac"
	"encoding/base64"

	"zettelstore.de/sx.fossil"
	"zettelstore.de/sx.fossil/sxreader"
)

var encoding = base64.RawURLEncoding

const digestAlg = crypto.SHA384

func sign(claim sx.Object, secret []byte) ([]byte, error) {







|
|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import (
	"bytes"
	"crypto"
	"crypto/hmac"
	"encoding/base64"

	"t73f.de/r/sx"
	"t73f.de/r/sx/sxreader"
)

var encoding = base64.RawURLEncoding

const digestAlg = crypto.SHA384

func sign(claim sx.Object, secret []byte) ([]byte, error) {

Changes to auth/impl/impl.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

import (
	"errors"
	"hash/fnv"
	"io"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/sexp"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/auth/policy"
	"zettelstore.de/z/box"
	"zettelstore.de/z/config"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"







|
|
|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

import (
	"errors"
	"hash/fnv"
	"io"
	"time"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/sexp"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/auth/policy"
	"zettelstore.de/z/box"
	"zettelstore.de/z/config"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
	if !ok || subject == "" {
		return nil, ErrNoIdent
	}

	now := time.Now().Round(time.Second)
	sClaim := sx.MakeList(
		sx.Int64(kind),
		sx.String(subject),
		sx.Int64(now.Unix()),
		sx.Int64(now.Add(d).Unix()),
		sx.Int64(ident.Zid),
	)
	return sign(sClaim, a.secret)
}








|







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
	if !ok || subject == "" {
		return nil, ErrNoIdent
	}

	now := time.Now().Round(time.Second)
	sClaim := sx.MakeList(
		sx.Int64(kind),
		sx.MakeString(subject),
		sx.Int64(now.Unix()),
		sx.Int64(now.Add(d).Unix()),
		sx.Int64(ident.Zid),
	)
	return sign(sClaim, a.secret)
}

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
	vals, err := sexp.ParseList(obj, "isiii")
	if err != nil {
		return ErrMalformedToken
	}
	if auth.TokenKind(vals[0].(sx.Int64)) != k {
		return ErrOtherKind
	}
	ident := vals[1].(sx.String)
	if ident == "" {
		return ErrNoIdent
	}
	issued := time.Unix(int64(vals[2].(sx.Int64)), 0)
	expires := time.Unix(int64(vals[3].(sx.Int64)), 0)
	now := time.Now().Round(time.Second)
	if expires.Before(now) {







|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
	vals, err := sexp.ParseList(obj, "isiii")
	if err != nil {
		return ErrMalformedToken
	}
	if auth.TokenKind(vals[0].(sx.Int64)) != k {
		return ErrOtherKind
	}
	ident := vals[1].(sx.String).GetValue()
	if ident == "" {
		return ErrNoIdent
	}
	issued := time.Unix(int64(vals[2].(sx.Int64)), 0)
	expires := time.Unix(int64(vals[3].(sx.Int64)), 0)
	now := time.Now().Round(time.Second)
	if expires.Before(now) {

Changes to auth/policy/default.go.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

package policy

import (
	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/zettel/meta"
)

type defaultPolicy struct {
	manager auth.AuthzManager
}







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

package policy

import (
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/zettel/meta"
)

type defaultPolicy struct {
	manager auth.AuthzManager
}

Changes to auth/policy/owner.go.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

package policy

import (
	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/config"
	"zettelstore.de/z/zettel/meta"
)

type ownerPolicy struct {
	manager    auth.AuthzManager







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

package policy

import (
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/config"
	"zettelstore.de/z/zettel/meta"
)

type ownerPolicy struct {
	manager    auth.AuthzManager

Changes to auth/policy/policy_test.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package policy

import (
	"fmt"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func TestPolicies(t *testing.T) {
	t.Parallel()







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package policy

import (
	"fmt"
	"testing"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func TestPolicies(t *testing.T) {
	t.Parallel()

Changes to box/box.go.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import (
	"context"
	"errors"
	"fmt"
	"io"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// BaseBox is implemented by all Zettel boxes.







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import (
	"context"
	"errors"
	"fmt"
	"io"
	"time"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// BaseBox is implemented by all Zettel boxes.

Changes to box/compbox/compbox.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Package compbox provides zettel that have computed content.
package compbox

import (
	"context"
	"net/url"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/manager"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"







|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Package compbox provides zettel that have computed content.
package compbox

import (
	"context"
	"net/url"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/manager"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
48
49
50
51
52
53
54

55
56
57
58
59
60
61
	meta    func(id.Zid) *meta.Meta
	content func(*meta.Meta) []byte
}{
	id.MustParse(api.ZidVersion):              {genVersionBuildM, genVersionBuildC},
	id.MustParse(api.ZidHost):                 {genVersionHostM, genVersionHostC},
	id.MustParse(api.ZidOperatingSystem):      {genVersionOSM, genVersionOSC},
	id.MustParse(api.ZidLog):                  {genLogM, genLogC},

	id.MustParse(api.ZidBoxManager):           {genManagerM, genManagerC},
	id.MustParse(api.ZidMetadataKey):          {genKeysM, genKeysC},
	id.MustParse(api.ZidParser):               {genParserM, genParserC},
	id.MustParse(api.ZidStartupConfiguration): {genConfigZettelM, genConfigZettelC},
}

// Get returns the one program box.







>







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
	meta    func(id.Zid) *meta.Meta
	content func(*meta.Meta) []byte
}{
	id.MustParse(api.ZidVersion):              {genVersionBuildM, genVersionBuildC},
	id.MustParse(api.ZidHost):                 {genVersionHostM, genVersionHostC},
	id.MustParse(api.ZidOperatingSystem):      {genVersionOSM, genVersionOSC},
	id.MustParse(api.ZidLog):                  {genLogM, genLogC},
	id.MustParse(api.ZidMemory):               {genMemoryM, genMemoryC},
	id.MustParse(api.ZidBoxManager):           {genManagerM, genManagerC},
	id.MustParse(api.ZidMetadataKey):          {genKeysM, genKeysC},
	id.MustParse(api.ZidParser):               {genParserM, genParserC},
	id.MustParse(api.ZidStartupConfiguration): {genConfigZettelM, genConfigZettelC},
}

// Get returns the one program box.

Changes to box/compbox/config.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package compbox

import (
	"bytes"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genConfigZettelM(zid id.Zid) *meta.Meta {
	if myConfig == nil {







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package compbox

import (
	"bytes"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genConfigZettelM(zid id.Zid) *meta.Meta {
	if myConfig == nil {

Changes to box/compbox/keys.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package compbox

import (
	"bytes"
	"fmt"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genKeysM(zid id.Zid) *meta.Meta {
	m := meta.New(zid)







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package compbox

import (
	"bytes"
	"fmt"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genKeysM(zid id.Zid) *meta.Meta {
	m := meta.New(zid)

Changes to box/compbox/log.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package compbox

import (
	"bytes"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genLogM(zid id.Zid) *meta.Meta {
	m := meta.New(zid)







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package compbox

import (
	"bytes"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genLogM(zid id.Zid) *meta.Meta {
	m := meta.New(zid)

Changes to box/compbox/manager.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package compbox

import (
	"bytes"
	"fmt"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genManagerM(zid id.Zid) *meta.Meta {
	m := meta.New(zid)







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package compbox

import (
	"bytes"
	"fmt"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genManagerM(zid id.Zid) *meta.Meta {
	m := meta.New(zid)

Added box/compbox/memory.go.

























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//-----------------------------------------------------------------------------
// Copyright (c) 2024-present Detlef Stern
//
// This file is part of Zettelstore.
//
// Zettelstore is licensed under the latest version of the EUPL (European Union
// Public License). Please see file LICENSE.txt for your rights and obligations
// under this license.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2024-present Detlef Stern
//-----------------------------------------------------------------------------

package compbox

import (
	"bytes"
	"fmt"
	"os"
	"runtime"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genMemoryM(zid id.Zid) *meta.Meta {
	if myConfig == nil {
		return nil
	}
	m := meta.New(zid)
	m.Set(api.KeyTitle, "Zettelstore Memory")
	m.Set(api.KeyCreated, kernel.Main.GetConfig(kernel.CoreService, kernel.CoreStarted).(string))
	m.Set(api.KeyVisibility, api.ValueVisibilityExpert)
	return m
}

func genMemoryC(*meta.Meta) []byte {
	pageSize := os.Getpagesize()
	var m runtime.MemStats
	runtime.GC()
	runtime.ReadMemStats(&m)

	var buf bytes.Buffer
	buf.WriteString("|=Name|=Value>\n")
	fmt.Fprintf(&buf, "|Page Size|%d\n", pageSize)
	fmt.Fprintf(&buf, "|Pages|%d\n", m.HeapSys/uint64(pageSize))
	fmt.Fprintf(&buf, "|Heap Objects|%d\n", m.HeapObjects)
	fmt.Fprintf(&buf, "|Heap Sys (KiB)|%d\n", m.HeapSys/1024)
	fmt.Fprintf(&buf, "|Heap Inuse (KiB)|%d\n", m.HeapInuse/1024)
	debug := kernel.Main.GetConfig(kernel.CoreService, kernel.CoreDebug).(bool)
	if debug {
		for i, bysize := range m.BySize {
			fmt.Fprintf(&buf, "|Size %2d: %d|%d - %d → %d\n",
				i, bysize.Size, bysize.Mallocs, bysize.Frees, bysize.Mallocs-bysize.Frees)
		}
	}
	return buf.Bytes()
}

Changes to box/compbox/parser.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"bytes"
	"fmt"
	"sort"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genParserM(zid id.Zid) *meta.Meta {







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"bytes"
	"fmt"
	"sort"
	"strings"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func genParserM(zid id.Zid) *meta.Meta {

Changes to box/compbox/version.go.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

package compbox

import (
	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func getVersionMeta(zid id.Zid, title string) *meta.Meta {
	m := meta.New(zid)







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

package compbox

import (
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func getVersionMeta(zid id.Zid, title string) *meta.Meta {
	m := meta.New(zid)

Changes to box/constbox/constbox.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package constbox

import (
	"context"
	_ "embed" // Allow to embed file content
	"net/url"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/manager"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package constbox

import (
	"context"
	_ "embed" // Allow to embed file content
	"net/url"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/manager"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
			api.KeyTitle:      "Zettelstore Dependencies",
			api.KeyRole:       api.ValueRoleConfiguration,
			api.KeySyntax:     meta.SyntaxZmk,
			api.KeyLang:       api.ValueLangEN,
			api.KeyReadOnly:   api.ValueTrue,
			api.KeyVisibility: api.ValueVisibilityPublic,
			api.KeyCreated:    "20210504135842",
			api.KeyModified:   "20230601163100",
		},
		zettel.NewContent(contentDependencies)},
	id.BaseTemplateZid: {
		constHeader{
			api.KeyTitle:      "Zettelstore Base HTML Template",
			api.KeyRole:       api.ValueRoleConfiguration,
			api.KeySyntax:     meta.SyntaxSxn,







|







168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
			api.KeyTitle:      "Zettelstore Dependencies",
			api.KeyRole:       api.ValueRoleConfiguration,
			api.KeySyntax:     meta.SyntaxZmk,
			api.KeyLang:       api.ValueLangEN,
			api.KeyReadOnly:   api.ValueTrue,
			api.KeyVisibility: api.ValueVisibilityPublic,
			api.KeyCreated:    "20210504135842",
			api.KeyModified:   "20240418095500",
		},
		zettel.NewContent(contentDependencies)},
	id.BaseTemplateZid: {
		constHeader{
			api.KeyTitle:      "Zettelstore Base HTML Template",
			api.KeyRole:       api.ValueRoleConfiguration,
			api.KeySyntax:     meta.SyntaxSxn,

Changes to box/constbox/dependencies.zettel.

126
127
128
129
130
131
132
133
134
135
136
137
138


139


140
141
142
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.
```

=== sx, zettelstore-client
These are companion projects, written by the current main developer of Zettelstore.
They are published under the same license, [[EUPL v1.2, or later|00000000000004]].

; URL & Source sx
: [[https://zettelstore.de/sx]]


; URL & Source zettelstore-client


: [[https://zettelstore.de/client/]]
; License:
: European Union Public License, version 1.2 (EUPL v1.2), or later.







|
|


|
|
>
>
|
>
>
|


126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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.
```

=== Sx, SxWebs, Webs, Zettelstore-Client
These are companion projects, written by the main developer of Zettelstore.
They are published under the same license, [[EUPL v1.2, or later|00000000000004]].

; URL & Source Sx
: [[https://t73f.de/r/sx]]
; URL & Source SxWebs
: [[https://t73f.de/r/sxwebs]]
; URL & Source Webs
: [[https://t73f.de/r/webs]]
; URL & Source Zettelstore-Client
: [[https://t73f.de/r/zsc]]
; License:
: European Union Public License, version 1.2 (EUPL v1.2), or later.

Changes to box/constbox/prelude.sxn.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------

;;; This zettel contains sxn definitions that are independent of specific
;;; subsystems, such as WebUI, API, or other. It just contains generic code to
;;; be used in all places.

;; Constants NIL and T
(defconst NIL ())
(defconst T   'T)

;; defunconst macro to define functions that are bound as a constant.
;;
;; (defunconst NAME ARGS EXPR ...)
(defmacro defunconst (name args . body)
    `(begin (defun ,name ,args ,@body) (defconst ,name ,name)))

;; not macro
(defmacro not (x) `(if ,x NIL T))

;; not= macro, to negate an equivalence
(defmacro not= args `(not (= ,@args)))

;; let macro
;;
;; (let (BINDING ...) EXPR ...), where BINDING is a list of two elements
;;   (SYMBOL EXPR)
(defmacro let (bindings . body)
    `((lambda ,(map car bindings) ,@body) ,@(map cadr bindings)))

;; let* macro
;;
;; (let* (BINDING ...) EXPR ...), where SYMBOL may occur in later bindings.
(defmacro let* (bindings . body)
    (if (null? bindings)
        `((lambda () ,@body))
        `((lambda (,(caar bindings))
                  (let* ,(cdr bindings) ,@body))
                  ,(cadar bindings))))

;; cond macro
;;
;; (cond ((COND EXPR) ...))
(defmacro cond clauses
    (if (null? clauses)
        ()
        (let* ((clause (car clauses))
               (the-cond (car clause)))
              (if (= the-cond T)
                  (cadr clause)
                  `(if ,the-cond
                       ,(cadr clause)
                       (cond ,@(cdr clauses)))))))

;; and macro
;;
;; (and EXPR ...)
(defmacro and args
    (cond ((null? args)       T)







|
<
<
<
<
<
<
<
<
<
<







<
<
<
<
<
<
<





|
|
|
<










|

|







9
10
11
12
13
14
15
16










17
18
19
20
21
22
23







24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------

;;; This zettel contains sxn definitions that are independent of specific
;;; subsystems, such as WebUI, API, or other. It just contains generic code to
;;; be used in all places. It asumes that the symbols NIL and T are defined.











;; not macro
(defmacro not (x) `(if ,x NIL T))

;; not= macro, to negate an equivalence
(defmacro not= args `(not (= ,@args)))








;; let* macro
;;
;; (let* (BINDING ...) EXPR ...), where SYMBOL may occur in later bindings.
(defmacro let* (bindings . body)
    (if (null? bindings)
        `(begin ,@body)
        `(let ((,(caar bindings) ,(cadar bindings)))
               (let* ,(cdr bindings) ,@body))))


;; cond macro
;;
;; (cond ((COND EXPR) ...))
(defmacro cond clauses
    (if (null? clauses)
        ()
        (let* ((clause (car clauses))
               (the-cond (car clause)))
              (if (= the-cond T)
                  `(begin ,@(cdr clause))
                  `(if ,the-cond
                       (begin ,@(cdr clause))
                       (cond ,@(cdr clauses)))))))

;; and macro
;;
;; (and EXPR ...)
(defmacro and args
    (cond ((null? args)       T)

Changes to box/constbox/wuicode.sxn.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------

;; Contains WebUI specific code, but not related to a specific template.

;; wui-list-item returns the argument as a HTML list item.
(defunconst wui-item (s) `(li ,s))

;; wui-info-meta-table-row takes a pair and translates it into a HTML table row
;; with two columns.
(defunconst wui-info-meta-table-row (p)
    `(tr (td (@ (class zs-info-meta-key)) ,(car p)) (td (@ (class zs-info-meta-value)) ,(cdr p))))

;; wui-valid-link translates a local link into a HTML link. A link is a pair
;; (valid . url). If valid is not truish, only the invalid url is returned.
(defunconst wui-valid-link (l)
    (if (car l)
        `(li (a (@ (href ,(cdr l))) ,(cdr l)))
        `(li ,(cdr l))))

;; wui-link takes a link (title . url) and returns a HTML reference.
(defunconst wui-link (q)
    `(a (@ (href ,(cdr q))) ,(car q)))

;; wui-item-link taks a pair (text . url) and returns a HTML link inside
;; a list item.
(defunconst wui-item-link (q) `(li ,(wui-link q)))

;; wui-tdata-link taks a pair (text . url) and returns a HTML link inside
;; a table data item.
(defunconst wui-tdata-link (q) `(td ,(wui-link q)))

;; wui-item-popup-link is like 'wui-item-link, but the HTML link will open
;; a new tab / window.
(defunconst wui-item-popup-link (e)
    `(li (a (@ (href ,e) (target "_blank") (rel "noopener noreferrer")) ,e)))

;; wui-option-value returns a value for an HTML option element.
(defunconst wui-option-value (v) `(option (@ (value ,v))))

;; wui-datalist returns a HTML datalist with the given HTML identifier and a
;; list of values.
(defunconst wui-datalist (id lst)
    (if lst
        `((datalist (@ (id ,id)) ,@(map wui-option-value lst)))))

;; wui-pair-desc-item takes a pair '(term . text) and returns a list with
;; a HTML description term and a HTML description data. 
(defunconst wui-pair-desc-item (p) `((dt ,(car p)) (dd ,(cdr p))))

;; wui-meta-desc returns a HTML description list made from the list of pairs
;; given.
(defunconst wui-meta-desc (l)
    `(dl ,@(apply append (map wui-pair-desc-item l))))

;; wui-enc-matrix returns the HTML table of all encodings and parts.
(defunconst wui-enc-matrix (matrix)
    `(table
      ,@(map
         (lambda (row) `(tr (th ,(car row)) ,@(map wui-tdata-link (cdr row))))
         matrix)))

;; CSS-ROLE-map is a mapping (pair list, assoc list) of role names to zettel
;; identifier. It is used in the base template to update the metadata of the







|



|




|





|




|



|



|



|



|





|



|



|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------

;; Contains WebUI specific code, but not related to a specific template.

;; wui-list-item returns the argument as a HTML list item.
(defun wui-item (s) `(li ,s))

;; wui-info-meta-table-row takes a pair and translates it into a HTML table row
;; with two columns.
(defun wui-info-meta-table-row (p)
    `(tr (td (@ (class zs-info-meta-key)) ,(car p)) (td (@ (class zs-info-meta-value)) ,(cdr p))))

;; wui-valid-link translates a local link into a HTML link. A link is a pair
;; (valid . url). If valid is not truish, only the invalid url is returned.
(defun wui-valid-link (l)
    (if (car l)
        `(li (a (@ (href ,(cdr l))) ,(cdr l)))
        `(li ,(cdr l))))

;; wui-link takes a link (title . url) and returns a HTML reference.
(defun wui-link (q)
    `(a (@ (href ,(cdr q))) ,(car q)))

;; wui-item-link taks a pair (text . url) and returns a HTML link inside
;; a list item.
(defun wui-item-link (q) `(li ,(wui-link q)))

;; wui-tdata-link taks a pair (text . url) and returns a HTML link inside
;; a table data item.
(defun wui-tdata-link (q) `(td ,(wui-link q)))

;; wui-item-popup-link is like 'wui-item-link, but the HTML link will open
;; a new tab / window.
(defun wui-item-popup-link (e)
    `(li (a (@ (href ,e) (target "_blank") (rel "noopener noreferrer")) ,e)))

;; wui-option-value returns a value for an HTML option element.
(defun wui-option-value (v) `(option (@ (value ,v))))

;; wui-datalist returns a HTML datalist with the given HTML identifier and a
;; list of values.
(defun wui-datalist (id lst)
    (if lst
        `((datalist (@ (id ,id)) ,@(map wui-option-value lst)))))

;; wui-pair-desc-item takes a pair '(term . text) and returns a list with
;; a HTML description term and a HTML description data. 
(defun wui-pair-desc-item (p) `((dt ,(car p)) (dd ,(cdr p))))

;; wui-meta-desc returns a HTML description list made from the list of pairs
;; given.
(defun wui-meta-desc (l)
    `(dl ,@(apply append (map wui-pair-desc-item l))))

;; wui-enc-matrix returns the HTML table of all encodings and parts.
(defun wui-enc-matrix (matrix)
    `(table
      ,@(map
         (lambda (row) `(tr (th ,(car row)) ,@(map wui-tdata-link (cdr row))))
         matrix)))

;; CSS-ROLE-map is a mapping (pair list, assoc list) of role names to zettel
;; identifier. It is used in the base template to update the metadata of the

Changes to box/dirbox/service.go.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/box/filebox"
	"zettelstore.de/z/box/notify"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"t73f.de/r/zsc/input"
	"zettelstore.de/z/box/filebox"
	"zettelstore.de/z/box/notify"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"

Changes to box/filebox/filebox.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import (
	"errors"
	"net/url"
	"path/filepath"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/manager"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)








|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import (
	"errors"
	"net/url"
	"path/filepath"
	"strings"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/manager"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

Changes to box/filebox/zipbox.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import (
	"archive/zip"
	"context"
	"fmt"
	"io"
	"strings"

	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/notify"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import (
	"archive/zip"
	"context"
	"fmt"
	"io"
	"strings"

	"t73f.de/r/zsc/input"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/notify"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"

Changes to box/manager/enrich.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package manager

import (
	"context"
	"strconv"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// Enrich computes additional properties and updates the given metadata.
func (mgr *Manager) Enrich(ctx context.Context, m *meta.Meta, boxNumber int) {







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package manager

import (
	"context"
	"strconv"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// Enrich computes additional properties and updates the given metadata.
func (mgr *Manager) Enrich(ctx context.Context, m *meta.Meta, boxNumber int) {

Changes to box/manager/mapstore/mapstore.go.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
	"context"
	"fmt"
	"io"
	"sort"
	"strings"
	"sync"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/maps"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/manager/store"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

type zettelData struct {







|
|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
	"context"
	"fmt"
	"io"
	"sort"
	"strings"
	"sync"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/maps"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/manager/store"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

type zettelData struct {

Changes to box/notify/entry.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package notify

import (
	"path/filepath"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

const (







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package notify

import (
	"path/filepath"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

const (

Changes to cmd/cmd_file.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import (
	"context"
	"flag"
	"fmt"
	"io"
	"os"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)








|
|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import (
	"context"
	"flag"
	"fmt"
	"io"
	"os"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

Changes to cmd/cmd_password.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import (
	"flag"
	"fmt"
	"os"

	"golang.org/x/term"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/auth/cred"
	"zettelstore.de/z/zettel/id"
)

// ---------- Subcommand: password -------------------------------------------

func cmdPassword(fs *flag.FlagSet) (int, error) {







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import (
	"flag"
	"fmt"
	"os"

	"golang.org/x/term"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth/cred"
	"zettelstore.de/z/zettel/id"
)

// ---------- Subcommand: password -------------------------------------------

func cmdPassword(fs *flag.FlagSet) (int, error) {

Changes to cmd/command.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package cmd

import (
	"flag"

	"zettelstore.de/client.fossil/maps"
	"zettelstore.de/z/logger"
)

// Command stores information about commands / sub-commands.
type Command struct {
	Name       string              // command name as it appears on the command line
	Func       CommandFunc         // function that executes a command







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package cmd

import (
	"flag"

	"t73f.de/r/zsc/maps"
	"zettelstore.de/z/logger"
)

// Command stores information about commands / sub-commands.
type Command struct {
	Name       string              // command name as it appears on the command line
	Func       CommandFunc         // function that executes a command

Changes to cmd/main.go.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
	"net/url"
	"os"
	"runtime/debug"
	"strconv"
	"strings"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/auth/impl"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/compbox"
	"zettelstore.de/z/box/manager"
	"zettelstore.de/z/config"
	"zettelstore.de/z/kernel"







|
|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
	"net/url"
	"os"
	"runtime/debug"
	"strconv"
	"strings"
	"time"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/auth/impl"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/compbox"
	"zettelstore.de/z/box/manager"
	"zettelstore.de/z/config"
	"zettelstore.de/z/kernel"

Changes to docs/manual/00001005090000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

19
20
21
22
23
24
25
id: 00001005090000
title: List of predefined zettel
role: manual
tags: #manual #reference #zettelstore
syntax: zmk
created: 20210126175322
modified: 20231129173425

The following table lists all predefined zettel with their purpose.

|= Identifier :|= Title | Purpose
| [[00000000000001]] | Zettelstore Version | Contains the version string of the running Zettelstore
| [[00000000000002]] | Zettelstore Host | Contains the name of the computer running the Zettelstore
| [[00000000000003]] | Zettelstore Operating System | Contains the operating system and CPU architecture of the computer running the Zettelstore
| [[00000000000004]] | Zettelstore License | Lists the license of Zettelstore
| [[00000000000005]] | Zettelstore Contributors | Lists all contributors of Zettelstore
| [[00000000000006]] | Zettelstore Dependencies | Lists all licensed content
| [[00000000000007]] | Zettelstore Log | Lists the last 8192 log messages

| [[00000000000020]] | Zettelstore Box Manager | Contains some statistics about zettel boxes and the the index process
| [[00000000000090]] | Zettelstore Supported Metadata Keys | Contains all supported metadata keys, their [[types|00001006030000]], and more
| [[00000000000092]] | Zettelstore Supported Parser | Lists all supported values for metadata [[syntax|00001006020000#syntax]] that are recognized by Zettelstore
| [[00000000000096]] | Zettelstore Startup Configuration | Contains the effective values of the [[startup configuration|00001004010000]]
| [[00000000000100]] | Zettelstore Runtime Configuration | Allows to [[configure Zettelstore at runtime|00001004020000]]
| [[00000000010100]] | Zettelstore Base HTML Template | Contains the general layout of the HTML view
| [[00000000010200]] | Zettelstore Login Form HTML Template | Layout of the login form, when authentication is [[enabled|00001010040100]]






|











>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
id: 00001005090000
title: List of predefined zettel
role: manual
tags: #manual #reference #zettelstore
syntax: zmk
created: 20210126175322
modified: 20240318115839

The following table lists all predefined zettel with their purpose.

|= Identifier :|= Title | Purpose
| [[00000000000001]] | Zettelstore Version | Contains the version string of the running Zettelstore
| [[00000000000002]] | Zettelstore Host | Contains the name of the computer running the Zettelstore
| [[00000000000003]] | Zettelstore Operating System | Contains the operating system and CPU architecture of the computer running the Zettelstore
| [[00000000000004]] | Zettelstore License | Lists the license of Zettelstore
| [[00000000000005]] | Zettelstore Contributors | Lists all contributors of Zettelstore
| [[00000000000006]] | Zettelstore Dependencies | Lists all licensed content
| [[00000000000007]] | Zettelstore Log | Lists the last 8192 log messages
| [[00000000000008]] | Zettelstore Memory | Some statistics about main memory usage
| [[00000000000020]] | Zettelstore Box Manager | Contains some statistics about zettel boxes and the the index process
| [[00000000000090]] | Zettelstore Supported Metadata Keys | Contains all supported metadata keys, their [[types|00001006030000]], and more
| [[00000000000092]] | Zettelstore Supported Parser | Lists all supported values for metadata [[syntax|00001006020000#syntax]] that are recognized by Zettelstore
| [[00000000000096]] | Zettelstore Startup Configuration | Contains the effective values of the [[startup configuration|00001004010000]]
| [[00000000000100]] | Zettelstore Runtime Configuration | Allows to [[configure Zettelstore at runtime|00001004020000]]
| [[00000000010100]] | Zettelstore Base HTML Template | Contains the general layout of the HTML view
| [[00000000010200]] | Zettelstore Login Form HTML Template | Layout of the login form, when authentication is [[enabled|00001010040100]]
43
44
45
46
47
48
49

50
51
| [[00000000090001]] | New Zettel | Template for a new zettel with role ""[[zettel|00001006020100#zettel]]""
| [[00000000090002]] | New User | Template for a new [[user zettel|00001010040200]]
| [[00000000090003]] | New Tag | Template for a new [[tag zettel|00001006020100#tag]]
| [[00000000090004]] | New Role | Template for a new [[role zettel|00001006020100#role]]
| [[00010000000000]] | Home | Default home zettel, contains some welcome information

If a zettel is not linked, it is not accessible for the current user.


**Important:** All identifier may change until a stable version of the software is released.







>


44
45
46
47
48
49
50
51
52
53
| [[00000000090001]] | New Zettel | Template for a new zettel with role ""[[zettel|00001006020100#zettel]]""
| [[00000000090002]] | New User | Template for a new [[user zettel|00001010040200]]
| [[00000000090003]] | New Tag | Template for a new [[tag zettel|00001006020100#tag]]
| [[00000000090004]] | New Role | Template for a new [[role zettel|00001006020100#role]]
| [[00010000000000]] | Home | Default home zettel, contains some welcome information

If a zettel is not linked, it is not accessible for the current user.
In most cases, you must at least enable [[''expert-mode''|00001004020000#expert-mode]].

**Important:** All identifier may change until a stable version of the software is released.

Changes to docs/manual/00001008000000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
id: 00001008000000
title: Other Markup Languages
role: manual
tags: #manual #zettelstore
syntax: zmk
created: 20210126175300
modified: 20230529223634

[[Zettelmarkup|00001007000000]] is not the only markup language you can use to define your content.
Zettelstore is quite agnostic with respect to markup languages.
Of course, Zettelmarkup plays an important role.
However, with the exception of zettel titles, you can use any (markup) language that is supported:

* CSS






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
id: 00001008000000
title: Other Markup Languages
role: manual
tags: #manual #zettelstore
syntax: zmk
created: 20210126175300
modified: 20240413160242

[[Zettelmarkup|00001007000000]] is not the only markup language you can use to define your content.
Zettelstore is quite agnostic with respect to markup languages.
Of course, Zettelmarkup plays an important role.
However, with the exception of zettel titles, you can use any (markup) language that is supported:

* CSS
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
: Only the metadata of a zettel is ""parsed"".
  Useful for displaying the full metadata.
  The [[runtime configuration zettel|00000000000100]] uses this syntax.
  The zettel content is ignored.
; [!svg|''svg'']
: [[Scalable Vector Graphics|https://www.w3.org/TR/SVG2/]].
; [!sxn|''sxn'']
: S-Expressions, as implemented by [[sx|https://zettelstore.de/sx]].
  Often used to specify templates when rendering a zettel as HTML for the [[web user interface|00001014000000]] (with the help of sxhtml]).
; [!text|''text''], [!plain|''plain''], [!txt|''txt'']
: Plain text that must not be interpreted further.
; [!zmk|''zmk'']
: [[Zettelmarkup|00001007000000]].

The actual values are also listed in a zettel named [[Zettelstore Supported Parser|00000000000092]].

If you specify something else, your content will be interpreted as plain text.







|









45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
: Only the metadata of a zettel is ""parsed"".
  Useful for displaying the full metadata.
  The [[runtime configuration zettel|00000000000100]] uses this syntax.
  The zettel content is ignored.
; [!svg|''svg'']
: [[Scalable Vector Graphics|https://www.w3.org/TR/SVG2/]].
; [!sxn|''sxn'']
: S-Expressions, as implemented by [[Sx|https://t73f.de/r/sx]].
  Often used to specify templates when rendering a zettel as HTML for the [[web user interface|00001014000000]] (with the help of sxhtml]).
; [!text|''text''], [!plain|''plain''], [!txt|''txt'']
: Plain text that must not be interpreted further.
; [!zmk|''zmk'']
: [[Zettelmarkup|00001007000000]].

The actual values are also listed in a zettel named [[Zettelstore Supported Parser|00000000000092]].

If you specify something else, your content will be interpreted as plain text.

Changes to docs/manual/00001012930500.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
id: 00001012930500
title: Syntax of Symbolic Expressions
role: manual
tags: #manual #reference #zettelstore
syntax: zmk
created: 20230403151127
modified: 20230703174218

=== Syntax of lists
A list always starts with the left parenthesis (""''(''"", U+0028) and ends with a right parenthesis (""'')''"", U+0029).
A list may contain a possibly empty sequence of elements, i.e. lists and / or atoms.

Internally, lists are composed of __cells__.
A cell allows to store two values.






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
id: 00001012930500
title: Syntax of Symbolic Expressions
role: manual
tags: #manual #reference #zettelstore
syntax: zmk
created: 20230403151127
modified: 20240413160345

=== Syntax of lists
A list always starts with the left parenthesis (""''(''"", U+0028) and ends with a right parenthesis (""'')''"", U+0029).
A list may contain a possibly empty sequence of elements, i.e. lists and / or atoms.

Internally, lists are composed of __cells__.
A cell allows to store two values.
67
68
69
70
71
72
73
74
75
76
77
To allow a string to contain a backslash, it also must be prefixed by one backslash.
Unicode characters with a code less than U+FF are encoded by by the sequence ""''\\xNM''"", where ''NM'' is the hex encoding of the character.
Unicode characters with a code less than U+FFFF are encoded by by the sequence ""''\\uNMOP''"", where ''NMOP'' is the hex encoding of the character.
Unicode characters with a code less than U+FFFFFF are encoded by by the sequence ""''\\UNMOPQR''"", where ''NMOPQR'' is the hex encoding of the character.
In addition, the sequence ""''\\t''"" encodes a horizontal tab (U+0009), the sequence ""''\\n''"" encodes a line feed (U+000A).

=== See also
* Currently, Zettelstore uses [[sx|https://zettelstore.de/sx]] (""Symbolic eXPression Framework"") to implement symbolic expression.
  The project page might contain additional information about the full syntax.

  Zettelstore only uses lists, numbers, string, and symbols to represent zettel.







|



67
68
69
70
71
72
73
74
75
76
77
To allow a string to contain a backslash, it also must be prefixed by one backslash.
Unicode characters with a code less than U+FF are encoded by by the sequence ""''\\xNM''"", where ''NM'' is the hex encoding of the character.
Unicode characters with a code less than U+FFFF are encoded by by the sequence ""''\\uNMOP''"", where ''NMOP'' is the hex encoding of the character.
Unicode characters with a code less than U+FFFFFF are encoded by by the sequence ""''\\UNMOPQR''"", where ''NMOPQR'' is the hex encoding of the character.
In addition, the sequence ""''\\t''"" encodes a horizontal tab (U+0009), the sequence ""''\\n''"" encodes a line feed (U+000A).

=== See also
* Currently, Zettelstore uses [[Sx|https://t73f.de/r/sx]] (""Symbolic eXpression framework"") to implement symbolic expressions.
  The project page might contain additional information about the full syntax.

  Zettelstore only uses lists, numbers, string, and symbols to represent zettel.

Changes to encoder/encoder.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package encoder

import (
	"errors"
	"fmt"
	"io"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/zettel/meta"
)

// Encoder is an interface that allows to encode different parts of a zettel.
type Encoder interface {
	WriteZettel(io.Writer, *ast.ZettelNode, EvalMetaFunc) (int, error)







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package encoder

import (
	"errors"
	"fmt"
	"io"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/zettel/meta"
)

// Encoder is an interface that allows to encode different parts of a zettel.
type Encoder interface {
	WriteZettel(io.Writer, *ast.ZettelNode, EvalMetaFunc) (int, error)

Changes to encoder/encoder_blob_test.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//-----------------------------------------------------------------------------

package encoder_test

import (
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"

	_ "zettelstore.de/z/parser/blob" // Allow to use BLOB parser.
)







|
|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//-----------------------------------------------------------------------------

package encoder_test

import (
	"testing"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"

	_ "zettelstore.de/z/parser/blob" // Allow to use BLOB parser.
)

Changes to encoder/encoder_test.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package encoder_test

import (
	"fmt"
	"strings"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/sx.fossil/sxreader"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"

	_ "zettelstore.de/z/encoder/htmlenc"  // Allow to use HTML encoder.







|
|
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package encoder_test

import (
	"fmt"
	"strings"
	"testing"

	"t73f.de/r/sx/sxreader"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"

	_ "zettelstore.de/z/encoder/htmlenc"  // Allow to use HTML encoder.

Changes to encoder/htmlenc/htmlenc.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Package htmlenc encodes the abstract syntax tree into HTML5 via zettelstore-client.
package htmlenc

import (
	"io"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/shtml"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/sx.fossil/sxhtml"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/encoder/szenc"
	"zettelstore.de/z/encoder/textenc"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)







|
|
|
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Package htmlenc encodes the abstract syntax tree into HTML5 via zettelstore-client.
package htmlenc

import (
	"io"
	"strings"

	"t73f.de/r/sx"
	"t73f.de/r/sxwebs/sxhtml"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/shtml"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/encoder/szenc"
	"zettelstore.de/z/encoder/textenc"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
	if err != nil {
		return 0, err
	}
	hen := he.th.Endnotes(&env)

	var head sx.ListBuilder
	head.Add(shtml.SymHead)
	head.Add(sx.Nil().Cons(sx.Nil().Cons(sx.Cons(sx.MakeSymbol("charset"), sx.String("utf-8"))).Cons(sxhtml.SymAttr)).Cons(shtml.SymMeta))
	head.ExtendBang(hm)
	var sb strings.Builder
	if hasTitle {
		he.textEnc.WriteInlines(&sb, &isTitle)
	} else {
		sb.Write(zn.Meta.Zid.Bytes())
	}
	head.Add(sx.MakeList(shtml.SymAttrTitle, sx.String(sb.String())))

	var body sx.ListBuilder
	body.Add(shtml.SymBody)
	if hasTitle {
		body.Add(htitle.Cons(shtml.SymH1))
	}
	body.ExtendBang(hast)
	if hen != nil {
		body.Add(sx.Cons(shtml.SymHR, nil))
		body.Add(hen)
	}

	doc := sx.MakeList(
		sxhtml.SymDoctype,
		sx.MakeList(shtml.SymHtml, head.List(), body.List()),
	)

	gen := sxhtml.NewGenerator(sxhtml.WithNewline)
	return gen.WriteHTML(w, doc)
}

// WriteMeta encodes meta data as HTML5.
func (he *Encoder) WriteMeta(w io.Writer, m *meta.Meta, evalMeta encoder.EvalMetaFunc) (int, error) {
	env := shtml.MakeEnvironment(he.lang)
	hm, err := he.th.Evaluate(he.tx.GetMeta(m, evalMeta), &env)
	if err != nil {
		return 0, err
	}
	gen := sxhtml.NewGenerator(sxhtml.WithNewline)
	return gen.WriteListHTML(w, hm)
}

func (he *Encoder) WriteContent(w io.Writer, zn *ast.ZettelNode) (int, error) {
	return he.WriteBlocks(w, &zn.Ast)
}








|







|

















|










|







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
	if err != nil {
		return 0, err
	}
	hen := he.th.Endnotes(&env)

	var head sx.ListBuilder
	head.Add(shtml.SymHead)
	head.Add(sx.Nil().Cons(sx.Nil().Cons(sx.Cons(sx.MakeSymbol("charset"), sx.MakeString("utf-8"))).Cons(sxhtml.SymAttr)).Cons(shtml.SymMeta))
	head.ExtendBang(hm)
	var sb strings.Builder
	if hasTitle {
		he.textEnc.WriteInlines(&sb, &isTitle)
	} else {
		sb.Write(zn.Meta.Zid.Bytes())
	}
	head.Add(sx.MakeList(shtml.SymAttrTitle, sx.MakeString(sb.String())))

	var body sx.ListBuilder
	body.Add(shtml.SymBody)
	if hasTitle {
		body.Add(htitle.Cons(shtml.SymH1))
	}
	body.ExtendBang(hast)
	if hen != nil {
		body.Add(sx.Cons(shtml.SymHR, nil))
		body.Add(hen)
	}

	doc := sx.MakeList(
		sxhtml.SymDoctype,
		sx.MakeList(shtml.SymHtml, head.List(), body.List()),
	)

	gen := sxhtml.NewGenerator().SetNewline()
	return gen.WriteHTML(w, doc)
}

// WriteMeta encodes meta data as HTML5.
func (he *Encoder) WriteMeta(w io.Writer, m *meta.Meta, evalMeta encoder.EvalMetaFunc) (int, error) {
	env := shtml.MakeEnvironment(he.lang)
	hm, err := he.th.Evaluate(he.tx.GetMeta(m, evalMeta), &env)
	if err != nil {
		return 0, err
	}
	gen := sxhtml.NewGenerator().SetNewline()
	return gen.WriteListHTML(w, hm)
}

func (he *Encoder) WriteContent(w io.Writer, zn *ast.ZettelNode) (int, error) {
	return he.WriteBlocks(w, &zn.Ast)
}

Changes to encoder/mdenc/mdenc.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// Package mdenc encodes the abstract syntax tree back into Markdown.
package mdenc

import (
	"io"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	encoder.Register(api.EncoderMD, func(*encoder.CreateParameter) encoder.Encoder { return Create() })







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// Package mdenc encodes the abstract syntax tree back into Markdown.
package mdenc

import (
	"io"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	encoder.Register(api.EncoderMD, func(*encoder.CreateParameter) encoder.Encoder { return Create() })

Changes to encoder/shtmlenc/shtmlenc.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

// Package shtmlenc encodes the abstract syntax tree into a s-expr which represents HTML.
package shtmlenc

import (
	"io"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/shtml"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/encoder/szenc"
	"zettelstore.de/z/zettel/meta"
)

func init() {







|
|
|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

// Package shtmlenc encodes the abstract syntax tree into a s-expr which represents HTML.
package shtmlenc

import (
	"io"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/shtml"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/encoder/szenc"
	"zettelstore.de/z/zettel/meta"
)

func init() {

Changes to encoder/szenc/szenc.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// Package szenc encodes the abstract syntax tree into a s-expr for zettel.
package szenc

import (
	"io"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	encoder.Register(api.EncoderSz, func(*encoder.CreateParameter) encoder.Encoder { return Create() })







|
|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// Package szenc encodes the abstract syntax tree into a s-expr for zettel.
package szenc

import (
	"io"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	encoder.Register(api.EncoderSz, func(*encoder.CreateParameter) encoder.Encoder { return Create() })

Changes to encoder/szenc/transform.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package szenc

import (
	"encoding/base64"
	"fmt"
	"strings"

	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/client.fossil/sz"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/zettel/meta"
)

// NewTransformer returns a new transformer to create s-expressions from AST nodes.
func NewTransformer() *Transformer {







|
|
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package szenc

import (
	"encoding/base64"
	"fmt"
	"strings"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/attrs"
	"t73f.de/r/zsc/sz"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/zettel/meta"
)

// NewTransformer returns a new transformer to create s-expressions from AST nodes.
func NewTransformer() *Transformer {
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
		return t.getInlineList(*n).Cons(sz.SymInline)
	case *ast.ParaNode:
		return t.getInlineList(n.Inlines).Cons(sz.SymPara)
	case *ast.VerbatimNode:
		return sx.MakeList(
			mapGetS(mapVerbatimKindS, n.Kind),
			getAttributes(n.Attrs),
			sx.String(string(n.Content)),
		)
	case *ast.RegionNode:
		return t.getRegion(n)
	case *ast.HeadingNode:
		return t.getInlineList(n.Inlines).
			Cons(sx.String(n.Fragment)).
			Cons(sx.String(n.Slug)).
			Cons(getAttributes(n.Attrs)).
			Cons(sx.Int64(int64(n.Level))).
			Cons(sz.SymHeading)
	case *ast.HRuleNode:
		return sx.MakeList(sz.SymThematic, getAttributes(n.Attrs))
	case *ast.NestedListNode:
		return t.getNestedList(n)
	case *ast.DescriptionListNode:
		return t.getDescriptionList(n)
	case *ast.TableNode:
		return t.getTable(n)
	case *ast.TranscludeNode:
		return sx.MakeList(sz.SymTransclude, getAttributes(n.Attrs), getReference(n.Ref))
	case *ast.BLOBNode:
		return t.getBLOB(n)
	case *ast.TextNode:
		return sx.MakeList(sz.SymText, sx.String(n.Text))
	case *ast.SpaceNode:
		if t.inVerse {
			return sx.MakeList(sz.SymSpace, sx.String(n.Lexeme))
		}
		return sx.MakeList(sz.SymSpace)
	case *ast.BreakNode:
		if n.Hard {
			return sx.MakeList(sz.SymHard)
		}
		return sx.MakeList(sz.SymSoft)
	case *ast.LinkNode:
		return t.getLink(n)
	case *ast.EmbedRefNode:
		return t.getInlineList(n.Inlines).
			Cons(sx.String(n.Syntax)).
			Cons(getReference(n.Ref)).
			Cons(getAttributes(n.Attrs)).
			Cons(sz.SymEmbed)
	case *ast.EmbedBLOBNode:
		return t.getEmbedBLOB(n)
	case *ast.CiteNode:
		return t.getInlineList(n.Inlines).
			Cons(sx.String(n.Key)).
			Cons(getAttributes(n.Attrs)).
			Cons(sz.SymCite)
	case *ast.FootnoteNode:
		// (ENDNODE attrs InlineElement ...)
		return t.getInlineList(n.Inlines).Cons(getAttributes(n.Attrs)).Cons(sz.SymEndnote)
	case *ast.MarkNode:
		return t.getInlineList(n.Inlines).
			Cons(sx.String(n.Fragment)).
			Cons(sx.String(n.Slug)).
			Cons(sx.String(n.Mark)).
			Cons(sz.SymMark)
	case *ast.FormatNode:
		return t.getInlineList(n.Inlines).
			Cons(getAttributes(n.Attrs)).
			Cons(mapGetS(mapFormatKindS, n.Kind))
	case *ast.LiteralNode:
		return sx.MakeList(
			mapGetS(mapLiteralKindS, n.Kind),
			getAttributes(n.Attrs),
			sx.String(string(n.Content)),
		)
	}
	return sx.MakeList(sz.SymUnknown, sx.String(fmt.Sprintf("%T %v", node, node)))
}

var mapVerbatimKindS = map[ast.VerbatimKind]*sx.Symbol{
	ast.VerbatimZettel:  sz.SymVerbatimZettel,
	ast.VerbatimProg:    sz.SymVerbatimProg,
	ast.VerbatimEval:    sz.SymVerbatimEval,
	ast.VerbatimMath:    sz.SymVerbatimMath,







|





|
|
















|


|











|







|







|
|
|









|


|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
		return t.getInlineList(*n).Cons(sz.SymInline)
	case *ast.ParaNode:
		return t.getInlineList(n.Inlines).Cons(sz.SymPara)
	case *ast.VerbatimNode:
		return sx.MakeList(
			mapGetS(mapVerbatimKindS, n.Kind),
			getAttributes(n.Attrs),
			sx.MakeString(string(n.Content)),
		)
	case *ast.RegionNode:
		return t.getRegion(n)
	case *ast.HeadingNode:
		return t.getInlineList(n.Inlines).
			Cons(sx.MakeString(n.Fragment)).
			Cons(sx.MakeString(n.Slug)).
			Cons(getAttributes(n.Attrs)).
			Cons(sx.Int64(int64(n.Level))).
			Cons(sz.SymHeading)
	case *ast.HRuleNode:
		return sx.MakeList(sz.SymThematic, getAttributes(n.Attrs))
	case *ast.NestedListNode:
		return t.getNestedList(n)
	case *ast.DescriptionListNode:
		return t.getDescriptionList(n)
	case *ast.TableNode:
		return t.getTable(n)
	case *ast.TranscludeNode:
		return sx.MakeList(sz.SymTransclude, getAttributes(n.Attrs), getReference(n.Ref))
	case *ast.BLOBNode:
		return t.getBLOB(n)
	case *ast.TextNode:
		return sx.MakeList(sz.SymText, sx.MakeString(n.Text))
	case *ast.SpaceNode:
		if t.inVerse {
			return sx.MakeList(sz.SymSpace, sx.MakeString(n.Lexeme))
		}
		return sx.MakeList(sz.SymSpace)
	case *ast.BreakNode:
		if n.Hard {
			return sx.MakeList(sz.SymHard)
		}
		return sx.MakeList(sz.SymSoft)
	case *ast.LinkNode:
		return t.getLink(n)
	case *ast.EmbedRefNode:
		return t.getInlineList(n.Inlines).
			Cons(sx.MakeString(n.Syntax)).
			Cons(getReference(n.Ref)).
			Cons(getAttributes(n.Attrs)).
			Cons(sz.SymEmbed)
	case *ast.EmbedBLOBNode:
		return t.getEmbedBLOB(n)
	case *ast.CiteNode:
		return t.getInlineList(n.Inlines).
			Cons(sx.MakeString(n.Key)).
			Cons(getAttributes(n.Attrs)).
			Cons(sz.SymCite)
	case *ast.FootnoteNode:
		// (ENDNODE attrs InlineElement ...)
		return t.getInlineList(n.Inlines).Cons(getAttributes(n.Attrs)).Cons(sz.SymEndnote)
	case *ast.MarkNode:
		return t.getInlineList(n.Inlines).
			Cons(sx.MakeString(n.Fragment)).
			Cons(sx.MakeString(n.Slug)).
			Cons(sx.MakeString(n.Mark)).
			Cons(sz.SymMark)
	case *ast.FormatNode:
		return t.getInlineList(n.Inlines).
			Cons(getAttributes(n.Attrs)).
			Cons(mapGetS(mapFormatKindS, n.Kind))
	case *ast.LiteralNode:
		return sx.MakeList(
			mapGetS(mapLiteralKindS, n.Kind),
			getAttributes(n.Attrs),
			sx.MakeString(string(n.Content)),
		)
	}
	return sx.MakeList(sz.SymUnknown, sx.MakeString(fmt.Sprintf("%T %v", node, node)))
}

var mapVerbatimKindS = map[ast.VerbatimKind]*sx.Symbol{
	ast.VerbatimZettel:  sz.SymVerbatimZettel,
	ast.VerbatimProg:    sz.SymVerbatimProg,
	ast.VerbatimEval:    sz.SymVerbatimEval,
	ast.VerbatimMath:    sz.SymVerbatimMath,
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
func (t *Transformer) getCell(cell *ast.TableCell) *sx.Pair {
	return t.getInlineList(cell.Inlines).Cons(mapGetS(alignmentSymbolS, cell.Align))
}

func (t *Transformer) getBLOB(bn *ast.BLOBNode) *sx.Pair {
	var lastObj sx.Object
	if bn.Syntax == meta.SyntaxSVG {
		lastObj = sx.String(string(bn.Blob))
	} else {
		lastObj = getBase64String(bn.Blob)
	}
	return sx.MakeList(
		sz.SymBLOB,
		t.getInlineList(bn.Description),
		sx.String(bn.Syntax),
		lastObj,
	)
}

var mapRefStateLink = map[ast.RefState]*sx.Symbol{
	ast.RefStateInvalid:  sz.SymLinkInvalid,
	ast.RefStateZettel:   sz.SymLinkZettel,
	ast.RefStateSelf:     sz.SymLinkSelf,
	ast.RefStateFound:    sz.SymLinkFound,
	ast.RefStateBroken:   sz.SymLinkBroken,
	ast.RefStateHosted:   sz.SymLinkHosted,
	ast.RefStateBased:    sz.SymLinkBased,
	ast.RefStateQuery:    sz.SymLinkQuery,
	ast.RefStateExternal: sz.SymLinkExternal,
}

func (t *Transformer) getLink(ln *ast.LinkNode) *sx.Pair {
	return t.getInlineList(ln.Inlines).
		Cons(sx.String(ln.Ref.Value)).
		Cons(getAttributes(ln.Attrs)).
		Cons(mapGetS(mapRefStateLink, ln.Ref.State))
}

func (t *Transformer) getEmbedBLOB(en *ast.EmbedBLOBNode) *sx.Pair {
	tail := t.getInlineList(en.Inlines)
	if en.Syntax == meta.SyntaxSVG {
		tail = tail.Cons(sx.String(string(en.Blob)))
	} else {
		tail = tail.Cons(getBase64String(en.Blob))
	}
	return tail.Cons(sx.String(en.Syntax)).Cons(getAttributes(en.Attrs)).Cons(sz.SymEmbedBLOB)
}

func (t *Transformer) getBlockList(bs *ast.BlockSlice) *sx.Pair {
	objs := make(sx.Vector, len(*bs))
	for i, n := range *bs {
		objs[i] = t.GetSz(n)
	}







|






|


















|







|



|







264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
func (t *Transformer) getCell(cell *ast.TableCell) *sx.Pair {
	return t.getInlineList(cell.Inlines).Cons(mapGetS(alignmentSymbolS, cell.Align))
}

func (t *Transformer) getBLOB(bn *ast.BLOBNode) *sx.Pair {
	var lastObj sx.Object
	if bn.Syntax == meta.SyntaxSVG {
		lastObj = sx.MakeString(string(bn.Blob))
	} else {
		lastObj = getBase64String(bn.Blob)
	}
	return sx.MakeList(
		sz.SymBLOB,
		t.getInlineList(bn.Description),
		sx.MakeString(bn.Syntax),
		lastObj,
	)
}

var mapRefStateLink = map[ast.RefState]*sx.Symbol{
	ast.RefStateInvalid:  sz.SymLinkInvalid,
	ast.RefStateZettel:   sz.SymLinkZettel,
	ast.RefStateSelf:     sz.SymLinkSelf,
	ast.RefStateFound:    sz.SymLinkFound,
	ast.RefStateBroken:   sz.SymLinkBroken,
	ast.RefStateHosted:   sz.SymLinkHosted,
	ast.RefStateBased:    sz.SymLinkBased,
	ast.RefStateQuery:    sz.SymLinkQuery,
	ast.RefStateExternal: sz.SymLinkExternal,
}

func (t *Transformer) getLink(ln *ast.LinkNode) *sx.Pair {
	return t.getInlineList(ln.Inlines).
		Cons(sx.MakeString(ln.Ref.Value)).
		Cons(getAttributes(ln.Attrs)).
		Cons(mapGetS(mapRefStateLink, ln.Ref.State))
}

func (t *Transformer) getEmbedBLOB(en *ast.EmbedBLOBNode) *sx.Pair {
	tail := t.getInlineList(en.Inlines)
	if en.Syntax == meta.SyntaxSVG {
		tail = tail.Cons(sx.MakeString(string(en.Blob)))
	} else {
		tail = tail.Cons(getBase64String(en.Blob))
	}
	return tail.Cons(sx.MakeString(en.Syntax)).Cons(getAttributes(en.Attrs)).Cons(sz.SymEmbedBLOB)
}

func (t *Transformer) getBlockList(bs *ast.BlockSlice) *sx.Pair {
	objs := make(sx.Vector, len(*bs))
	for i, n := range *bs {
		objs[i] = t.GetSz(n)
	}
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
func getAttributes(a attrs.Attributes) sx.Object {
	if a.IsEmpty() {
		return sx.Nil()
	}
	keys := a.Keys()
	objs := make(sx.Vector, 0, len(keys))
	for _, k := range keys {
		objs = append(objs, sx.Cons(sx.String(k), sx.String(a[k])))
	}
	return sx.MakeList(objs...)
}

var mapRefStateS = map[ast.RefState]*sx.Symbol{
	ast.RefStateInvalid:  sz.SymRefStateInvalid,
	ast.RefStateZettel:   sz.SymRefStateZettel,
	ast.RefStateSelf:     sz.SymRefStateSelf,
	ast.RefStateFound:    sz.SymRefStateFound,
	ast.RefStateBroken:   sz.SymRefStateBroken,
	ast.RefStateHosted:   sz.SymRefStateHosted,
	ast.RefStateBased:    sz.SymRefStateBased,
	ast.RefStateQuery:    sz.SymRefStateQuery,
	ast.RefStateExternal: sz.SymRefStateExternal,
}

func getReference(ref *ast.Reference) *sx.Pair {
	return sx.MakeList(mapGetS(mapRefStateS, ref.State), sx.String(ref.Value))
}

var mapMetaTypeS = map[*meta.DescriptionType]*sx.Symbol{
	meta.TypeCredential:   sz.SymTypeCredential,
	meta.TypeEmpty:        sz.SymTypeEmpty,
	meta.TypeID:           sz.SymTypeID,
	meta.TypeIDSet:        sz.SymTypeIDSet,







|

















|







327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
func getAttributes(a attrs.Attributes) sx.Object {
	if a.IsEmpty() {
		return sx.Nil()
	}
	keys := a.Keys()
	objs := make(sx.Vector, 0, len(keys))
	for _, k := range keys {
		objs = append(objs, sx.Cons(sx.MakeString(k), sx.MakeString(a[k])))
	}
	return sx.MakeList(objs...)
}

var mapRefStateS = map[ast.RefState]*sx.Symbol{
	ast.RefStateInvalid:  sz.SymRefStateInvalid,
	ast.RefStateZettel:   sz.SymRefStateZettel,
	ast.RefStateSelf:     sz.SymRefStateSelf,
	ast.RefStateFound:    sz.SymRefStateFound,
	ast.RefStateBroken:   sz.SymRefStateBroken,
	ast.RefStateHosted:   sz.SymRefStateHosted,
	ast.RefStateBased:    sz.SymRefStateBased,
	ast.RefStateQuery:    sz.SymRefStateQuery,
	ast.RefStateExternal: sz.SymRefStateExternal,
}

func getReference(ref *ast.Reference) *sx.Pair {
	return sx.MakeList(mapGetS(mapRefStateS, ref.State), sx.MakeString(ref.Value))
}

var mapMetaTypeS = map[*meta.DescriptionType]*sx.Symbol{
	meta.TypeCredential:   sz.SymTypeCredential,
	meta.TypeEmpty:        sz.SymTypeEmpty,
	meta.TypeID:           sz.SymTypeID,
	meta.TypeIDSet:        sz.SymTypeIDSet,
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
		ty := m.Type(key)
		symType := mapGetS(mapMetaTypeS, ty)
		var obj sx.Object
		if ty.IsSet {
			setList := meta.ListFromValue(p.Value)
			setObjs := make(sx.Vector, len(setList))
			for i, val := range setList {
				setObjs[i] = sx.String(val)
			}
			obj = sx.MakeList(setObjs...)
		} else if ty == meta.TypeZettelmarkup {
			is := evalMeta(p.Value)
			obj = t.getInlineList(is)
		} else {
			obj = sx.String(p.Value)
		}
		objs = append(objs, sx.Nil().Cons(obj).Cons(sx.MakeSymbol(key)).Cons(symType))
	}
	return sx.MakeList(objs...).Cons(sz.SymMeta)
}

func mapGetS[T comparable](m map[T]*sx.Symbol, k T) *sx.Symbol {







|






|







374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
		ty := m.Type(key)
		symType := mapGetS(mapMetaTypeS, ty)
		var obj sx.Object
		if ty.IsSet {
			setList := meta.ListFromValue(p.Value)
			setObjs := make(sx.Vector, len(setList))
			for i, val := range setList {
				setObjs[i] = sx.MakeString(val)
			}
			obj = sx.MakeList(setObjs...)
		} else if ty == meta.TypeZettelmarkup {
			is := evalMeta(p.Value)
			obj = t.getInlineList(is)
		} else {
			obj = sx.MakeString(p.Value)
		}
		objs = append(objs, sx.Nil().Cons(obj).Cons(sx.MakeSymbol(key)).Cons(symType))
	}
	return sx.MakeList(objs...).Cons(sz.SymMeta)
}

func mapGetS[T comparable](m map[T]*sx.Symbol, k T) *sx.Symbol {
403
404
405
406
407
408
409
410
411
412
413
	var sb strings.Builder
	encoder := base64.NewEncoder(base64.StdEncoding, &sb)
	_, err := encoder.Write(data)
	if err == nil {
		err = encoder.Close()
	}
	if err == nil {
		return sx.String(sb.String())
	}
	return sx.String("")
}







|

|

403
404
405
406
407
408
409
410
411
412
413
	var sb strings.Builder
	encoder := base64.NewEncoder(base64.StdEncoding, &sb)
	_, err := encoder.Write(data)
	if err == nil {
		err = encoder.Close()
	}
	if err == nil {
		return sx.MakeString(sb.String())
	}
	return sx.MakeString("")
}

Changes to encoder/textenc/textenc.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// Package textenc encodes the abstract syntax tree into its text.
package textenc

import (
	"io"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	encoder.Register(api.EncoderText, func(*encoder.CreateParameter) encoder.Encoder { return Create() })







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// Package textenc encodes the abstract syntax tree into its text.
package textenc

import (
	"io"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	encoder.Register(api.EncoderText, func(*encoder.CreateParameter) encoder.Encoder { return Create() })

Changes to encoder/zmkenc/zmkenc.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package zmkenc

import (
	"fmt"
	"io"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/encoder/textenc"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/meta"
)








|
|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package zmkenc

import (
	"fmt"
	"io"
	"strings"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/attrs"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/encoder/textenc"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/meta"
)

Changes to encoding/atom/atom.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Package atom provides an Atom encoding.
package atom

import (
	"bytes"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoding"
	"zettelstore.de/z/encoding/xml"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/query"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/id"







|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Package atom provides an Atom encoding.
package atom

import (
	"bytes"
	"time"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoding"
	"zettelstore.de/z/encoding/xml"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/query"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/id"

Changes to encoding/encoding.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// Package encoding provides helper functions for encodings.
package encoding

import (
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// LastUpdated returns the formated time of the zettel which was updated at the latest time.
func LastUpdated(ml []*meta.Meta, timeFormat string) string {







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// Package encoding provides helper functions for encodings.
package encoding

import (
	"time"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// LastUpdated returns the formated time of the zettel which was updated at the latest time.
func LastUpdated(ml []*meta.Meta, timeFormat string) string {

Changes to encoding/rss/rss.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package rss

import (
	"bytes"
	"context"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoding"
	"zettelstore.de/z/encoding/xml"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/query"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/id"







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package rss

import (
	"bytes"
	"context"
	"time"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoding"
	"zettelstore.de/z/encoding/xml"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/query"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/id"

Changes to evaluator/evaluator.go.

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
	"context"
	"errors"
	"fmt"
	"path"
	"strconv"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/sx.fossil/sxbuiltins"
	"zettelstore.de/sx.fossil/sxreader"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/parser/cleaner"
	"zettelstore.de/z/parser/draw"
	"zettelstore.de/z/query"







|
|
|
|
|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
	"context"
	"errors"
	"fmt"
	"path"
	"strconv"
	"strings"

	"t73f.de/r/sx/sxbuiltins"
	"t73f.de/r/sx/sxreader"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/attrs"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/parser/cleaner"
	"zettelstore.de/z/parser/draw"
	"zettelstore.de/z/query"

Changes to evaluator/list.go.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
	"bytes"
	"context"
	"math"
	"sort"
	"strconv"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoding/atom"
	"zettelstore.de/z/encoding/rss"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel/meta"







|
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
	"bytes"
	"context"
	"math"
	"sort"
	"strconv"
	"strings"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/attrs"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoding/atom"
	"zettelstore.de/z/encoding/rss"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel/meta"

Changes to go.mod.

1
2
3
4
5
6
7
8
9
10
11

12
13
14

15


module zettelstore.de/z

go 1.22

require (
	github.com/fsnotify/fsnotify v1.7.0
	github.com/yuin/goldmark v1.7.0
	golang.org/x/crypto v0.20.0
	golang.org/x/term v0.17.0
	golang.org/x/text v0.14.0
	zettelstore.de/client.fossil v0.0.0-20240304164340-1f9d9b832cdd

	zettelstore.de/sx.fossil v0.0.0-20240304124557-67e0a1799d1d
)


require golang.org/x/sys v0.17.0 // indirect








|
|
|

|
>
|


>
|
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module zettelstore.de/z

go 1.22

require (
	github.com/fsnotify/fsnotify v1.7.0
	github.com/yuin/goldmark v1.7.1
	golang.org/x/crypto v0.22.0
	golang.org/x/term v0.19.0
	golang.org/x/text v0.14.0
	t73f.de/r/sx v0.0.0-20240418072254-b6eff7d787f9
	t73f.de/r/sxwebs v0.0.0-20240422143910-320427142398
	t73f.de/r/zsc v0.0.0-20240422145115-3bbfcfd9394c
)

require (
	golang.org/x/sys v0.19.0 // indirect
	t73f.de/r/webs v0.0.0-20240422103534-8f5067bc11bc // indirect
)

Changes to go.sum.

1
2
3
4
5
6
7
8
9
10
11
12
13
14


15
16


github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
zettelstore.de/client.fossil v0.0.0-20240304164340-1f9d9b832cdd h1:+LUJqi1mvXo/zM9Ii64hcGd1LD3oC8kh5yrmw2fFoco=
zettelstore.de/client.fossil v0.0.0-20240304164340-1f9d9b832cdd/go.mod h1:y5zhvVuDHJKFcySEe70537w+5RL50jpeZjqyQuBjfa0=


zettelstore.de/sx.fossil v0.0.0-20240304124557-67e0a1799d1d h1:Gl5ZmdNV5wJsNMIQYjAd/sWLq2ng4NP+eglWU7lQP+I=
zettelstore.de/sx.fossil v0.0.0-20240304124557-67e0a1799d1d/go.mod h1:/iGHxFXoo6GSV04PUkwaLuFrrCa5LMorxD73iLMAruI=




|
|
|
|
|
|
|
|


|
|
>
>
|
|
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
t73f.de/r/sx v0.0.0-20240418072254-b6eff7d787f9 h1:lVPkYN8+J9f6JA9SmoF6icvpLxz4u3h1MCTuDYJYwdU=
t73f.de/r/sx v0.0.0-20240418072254-b6eff7d787f9/go.mod h1:G9pD1j2R6y9ZkPBb81mSnmwaAvTOg7r6jKp/OF7WeFA=
t73f.de/r/sxwebs v0.0.0-20240422143910-320427142398 h1:/G054FNxS8zEYbdhOTNk+GhdhjWBVt398FTm1Ud4A4o=
t73f.de/r/sxwebs v0.0.0-20240422143910-320427142398/go.mod h1:PtIkpRfTTiQITciKaWcTiAwy9FJ63WSQKciTp/dJbOA=
t73f.de/r/webs v0.0.0-20240422103534-8f5067bc11bc h1:i6tm/AEJUs8J8m7iDP8bTZgM0wYERh97RR47soJglxs=
t73f.de/r/webs v0.0.0-20240422103534-8f5067bc11bc/go.mod h1:UGAAtul0TK5ACeZ6zTS3SX6GqwMFXxlUpHiV8oqNq5w=
t73f.de/r/zsc v0.0.0-20240422145115-3bbfcfd9394c h1:TOvMeqVXG5s1Rj2a2q3r1+K8LlS9fHruMon0n7Aw5qM=
t73f.de/r/zsc v0.0.0-20240422145115-3bbfcfd9394c/go.mod h1:YdsjqbI1th0bJoMclgbNycJUVr8ovAV02ZgA91uOhkU=

Changes to kernel/impl/cfg.go.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	"context"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/config"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	"context"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/config"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"

Changes to kernel/impl/cmd.go.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
	"io"
	"os"
	"runtime/metrics"
	"sort"
	"strconv"
	"strings"

	"zettelstore.de/client.fossil/maps"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/strfun"
)

type cmdSession struct {
	w        io.Writer







|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
	"io"
	"os"
	"runtime/metrics"
	"sort"
	"strconv"
	"strings"

	"t73f.de/r/zsc/maps"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/strfun"
)

type cmdSession struct {
	w        io.Writer

Changes to kernel/impl/config.go.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"sync"

	"zettelstore.de/client.fossil/maps"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/zettel/id"
)

type parseFunc func(string) (any, error)
type configDescription struct {







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"sync"

	"t73f.de/r/zsc/maps"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/zettel/id"
)

type parseFunc func(string) (any, error)
type configDescription struct {

Changes to kernel/impl/core.go.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	"fmt"
	"net"
	"os"
	"runtime"
	"sync"
	"time"

	"zettelstore.de/client.fossil/maps"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/id"
)

type coreService struct {







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	"fmt"
	"net"
	"os"
	"runtime"
	"sync"
	"time"

	"t73f.de/r/zsc/maps"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/id"
)

type coreService struct {

Changes to logger/message.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"context"
	"net/http"
	"strconv"
	"sync"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/zettel/id"
)

// Message presents a message to log.
type Message struct {
	logger *Logger
	level  Level







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"context"
	"net/http"
	"strconv"
	"sync"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/zettel/id"
)

// Message presents a message to log.
type Message struct {
	logger *Logger
	level  Level

Changes to parser/blob/blob.go.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

// Package blob provides a parser of binary data.
package blob

import (
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	parser.Register(&parser.Info{







|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

// Package blob provides a parser of binary data.
package blob

import (
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	parser.Register(&parser.Info{

Changes to parser/draw/draw.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// It is not a parser registered by the general parser framework (directed by
// metadata "syntax" of a zettel). It will be used when a zettel is evaluated.
package draw

import (
	"strconv"

	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	parser.Register(&parser.Info{







|
|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// It is not a parser registered by the general parser framework (directed by
// metadata "syntax" of a zettel). It will be used when a zettel is evaluated.
package draw

import (
	"strconv"

	"t73f.de/r/zsc/attrs"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	parser.Register(&parser.Info{

Changes to parser/draw/draw_test.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//-----------------------------------------------------------------------------

package draw_test

import (
	"testing"

	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func FuzzParseBlocks(f *testing.F) {
	f.Fuzz(func(t *testing.T, src []byte) {
		t.Parallel()
		inp := input.NewInput(src)
		parser.ParseBlocks(inp, nil, meta.SyntaxDraw, config.NoHTML)
	})
}







|












12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//-----------------------------------------------------------------------------

package draw_test

import (
	"testing"

	"t73f.de/r/zsc/input"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func FuzzParseBlocks(f *testing.F) {
	f.Fuzz(func(t *testing.T, src []byte) {
		t.Parallel()
		inp := input.NewInput(src)
		parser.ParseBlocks(inp, nil, meta.SyntaxDraw, config.NoHTML)
	})
}

Changes to parser/markdown/markdown.go.

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
	"strconv"
	"strings"

	gm "github.com/yuin/goldmark"
	gmAst "github.com/yuin/goldmark/ast"
	gmText "github.com/yuin/goldmark/text"

	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder/textenc"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {







|
|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
	"strconv"
	"strings"

	gm "github.com/yuin/goldmark"
	gmAst "github.com/yuin/goldmark/ast"
	gmText "github.com/yuin/goldmark/text"

	"t73f.de/r/zsc/attrs"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder/textenc"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {

Changes to parser/none/none.go.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

// Package none provides a none-parser, e.g. for zettel with just metadata.
package none

import (
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	parser.Register(&parser.Info{







|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

// Package none provides a none-parser, e.g. for zettel with just metadata.
package none

import (
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	parser.Register(&parser.Info{

Changes to parser/parser.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package parser

import (
	"context"
	"fmt"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser/cleaner"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/meta"
)








|
|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package parser

import (
	"context"
	"fmt"
	"strings"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser/cleaner"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/meta"
)

Changes to parser/plain/plain.go.

14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29
30
// Package plain provides a parser for plain text data.
package plain

import (
	"bytes"
	"strings"


	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/sx.fossil/sxreader"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	parser.Register(&parser.Info{







>
|
|
<







14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
// Package plain provides a parser for plain text data.
package plain

import (
	"bytes"
	"strings"

	"t73f.de/r/sx/sxreader"
	"t73f.de/r/zsc/attrs"
	"t73f.de/r/zsc/input"

	"zettelstore.de/z/ast"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	parser.Register(&parser.Info{

Changes to parser/zettelmark/block.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package zettelmark

import (
	"fmt"

	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/ast"
)

// parseBlockSlice parses a sequence of blocks.
func (cp *zmkP) parseBlockSlice() ast.BlockSlice {
	inp := cp.inp
	var lastPara *ast.ParaNode







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package zettelmark

import (
	"fmt"

	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
)

// parseBlockSlice parses a sequence of blocks.
func (cp *zmkP) parseBlockSlice() ast.BlockSlice {
	inp := cp.inp
	var lastPara *ast.ParaNode

Changes to parser/zettelmark/inline.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package zettelmark

import (
	"bytes"
	"fmt"
	"strings"

	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/zettel/meta"
)

// parseInlineSlice parses a sequence of Inlines until EOS.
func (cp *zmkP) parseInlineSlice() (ins ast.InlineSlice) {
	inp := cp.inp







|
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package zettelmark

import (
	"bytes"
	"fmt"
	"strings"

	"t73f.de/r/zsc/attrs"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/zettel/meta"
)

// parseInlineSlice parses a sequence of Inlines until EOS.
func (cp *zmkP) parseInlineSlice() (ins ast.InlineSlice) {
	inp := cp.inp

Changes to parser/zettelmark/zettelmark.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Package zettelmark provides a parser for zettelmarkup.
package zettelmark

import (
	"strings"
	"unicode"

	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	parser.Register(&parser.Info{







|
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Package zettelmark provides a parser for zettelmarkup.
package zettelmark

import (
	"strings"
	"unicode"

	"t73f.de/r/zsc/attrs"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func init() {
	parser.Register(&parser.Info{

Changes to parser/zettelmark/zettelmark_fuzz_test.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//-----------------------------------------------------------------------------

package zettelmark_test

import (
	"testing"

	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func FuzzParseBlocks(f *testing.F) {
	f.Fuzz(func(t *testing.T, src []byte) {
		t.Parallel()
		inp := input.NewInput(src)
		parser.ParseBlocks(inp, nil, meta.SyntaxZmk, config.NoHTML)
	})
}







|












12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//-----------------------------------------------------------------------------

package zettelmark_test

import (
	"testing"

	"t73f.de/r/zsc/input"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

func FuzzParseBlocks(f *testing.F) {
	f.Fuzz(func(t *testing.T, src []byte) {
		t.Parallel()
		inp := input.NewInput(src)
		parser.ParseBlocks(inp, nil, meta.SyntaxZmk, config.NoHTML)
	})
}

Changes to parser/zettelmark/zettelmark_test.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package zettelmark_test

import (
	"fmt"
	"strings"
	"testing"

	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

type TestCase struct{ source, want string }







|
|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package zettelmark_test

import (
	"fmt"
	"strings"
	"testing"

	"t73f.de/r/zsc/attrs"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

type TestCase struct{ source, want string }

Changes to query/context.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package query

import (
	"container/heap"
	"context"
	"math"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// ContextSpec contains all specification values for calculating a context.
type ContextSpec struct {
	Direction ContextDirection







|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package query

import (
	"container/heap"
	"context"
	"math"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// ContextSpec contains all specification values for calculating a context.
type ContextSpec struct {
	Direction ContextDirection

Changes to query/parser.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//-----------------------------------------------------------------------------

package query

import (
	"strconv"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// Parse the query specification and return a Query object.
func Parse(spec string) (q *Query) { return q.Parse(spec) }








|
|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//-----------------------------------------------------------------------------

package query

import (
	"strconv"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// Parse the query specification and return a Query object.
func Parse(spec string) (q *Query) { return q.Parse(spec) }

Changes to query/print.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package query

import (
	"io"
	"strconv"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/maps"
	"zettelstore.de/z/zettel/id"
)

var op2string = map[compareOp]string{
	cmpExist:     api.ExistOperator,
	cmpNotExist:  api.ExistNotOperator,
	cmpEqual:     api.SearchOperatorEqual,







|
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package query

import (
	"io"
	"strconv"
	"strings"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/maps"
	"zettelstore.de/z/zettel/id"
)

var op2string = map[compareOp]string{
	cmpExist:     api.ExistOperator,
	cmpNotExist:  api.ExistNotOperator,
	cmpEqual:     api.SearchOperatorEqual,

Changes to query/select_test.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package query_test

import (
	"context"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func TestMatchZidNegate(t *testing.T) {
	q := query.Parse(api.KeyID + api.SearchOperatorHasNot + string(api.ZidVersion) + " " + api.KeyID + api.SearchOperatorHasNot + string(api.ZidLicense))







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package query_test

import (
	"context"
	"testing"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func TestMatchZidNegate(t *testing.T) {
	q := query.Parse(api.KeyID + api.SearchOperatorHasNot + string(api.ZidVersion) + " " + api.KeyID + api.SearchOperatorHasNot + string(api.ZidLicense))

Changes to query/sorter.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package query

import (
	"strconv"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/zettel/meta"
)

type sortFunc func(i, j int) bool

func createSortFunc(order []sortOrder, ml []*meta.Meta) sortFunc {
	hasID := false







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package query

import (
	"strconv"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/zettel/meta"
)

type sortFunc func(i, j int) bool

func createSortFunc(order []sortOrder, ml []*meta.Meta) sortFunc {
	hasID := false

Changes to query/specs.go.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------

package query

import "zettelstore.de/client.fossil/api"

// IdentSpec contains all specification values to calculate the ident directive.
type IdentSpec struct{}

func (spec *IdentSpec) Print(pe *PrintEnv) {
	pe.printSpace()
	pe.writeString(api.IdentDirective)







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------

package query

import "t73f.de/r/zsc/api"

// IdentSpec contains all specification values to calculate the ident directive.
type IdentSpec struct{}

func (spec *IdentSpec) Print(pe *PrintEnv) {
	pe.printSpace()
	pe.writeString(api.IdentDirective)

Changes to query/unlinked.go.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------

package query

import (
	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/meta"
)

// UnlinkedSpec contains all specification values to calculate unlinked references.
type UnlinkedSpec struct {
	words []string







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------

package query

import (
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/meta"
)

// UnlinkedSpec contains all specification values to calculate unlinked references.
type UnlinkedSpec struct {
	words []string

Changes to tests/client/client_test.go.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
	"io"
	"net/http"
	"net/url"
	"slices"
	"strconv"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/client"
	"zettelstore.de/z/kernel"
)

func nextZid(zid api.ZettelID) api.ZettelID {
	numVal, err := strconv.ParseUint(string(zid), 10, 64)
	if err != nil {
		panic(err)







|
|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
	"io"
	"net/http"
	"net/url"
	"slices"
	"strconv"
	"testing"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/client"
	"zettelstore.de/z/kernel"
)

func nextZid(zid api.ZettelID) api.ZettelID {
	numVal, err := strconv.ParseUint(string(zid), 10, 64)
	if err != nil {
		panic(err)
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
		}

	}
}

func TestListZettel(t *testing.T) {
	const (
		ownerZettel      = 55
		configRoleZettel = 33
		writerZettel     = ownerZettel - 24
		readerZettel     = ownerZettel - 24
		creatorZettel    = 10
		publicZettel     = 5
	)

	testdata := []struct {
		user string
		exp  int







|
|
|
|







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
		}

	}
}

func TestListZettel(t *testing.T) {
	const (
		ownerZettel      = 56
		configRoleZettel = 34
		writerZettel     = ownerZettel - 25
		readerZettel     = ownerZettel - 25
		creatorZettel    = 10
		publicZettel     = 5
	)

	testdata := []struct {
		user string
		exp  int

Changes to tests/client/crud_test.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package client_test

import (
	"context"
	"strings"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/client"
)

// ---------------------------------------------------------------------------
// Tests that change the Zettelstore must nor run parallel to other tests.

func TestCreateGetRenameDeleteZettel(t *testing.T) {
	// Is not to be allowed to run in parallel with other tests.







|
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package client_test

import (
	"context"
	"strings"
	"testing"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/client"
)

// ---------------------------------------------------------------------------
// Tests that change the Zettelstore must nor run parallel to other tests.

func TestCreateGetRenameDeleteZettel(t *testing.T) {
	// Is not to be allowed to run in parallel with other tests.

Changes to tests/client/embed_test.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package client_test

import (
	"context"
	"strings"
	"testing"

	"zettelstore.de/client.fossil/api"
)

const (
	abcZid   = api.ZettelID("20211020121000")
	abc10Zid = api.ZettelID("20211020121100")
)








|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package client_test

import (
	"context"
	"strings"
	"testing"

	"t73f.de/r/zsc/api"
)

const (
	abcZid   = api.ZettelID("20211020121000")
	abc10Zid = api.ZettelID("20211020121100")
)

Changes to tests/markdown_test.go.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoder"
	_ "zettelstore.de/z/encoder/htmlenc"
	_ "zettelstore.de/z/encoder/mdenc"
	_ "zettelstore.de/z/encoder/shtmlenc"
	_ "zettelstore.de/z/encoder/szenc"







|
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"testing"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoder"
	_ "zettelstore.de/z/encoder/htmlenc"
	_ "zettelstore.de/z/encoder/mdenc"
	_ "zettelstore.de/z/encoder/shtmlenc"
	_ "zettelstore.de/z/encoder/szenc"

Changes to tests/naughtystrings_test.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import (
	"bufio"
	"io"
	"os"
	"path/filepath"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	_ "zettelstore.de/z/cmd"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

// Test all parser / encoder with a list of "naughty strings", i.e. unusual strings







|
|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import (
	"bufio"
	"io"
	"os"
	"path/filepath"
	"testing"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	_ "zettelstore.de/z/cmd"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/zettel/meta"
)

// Test all parser / encoder with a list of "naughty strings", i.e. unusual strings

Changes to tests/regression_test.go.

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
	"io"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/manager"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/parser"







|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
	"io"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/box/manager"
	"zettelstore.de/z/config"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/parser"

Changes to tools/build/build.go.

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
	"io"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/tools"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func readVersionFile() (string, error) {







|
|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
	"io"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
	"time"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/tools"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func readVersionFile() (string, error) {

Changes to tools/htmllint/htmllint.go.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
	"math/rand/v2"
	"net/url"
	"os"
	"regexp"
	"sort"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/client"
	"zettelstore.de/z/tools"
)

func main() {
	flag.BoolVar(&tools.Verbose, "v", false, "Verbose output")
	flag.Parse()








|
|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
	"math/rand/v2"
	"net/url"
	"os"
	"regexp"
	"sort"
	"strings"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/client"
	"zettelstore.de/z/tools"
)

func main() {
	flag.BoolVar(&tools.Verbose, "v", false, "Verbose output")
	flag.Parse()

Changes to tools/tools.go.

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
	"os/exec"
	"strings"

	"zettelstore.de/z/strfun"
)

var EnvDirectProxy = []string{"GOPROXY=direct"}
var EnvGoVCS = []string{"GOVCS=zettelstore.de:fossil"}
var Verbose bool

func ExecuteCommand(env []string, name string, arg ...string) (string, error) {
	LogCommand("EXEC", env, name, arg)
	var out strings.Builder
	cmd := PrepareCommand(env, name, arg, nil, &out, os.Stderr)
	err := cmd.Run()







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
	"os/exec"
	"strings"

	"zettelstore.de/z/strfun"
)

var EnvDirectProxy = []string{"GOPROXY=direct"}
var EnvGoVCS = []string{"GOVCS=zettelstore.de:fossil,t73f.de:fossil"}
var Verbose bool

func ExecuteCommand(env []string, name string, arg ...string) (string, error) {
	LogCommand("EXEC", env, name, arg)
	var out strings.Builder
	cmd := PrepareCommand(env, name, arg, nil, &out, os.Stderr)
	err := cmd.Run()

Changes to usecase/authenticate.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"context"
	"math/rand/v2"
	"net/http"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/auth/cred"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)








|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"context"
	"math/rand/v2"
	"net/http"
	"time"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/auth/cred"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

Changes to usecase/create_zettel.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package usecase

import (
	"context"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/config"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)








|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package usecase

import (
	"context"
	"time"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/config"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

Changes to usecase/get_special_zettel.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package usecase

import (
	"context"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// TagZettel is the usecase of retrieving a "tag zettel", i.e. a zettel that







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package usecase

import (
	"context"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// TagZettel is the usecase of retrieving a "tag zettel", i.e. a zettel that

Changes to usecase/get_user.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package usecase

import (
	"context"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/box"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package usecase

import (
	"context"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/box"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

Changes to usecase/lists.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package usecase

import (
	"context"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel/meta"
)

// -------- List syntax ------------------------------------------------------







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package usecase

import (
	"context"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/query"
	"zettelstore.de/z/zettel/meta"
)

// -------- List syntax ------------------------------------------------------

Changes to usecase/query.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/collect"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/query"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel"







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/collect"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/query"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel"

Changes to usecase/update_zettel.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package usecase

import (
	"context"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)








|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package usecase

import (
	"context"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

Changes to web/adapter/adapter.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// Package adapter provides handlers for web requests, and some helper tools.
package adapter

import (
	"context"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/zettel/meta"
)

// TryReIndex executes a re-index if the appropriate query action is given.
func TryReIndex(ctx context.Context, actions []string, metaSeq []*meta.Meta, reIndex *usecase.ReIndex) ([]string, error) {
	if lenActions := len(actions); lenActions > 0 {







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// Package adapter provides handlers for web requests, and some helper tools.
package adapter

import (
	"context"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/zettel/meta"
)

// TryReIndex executes a re-index if the appropriate query action is given.
func TryReIndex(ctx context.Context, actions []string, metaSeq []*meta.Meta, reIndex *usecase.ReIndex) ([]string, error) {
	if lenActions := len(actions); lenActions > 0 {

Changes to web/adapter/api/api.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import (
	"bytes"
	"context"
	"net/http"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/config"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/meta"







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import (
	"bytes"
	"context"
	"net/http"
	"time"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/config"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/meta"

Changes to web/adapter/api/command.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package api

import (
	"context"
	"net/http"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/usecase"
)

// MakePostCommandHandler creates a new HTTP handler to execute certain commands.
func (a *API) MakePostCommandHandler(
	ucIsAuth *usecase.IsAuthenticated,
	ucRefresh *usecase.Refresh,







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package api

import (
	"context"
	"net/http"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/usecase"
)

// MakePostCommandHandler creates a new HTTP handler to execute certain commands.
func (a *API) MakePostCommandHandler(
	ucIsAuth *usecase.IsAuthenticated,
	ucRefresh *usecase.Refresh,

Changes to web/adapter/api/create_zettel.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//-----------------------------------------------------------------------------

package api

import (
	"net/http"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/content"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
)








|
|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//-----------------------------------------------------------------------------

package api

import (
	"net/http"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/content"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
)

Changes to web/adapter/api/get_data.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//-----------------------------------------------------------------------------

package api

import (
	"net/http"

	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/zettel/id"
)

// MakeGetDataHandler creates a new HTTP handler to return zettelstore data.
func (a *API) MakeGetDataHandler(ucVersion usecase.Version) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		version := ucVersion.Run()
		err := a.writeObject(w, id.Invalid, sx.MakeList(
			sx.Int64(version.Major),
			sx.Int64(version.Minor),
			sx.Int64(version.Patch),
			sx.String(version.Info),
			sx.String(version.Hash),
		))
		if err != nil {
			a.log.Error().Err(err).Msg("Write Version Info")
		}
	}
}







|












|
|






12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//-----------------------------------------------------------------------------

package api

import (
	"net/http"

	"t73f.de/r/sx"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/zettel/id"
)

// MakeGetDataHandler creates a new HTTP handler to return zettelstore data.
func (a *API) MakeGetDataHandler(ucVersion usecase.Version) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		version := ucVersion.Run()
		err := a.writeObject(w, id.Invalid, sx.MakeList(
			sx.Int64(version.Major),
			sx.Int64(version.Minor),
			sx.Int64(version.Patch),
			sx.MakeString(version.Info),
			sx.MakeString(version.Hash),
		))
		if err != nil {
			a.log.Error().Err(err).Msg("Write Version Info")
		}
	}
}

Changes to web/adapter/api/get_zettel.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

import (
	"bytes"
	"context"
	"fmt"
	"net/http"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/sexp"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/content"







|
|
|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

import (
	"bytes"
	"context"
	"fmt"
	"net/http"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/sexp"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/content"

Changes to web/adapter/api/login.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package api

import (
	"net/http"
	"time"

	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/zettel/id"
)

// MakePostLoginHandler creates a new HTTP handler to authenticate the given user via API.







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package api

import (
	"net/http"
	"time"

	"t73f.de/r/sx"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/zettel/id"
)

// MakePostLoginHandler creates a new HTTP handler to authenticate the given user via API.
99
100
101
102
103
104
105
106
107
108
109
110
			a.log.Error().Err(err).Msg("Write renewed token")
		}
	}
}

func (a *API) writeToken(w http.ResponseWriter, token string, lifetime time.Duration) error {
	return a.writeObject(w, id.Invalid, sx.MakeList(
		sx.String("Bearer"),
		sx.String(token),
		sx.Int64(int64(lifetime/time.Second)),
	))
}







|
|



99
100
101
102
103
104
105
106
107
108
109
110
			a.log.Error().Err(err).Msg("Write renewed token")
		}
	}
}

func (a *API) writeToken(w http.ResponseWriter, token string, lifetime time.Duration) error {
	return a.writeObject(w, id.Invalid, sx.MakeList(
		sx.MakeString("Bearer"),
		sx.MakeString(token),
		sx.Int64(int64(lifetime/time.Second)),
	))
}

Changes to web/adapter/api/query.go.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/sexp"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/query"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/content"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)







|
|
|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/sexp"
	"zettelstore.de/z/query"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/content"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
		})
		msz = sx.Cons(sx.MakeList(symID, sx.Int64(m.Zid)), msz.Cdr()).Cons(symZettel)
		result[i+1] = msz
	}

	_, err := sx.Print(w, sx.MakeList(
		sx.MakeSymbol("meta-list"),
		sx.MakeList(sx.MakeSymbol("query"), sx.String(dze.sq.String())),
		sx.MakeList(sx.MakeSymbol("human"), sx.String(dze.sq.Human())),
		sx.MakeList(result...),
	))
	return err
}
func (dze *dataZettelEncoder) writeArrangement(w io.Writer, act string, arr meta.Arrangement) error {
	result := sx.Nil()
	for aggKey, metaList := range arr {
		sxMeta := sx.Nil()
		for i := len(metaList) - 1; i >= 0; i-- {
			sxMeta = sxMeta.Cons(sx.Int64(metaList[i].Zid))
		}
		sxMeta = sxMeta.Cons(sx.String(aggKey))
		result = result.Cons(sxMeta)
	}
	_, err := sx.Print(w, sx.MakeList(
		sx.MakeSymbol("aggregate"),
		sx.String(act),
		sx.MakeList(sx.MakeSymbol("query"), sx.String(dze.sq.String())),
		sx.MakeList(sx.MakeSymbol("human"), sx.String(dze.sq.Human())),
		result.Cons(sx.SymbolList),
	))
	return err
}

func (a *API) handleTagZettel(w http.ResponseWriter, r *http.Request, tagZettel *usecase.TagZettel, vals url.Values) bool {
	tag := vals.Get(api.QueryKeyTag)







|
|











|




|
|
|







212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
		})
		msz = sx.Cons(sx.MakeList(symID, sx.Int64(m.Zid)), msz.Cdr()).Cons(symZettel)
		result[i+1] = msz
	}

	_, err := sx.Print(w, sx.MakeList(
		sx.MakeSymbol("meta-list"),
		sx.MakeList(sx.MakeSymbol("query"), sx.MakeString(dze.sq.String())),
		sx.MakeList(sx.MakeSymbol("human"), sx.MakeString(dze.sq.Human())),
		sx.MakeList(result...),
	))
	return err
}
func (dze *dataZettelEncoder) writeArrangement(w io.Writer, act string, arr meta.Arrangement) error {
	result := sx.Nil()
	for aggKey, metaList := range arr {
		sxMeta := sx.Nil()
		for i := len(metaList) - 1; i >= 0; i-- {
			sxMeta = sxMeta.Cons(sx.Int64(metaList[i].Zid))
		}
		sxMeta = sxMeta.Cons(sx.MakeString(aggKey))
		result = result.Cons(sxMeta)
	}
	_, err := sx.Print(w, sx.MakeList(
		sx.MakeSymbol("aggregate"),
		sx.MakeString(act),
		sx.MakeList(sx.MakeSymbol("query"), sx.MakeString(dze.sq.String())),
		sx.MakeList(sx.MakeSymbol("human"), sx.MakeString(dze.sq.Human())),
		result.Cons(sx.SymbolList),
	))
	return err
}

func (a *API) handleTagZettel(w http.ResponseWriter, r *http.Request, tagZettel *usecase.TagZettel, vals url.Values) bool {
	tag := vals.Get(api.QueryKeyTag)

Changes to web/adapter/api/rename_zettel.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package api

import (
	"net/http"
	"net/url"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/zettel/id"
)

// MakeRenameZettelHandler creates a new HTTP handler to update a zettel.
func (a *API) MakeRenameZettelHandler(renameZettel *usecase.RenameZettel) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package api

import (
	"net/http"
	"net/url"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/zettel/id"
)

// MakeRenameZettelHandler creates a new HTTP handler to update a zettel.
func (a *API) MakeRenameZettelHandler(renameZettel *usecase.RenameZettel) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

Changes to web/adapter/api/request.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package api

import (
	"io"
	"net/http"
	"net/url"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/client.fossil/sexp"
	"zettelstore.de/sx.fossil/sxreader"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// getEncoding returns the data encoding selected by the caller.
func getEncoding(r *http.Request, q url.Values) (api.EncodingEnum, string) {







|
|
|
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package api

import (
	"io"
	"net/http"
	"net/url"

	"t73f.de/r/sx/sxreader"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"t73f.de/r/zsc/sexp"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// getEncoding returns the data encoding selected by the caller.
func getEncoding(r *http.Request, q url.Values) (api.EncodingEnum, string) {

Changes to web/adapter/api/response.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package api

import (
	"bytes"
	"net/http"

	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/web/content"
	"zettelstore.de/z/zettel/id"
)

func (a *API) writeObject(w http.ResponseWriter, zid id.Zid, obj sx.Object) error {
	var buf bytes.Buffer
	if _, err := sx.Print(&buf, obj); err != nil {







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package api

import (
	"bytes"
	"net/http"

	"t73f.de/r/sx"
	"zettelstore.de/z/web/content"
	"zettelstore.de/z/zettel/id"
)

func (a *API) writeObject(w http.ResponseWriter, zid id.Zid, obj sx.Object) error {
	var buf bytes.Buffer
	if _, err := sx.Print(&buf, obj); err != nil {

Changes to web/adapter/api/update_zettel.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package api

import (
	"net/http"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
)

// MakeUpdateZettelHandler creates a new HTTP handler to update a zettel.







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package api

import (
	"net/http"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
)

// MakeUpdateZettelHandler creates a new HTTP handler to update a zettel.

Changes to web/adapter/request.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/query"
)

// GetCredentialsViaForm retrieves the authentication credentions from a form.
func GetCredentialsViaForm(r *http.Request) (ident, cred string, ok bool) {
	err := r.ParseForm()







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/query"
)

// GetCredentialsViaForm retrieves the authentication credentions from a form.
func GetCredentialsViaForm(r *http.Request) (ident, cred string, ok bool) {
	err := r.ParseForm()

Changes to web/adapter/response.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"errors"
	"fmt"
	"net/http"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/usecase"
)

// WriteData emits the given data to the response writer.
func WriteData(w http.ResponseWriter, data []byte, contentType string) error {
	if len(data) == 0 {







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"errors"
	"fmt"
	"net/http"
	"strings"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/usecase"
)

// WriteData emits the given data to the response writer.
func WriteData(w http.ResponseWriter, data []byte, contentType string) error {
	if len(data) == 0 {

Changes to web/adapter/webui/create_zettel.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import (
	"bytes"
	"context"
	"net/http"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/box"
	"zettelstore.de/z/encoder/zmkenc"
	"zettelstore.de/z/evaluator"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/server"







|
|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import (
	"bytes"
	"context"
	"net/http"
	"strings"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/encoder/zmkenc"
	"zettelstore.de/z/evaluator"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/server"
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
	for _, p := range m.PairsRest() {
		sb.WriteString(p.Key)
		sb.WriteString(": ")
		sb.WriteString(p.Value)
		sb.WriteByte('\n')
	}
	env, rb := wui.createRenderEnv(ctx, "form", wui.rtConfig.Get(ctx, nil, api.KeyLang), title, user)
	rb.bindString("heading", sx.String(title))
	rb.bindString("form-action-url", sx.String(formActionURL))
	rb.bindString("role-data", makeStringList(roleData))
	rb.bindString("syntax-data", makeStringList(syntaxData))
	rb.bindString("meta", sx.String(sb.String()))
	if !ztl.Content.IsBinary() {
		rb.bindString("content", sx.String(ztl.Content.AsString()))
	}
	wui.bindCommonZettelData(ctx, &rb, user, m, &ztl.Content)
	if rb.err == nil {
		rb.err = wui.renderSxnTemplate(ctx, w, id.FormTemplateZid, env)
	}
	if err := rb.err; err != nil {
		wui.reportError(ctx, w, err)







|
|


|

|







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
	for _, p := range m.PairsRest() {
		sb.WriteString(p.Key)
		sb.WriteString(": ")
		sb.WriteString(p.Value)
		sb.WriteByte('\n')
	}
	env, rb := wui.createRenderEnv(ctx, "form", wui.rtConfig.Get(ctx, nil, api.KeyLang), title, user)
	rb.bindString("heading", sx.MakeString(title))
	rb.bindString("form-action-url", sx.MakeString(formActionURL))
	rb.bindString("role-data", makeStringList(roleData))
	rb.bindString("syntax-data", makeStringList(syntaxData))
	rb.bindString("meta", sx.MakeString(sb.String()))
	if !ztl.Content.IsBinary() {
		rb.bindString("content", sx.MakeString(ztl.Content.AsString()))
	}
	wui.bindCommonZettelData(ctx, &rb, user, m, &ztl.Content)
	if rb.err == nil {
		rb.err = wui.renderSxnTemplate(ctx, w, id.FormTemplateZid, env)
	}
	if err := rb.err; err != nil {
		wui.reportError(ctx, w, err)

Changes to web/adapter/webui/delete_zettel.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//-----------------------------------------------------------------------------

package webui

import (
	"net/http"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/maps"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/box"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)







|
|
|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//-----------------------------------------------------------------------------

package webui

import (
	"net/http"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/maps"
	"zettelstore.de/z/box"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
		m := zs[0].Meta

		user := server.GetUser(ctx)
		env, rb := wui.createRenderEnv(
			ctx, "delete",
			wui.rtConfig.Get(ctx, nil, api.KeyLang), "Delete Zettel "+m.Zid.String(), user)
		if len(zs) > 1 {
			rb.bindString("shadowed-box", sx.String(zs[1].Meta.GetDefault(api.KeyBoxNumber, "???")))
			rb.bindString("incoming", nil)
		} else {
			rb.bindString("shadowed-box", nil)
			rb.bindString("incoming", wui.encodeIncoming(m, wui.makeGetTextTitle(ctx, getZettel)))
		}
		wui.bindCommonZettelData(ctx, &rb, user, m, nil)








|







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
		m := zs[0].Meta

		user := server.GetUser(ctx)
		env, rb := wui.createRenderEnv(
			ctx, "delete",
			wui.rtConfig.Get(ctx, nil, api.KeyLang), "Delete Zettel "+m.Zid.String(), user)
		if len(zs) > 1 {
			rb.bindString("shadowed-box", sx.MakeString(zs[1].Meta.GetDefault(api.KeyBoxNumber, "???")))
			rb.bindString("incoming", nil)
		} else {
			rb.bindString("shadowed-box", nil)
			rb.bindString("incoming", wui.encodeIncoming(m, wui.makeGetTextTitle(ctx, getZettel)))
		}
		wui.bindCommonZettelData(ctx, &rb, user, m, nil)

Changes to web/adapter/webui/forms.go.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
	"errors"
	"io"
	"net/http"
	"regexp"
	"strings"
	"unicode"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/web/content"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)







|
|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
	"errors"
	"io"
	"net/http"
	"regexp"
	"strings"
	"unicode"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/web/content"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

Changes to web/adapter/webui/get_info.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import (
	"context"
	"net/http"
	"sort"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/collect"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/evaluator"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/query"







|
|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import (
	"context"
	"net/http"
	"sort"
	"strings"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/collect"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/evaluator"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/query"
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
			return ucEvaluate.RunMetadata(ctx, val)
		}
		pairs := zn.Meta.ComputedPairs()
		metadata := sx.Nil()
		for i := len(pairs) - 1; i >= 0; i-- {
			key := pairs[i].Key
			sxval := wui.writeHTMLMetaValue(key, pairs[i].Value, getTextTitle, evalMeta, enc)
			metadata = metadata.Cons(sx.Cons(sx.String(key), sxval))
		}

		summary := collect.References(zn)
		locLinks, queryLinks, extLinks := wui.splitLocSeaExtLinks(append(summary.Links, summary.Embeds...))

		title := parser.NormalizedSpacedText(zn.InhMeta.GetTitle())
		phrase := q.Get(api.QueryKeyPhrase)







|







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
			return ucEvaluate.RunMetadata(ctx, val)
		}
		pairs := zn.Meta.ComputedPairs()
		metadata := sx.Nil()
		for i := len(pairs) - 1; i >= 0; i-- {
			key := pairs[i].Key
			sxval := wui.writeHTMLMetaValue(key, pairs[i].Value, getTextTitle, evalMeta, enc)
			metadata = metadata.Cons(sx.Cons(sx.MakeString(key), sxval))
		}

		summary := collect.References(zn)
		locLinks, queryLinks, extLinks := wui.splitLocSeaExtLinks(append(summary.Links, summary.Embeds...))

		title := parser.NormalizedSpacedText(zn.InhMeta.GetTitle())
		phrase := q.Get(api.QueryKeyPhrase)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
		user := server.GetUser(ctx)
		env, rb := wui.createRenderEnv(ctx, "info", wui.rtConfig.Get(ctx, nil, api.KeyLang), title, user)
		rb.bindString("metadata", metadata)
		rb.bindString("local-links", locLinks)
		rb.bindString("query-links", queryLinks)
		rb.bindString("ext-links", extLinks)
		rb.bindString("unlinked-content", unlinkedContent)
		rb.bindString("phrase", sx.String(phrase))
		rb.bindString("query-key-phrase", sx.String(api.QueryKeyPhrase))
		rb.bindString("enc-eval", wui.infoAPIMatrix(zid, false, encTexts))
		rb.bindString("enc-parsed", wui.infoAPIMatrixParsed(zid, encTexts))
		rb.bindString("shadow-links", shadowLinks)
		wui.bindCommonZettelData(ctx, &rb, user, zn.InhMeta, &zn.Content)
		if rb.err == nil {
			err = wui.renderSxnTemplate(ctx, w, id.InfoTemplateZid, env)
		} else {







|
|







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
		user := server.GetUser(ctx)
		env, rb := wui.createRenderEnv(ctx, "info", wui.rtConfig.Get(ctx, nil, api.KeyLang), title, user)
		rb.bindString("metadata", metadata)
		rb.bindString("local-links", locLinks)
		rb.bindString("query-links", queryLinks)
		rb.bindString("ext-links", extLinks)
		rb.bindString("unlinked-content", unlinkedContent)
		rb.bindString("phrase", sx.MakeString(phrase))
		rb.bindString("query-key-phrase", sx.MakeString(api.QueryKeyPhrase))
		rb.bindString("enc-eval", wui.infoAPIMatrix(zid, false, encTexts))
		rb.bindString("enc-parsed", wui.infoAPIMatrixParsed(zid, encTexts))
		rb.bindString("shadow-links", shadowLinks)
		wui.bindCommonZettelData(ctx, &rb, user, zn.InhMeta, &zn.Content)
		if rb.err == nil {
			err = wui.renderSxnTemplate(ctx, w, id.InfoTemplateZid, env)
		} else {
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
		ref := links[i]
		if ref.State == ast.RefStateSelf || ref.IsZettel() {
			continue
		}
		if ref.State == ast.RefStateQuery {
			queries = queries.Cons(
				sx.Cons(
					sx.String(ref.Value),
					sx.String(wui.NewURLBuilder('h').AppendQuery(ref.Value).String())))
			continue
		}
		if ref.IsExternal() {
			extLinks = extLinks.Cons(sx.String(ref.String()))
			continue
		}
		locLinks = locLinks.Cons(sx.Cons(sx.MakeBoolean(ref.IsValid()), sx.String(ref.String())))
	}
	return locLinks, queries, extLinks
}

func createUnlinkedQuery(zid id.Zid, phrase string) *query.Query {
	var sb strings.Builder
	sb.Write(zid.Bytes())







|
|



|


|







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
		ref := links[i]
		if ref.State == ast.RefStateSelf || ref.IsZettel() {
			continue
		}
		if ref.State == ast.RefStateQuery {
			queries = queries.Cons(
				sx.Cons(
					sx.MakeString(ref.Value),
					sx.MakeString(wui.NewURLBuilder('h').AppendQuery(ref.Value).String())))
			continue
		}
		if ref.IsExternal() {
			extLinks = extLinks.Cons(sx.MakeString(ref.String()))
			continue
		}
		locLinks = locLinks.Cons(sx.Cons(sx.MakeBoolean(ref.IsValid()), sx.MakeString(ref.String())))
	}
	return locLinks, queries, extLinks
}

func createUnlinkedQuery(zid id.Zid, phrase string) *query.Query {
	var sb strings.Builder
	sb.Write(zid.Bytes())
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
		for je := len(encTexts) - 1; je >= 0; je-- {
			enc := encTexts[je]
			if parseOnly {
				u.AppendKVQuery(api.QueryKeyParseOnly, "")
			}
			u.AppendKVQuery(api.QueryKeyPart, part)
			u.AppendKVQuery(api.QueryKeyEncoding, enc)
			row = row.Cons(sx.Cons(sx.String(enc), sx.String(u.String())))
			u.ClearQuery()
		}
		matrix = matrix.Cons(sx.Cons(sx.String(part), row))
	}
	return matrix
}

func (wui *WebUI) infoAPIMatrixParsed(zid id.Zid, encTexts []string) *sx.Pair {
	matrix := wui.infoAPIMatrix(zid, true, encTexts)
	u := wui.NewURLBuilder('z').SetZid(zid.ZettelID())

	for i, row := 0, matrix; i < len(apiParts) && row != nil; row = row.Tail() {
		line, isLine := sx.GetPair(row.Car())
		if !isLine || line == nil {
			continue
		}
		last := line.LastPair()
		part := apiParts[i]
		u.AppendKVQuery(api.QueryKeyPart, part)
		last = last.AppendBang(sx.Cons(sx.String("plain"), sx.String(u.String())))
		u.ClearQuery()
		if i < 2 {
			u.AppendKVQuery(api.QueryKeyEncoding, api.EncodingData)
			u.AppendKVQuery(api.QueryKeyPart, part)
			last.AppendBang(sx.Cons(sx.String("data"), sx.String(u.String())))
			u.ClearQuery()
		}
		i++
	}
	return matrix
}

func getShadowLinks(ctx context.Context, zid id.Zid, getAllZettel usecase.GetAllZettel) *sx.Pair {
	result := sx.Nil()
	if zl, err := getAllZettel.Run(ctx, zid); err == nil {
		for i := len(zl) - 1; i >= 1; i-- {
			if boxNo, ok := zl[i].Meta.Get(api.KeyBoxNumber); ok {
				result = result.Cons(sx.String(boxNo))
			}
		}
	}
	return result
}







|


|
















|




|












|





181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
		for je := len(encTexts) - 1; je >= 0; je-- {
			enc := encTexts[je]
			if parseOnly {
				u.AppendKVQuery(api.QueryKeyParseOnly, "")
			}
			u.AppendKVQuery(api.QueryKeyPart, part)
			u.AppendKVQuery(api.QueryKeyEncoding, enc)
			row = row.Cons(sx.Cons(sx.MakeString(enc), sx.MakeString(u.String())))
			u.ClearQuery()
		}
		matrix = matrix.Cons(sx.Cons(sx.MakeString(part), row))
	}
	return matrix
}

func (wui *WebUI) infoAPIMatrixParsed(zid id.Zid, encTexts []string) *sx.Pair {
	matrix := wui.infoAPIMatrix(zid, true, encTexts)
	u := wui.NewURLBuilder('z').SetZid(zid.ZettelID())

	for i, row := 0, matrix; i < len(apiParts) && row != nil; row = row.Tail() {
		line, isLine := sx.GetPair(row.Car())
		if !isLine || line == nil {
			continue
		}
		last := line.LastPair()
		part := apiParts[i]
		u.AppendKVQuery(api.QueryKeyPart, part)
		last = last.AppendBang(sx.Cons(sx.MakeString("plain"), sx.MakeString(u.String())))
		u.ClearQuery()
		if i < 2 {
			u.AppendKVQuery(api.QueryKeyEncoding, api.EncodingData)
			u.AppendKVQuery(api.QueryKeyPart, part)
			last.AppendBang(sx.Cons(sx.MakeString("data"), sx.MakeString(u.String())))
			u.ClearQuery()
		}
		i++
	}
	return matrix
}

func getShadowLinks(ctx context.Context, zid id.Zid, getAllZettel usecase.GetAllZettel) *sx.Pair {
	result := sx.Nil()
	if zl, err := getAllZettel.Run(ctx, zid); err == nil {
		for i := len(zl) - 1; i >= 1; i-- {
			if boxNo, ok := zl[i].Meta.Get(api.KeyBoxNumber); ok {
				result = result.Cons(sx.MakeString(boxNo))
			}
		}
	}
	return result
}

Changes to web/adapter/webui/get_zettel.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package webui

import (
	"context"
	"net/http"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/shtml"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/box"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"







|
|
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package webui

import (
	"context"
	"net/http"
	"strings"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/shtml"
	"zettelstore.de/z/box"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

		user := server.GetUser(ctx)
		getTextTitle := wui.makeGetTextTitle(ctx, getZettel)

		title := parser.NormalizedSpacedText(zn.InhMeta.GetTitle())
		env, rb := wui.createRenderEnv(ctx, "zettel", wui.rtConfig.Get(ctx, zn.InhMeta, api.KeyLang), title, user)
		rb.bindSymbol(symMetaHeader, metaObj)
		rb.bindString("heading", sx.String(title))
		if role, found := zn.InhMeta.Get(api.KeyRole); found && role != "" {
			rb.bindString("role-url", sx.String(wui.NewURLBuilder('h').AppendQuery(api.KeyRole+api.SearchOperatorHas+role).String()))
		}
		if folgeRole, found := zn.InhMeta.Get(api.KeyFolgeRole); found && folgeRole != "" {
			rb.bindString("folge-role-url", sx.String(wui.NewURLBuilder('h').AppendQuery(api.KeyRole+api.SearchOperatorHas+folgeRole).String()))
		}
		rb.bindString("tag-refs", wui.transformTagSet(api.KeyTags, meta.ListFromValue(zn.InhMeta.GetDefault(api.KeyTags, ""))))
		rb.bindString("predecessor-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeyPredecessor, getTextTitle))
		rb.bindString("precursor-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeyPrecursor, getTextTitle))
		rb.bindString("superior-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeySuperior, getTextTitle))
		rb.bindString("urls", metaURLAssoc(zn.InhMeta))
		rb.bindString("content", content)







|

|


|







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

		user := server.GetUser(ctx)
		getTextTitle := wui.makeGetTextTitle(ctx, getZettel)

		title := parser.NormalizedSpacedText(zn.InhMeta.GetTitle())
		env, rb := wui.createRenderEnv(ctx, "zettel", wui.rtConfig.Get(ctx, zn.InhMeta, api.KeyLang), title, user)
		rb.bindSymbol(symMetaHeader, metaObj)
		rb.bindString("heading", sx.MakeString(title))
		if role, found := zn.InhMeta.Get(api.KeyRole); found && role != "" {
			rb.bindString("role-url", sx.MakeString(wui.NewURLBuilder('h').AppendQuery(api.KeyRole+api.SearchOperatorHas+role).String()))
		}
		if folgeRole, found := zn.InhMeta.Get(api.KeyFolgeRole); found && folgeRole != "" {
			rb.bindString("folge-role-url", sx.MakeString(wui.NewURLBuilder('h').AppendQuery(api.KeyRole+api.SearchOperatorHas+folgeRole).String()))
		}
		rb.bindString("tag-refs", wui.transformTagSet(api.KeyTags, meta.ListFromValue(zn.InhMeta.GetDefault(api.KeyTags, ""))))
		rb.bindString("predecessor-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeyPredecessor, getTextTitle))
		rb.bindString("precursor-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeyPrecursor, getTextTitle))
		rb.bindString("superior-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeySuperior, getTextTitle))
		rb.bindString("urls", metaURLAssoc(zn.InhMeta))
		rb.bindString("content", content)
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
}

func metaURLAssoc(m *meta.Meta) *sx.Pair {
	var result sx.ListBuilder
	for _, p := range m.PairsRest() {
		if key := p.Key; strings.HasSuffix(key, meta.SuffixKeyURL) {
			if val := p.Value; val != "" {
				result.Add(sx.Cons(sx.String(capitalizeMetaKey(key)), sx.String(val)))
			}
		}
	}
	return result.List()
}

func (wui *WebUI) bindLinks(ctx context.Context, rb *renderBinder, varPrefix string, m *meta.Meta, key, configKey string, getTextTitle getTextTitleFunc) {







|







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
}

func metaURLAssoc(m *meta.Meta) *sx.Pair {
	var result sx.ListBuilder
	for _, p := range m.PairsRest() {
		if key := p.Key; strings.HasSuffix(key, meta.SuffixKeyURL) {
			if val := p.Value; val != "" {
				result.Add(sx.Cons(sx.MakeString(capitalizeMetaKey(key)), sx.MakeString(val)))
			}
		}
	}
	return result.List()
}

func (wui *WebUI) bindLinks(ctx context.Context, rb *renderBinder, varPrefix string, m *meta.Meta, key, configKey string, getTextTitle getTextTitleFunc) {
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
	for i := len(values) - 1; i >= 0; i-- {
		val := values[i]
		zid, err := id.Parse(val)
		if err != nil {
			continue
		}
		if title, found := getTextTitle(zid); found > 0 {
			url := sx.String(wui.NewURLBuilder('h').SetZid(zid.ZettelID()).String())
			if title == "" {
				lst = lst.Cons(sx.Cons(sx.String(val), url))
			} else {
				lst = lst.Cons(sx.Cons(sx.String(title), url))
			}
		}
	}
	return lst
}







|

|

|





147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
	for i := len(values) - 1; i >= 0; i-- {
		val := values[i]
		zid, err := id.Parse(val)
		if err != nil {
			continue
		}
		if title, found := getTextTitle(zid); found > 0 {
			url := sx.MakeString(wui.NewURLBuilder('h').SetZid(zid.ZettelID()).String())
			if title == "" {
				lst = lst.Cons(sx.Cons(sx.MakeString(val), url))
			} else {
				lst = lst.Cons(sx.Cons(sx.MakeString(title), url))
			}
		}
	}
	return lst
}

Changes to web/adapter/webui/htmlgen.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

package webui

import (
	"net/url"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/attrs"
	"zettelstore.de/client.fossil/maps"
	"zettelstore.de/client.fossil/shtml"
	"zettelstore.de/client.fossil/sz"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/sx.fossil/sxhtml"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/encoder/szenc"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/meta"
)








|
|
|
|
|
|
|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

package webui

import (
	"net/url"
	"strings"

	"t73f.de/r/sx"
	"t73f.de/r/sxwebs/sxhtml"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/attrs"
	"t73f.de/r/zsc/maps"
	"t73f.de/r/zsc/shtml"
	"t73f.de/r/zsc/sz"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/encoder/szenc"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/meta"
)

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
		if hrefP == nil {
			return obj
		}
		href, ok := sx.GetString(hrefP.Cdr())
		if !ok {
			return obj
		}
		zid, fragment, hasFragment := strings.Cut(string(href), "#")
		u := builder.NewURLBuilder('h').SetZid(api.ZettelID(zid))
		if hasFragment {
			u = u.SetFragment(fragment)
		}
		assoc = assoc.Cons(sx.Cons(shtml.SymAttrHref, sx.String(u.String())))
		return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
	}

	rebind(th, sz.SymLinkZettel, linkZettel)
	rebind(th, sz.SymLinkFound, linkZettel)
	rebind(th, sz.SymLinkBased, func(obj sx.Object) sx.Object {
		attr, assoc, rest := findA(obj)
		if attr == nil {
			return obj
		}
		hrefP := assoc.Assoc(shtml.SymAttrHref)
		if hrefP == nil {
			return obj
		}
		href, ok := sx.GetString(hrefP.Cdr())
		if !ok {
			return obj
		}
		u := builder.NewURLBuilder('/').SetRawLocal(string(href))
		assoc = assoc.Cons(sx.Cons(shtml.SymAttrHref, sx.String(u.String())))
		return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
	})
	rebind(th, sz.SymLinkQuery, func(obj sx.Object) sx.Object {
		attr, assoc, rest := findA(obj)
		if attr == nil {
			return obj
		}
		hrefP := assoc.Assoc(shtml.SymAttrHref)
		if hrefP == nil {
			return obj
		}
		href, ok := sx.GetString(hrefP.Cdr())
		if !ok {
			return obj
		}
		ur, err := url.Parse(string(href))
		if err != nil {
			return obj
		}
		q := ur.Query().Get(api.QueryKeyQuery)
		if q == "" {
			return obj
		}
		u := builder.NewURLBuilder('h').AppendQuery(q)
		assoc = assoc.Cons(sx.Cons(shtml.SymAttrHref, sx.String(u.String())))
		return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
	})
	rebind(th, sz.SymLinkExternal, func(obj sx.Object) sx.Object {
		attr, assoc, rest := findA(obj)
		if attr == nil {
			return obj
		}
		assoc = assoc.Cons(sx.Cons(shtml.SymAttrClass, sx.String("external"))).
			Cons(sx.Cons(shtml.SymAttrTarget, sx.String("_blank"))).
			Cons(sx.Cons(shtml.SymAttrRel, sx.String("noopener noreferrer")))
		return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
	})
	rebind(th, sz.SymEmbed, func(obj sx.Object) sx.Object {
		pair, isPair := sx.GetPair(obj)
		if !isPair || !shtml.SymIMG.IsEqual(pair.Car()) {
			return obj
		}
		attr, isPair := sx.GetPair(pair.Tail().Car())
		if !isPair || !sxhtml.SymAttr.IsEqual(attr.Car()) {
			return obj
		}
		srcP := attr.Tail().Assoc(shtml.SymAttrSrc)
		if srcP == nil {
			return obj
		}
		src, isString := sx.GetString(srcP.Cdr())
		if !isString {
			return obj
		}
		zid := api.ZettelID(src)
		if !zid.IsValid() {
			return obj
		}
		u := builder.NewURLBuilder('z').SetZid(zid)
		imgAttr := attr.Tail().Cons(sx.Cons(shtml.SymAttrSrc, sx.String(u.String()))).Cons(sxhtml.SymAttr)
		return pair.Tail().Tail().Cons(imgAttr).Cons(shtml.SymIMG)
	})

	return &htmlGenerator{
		tx:   szenc.NewTransformer(),
		th:   th,
		lang: lang,







|




|


















|
|















|








|







|
|
|



















|




|







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
		if hrefP == nil {
			return obj
		}
		href, ok := sx.GetString(hrefP.Cdr())
		if !ok {
			return obj
		}
		zid, fragment, hasFragment := strings.Cut(href.GetValue(), "#")
		u := builder.NewURLBuilder('h').SetZid(api.ZettelID(zid))
		if hasFragment {
			u = u.SetFragment(fragment)
		}
		assoc = assoc.Cons(sx.Cons(shtml.SymAttrHref, sx.MakeString(u.String())))
		return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
	}

	rebind(th, sz.SymLinkZettel, linkZettel)
	rebind(th, sz.SymLinkFound, linkZettel)
	rebind(th, sz.SymLinkBased, func(obj sx.Object) sx.Object {
		attr, assoc, rest := findA(obj)
		if attr == nil {
			return obj
		}
		hrefP := assoc.Assoc(shtml.SymAttrHref)
		if hrefP == nil {
			return obj
		}
		href, ok := sx.GetString(hrefP.Cdr())
		if !ok {
			return obj
		}
		u := builder.NewURLBuilder('/')
		assoc = assoc.Cons(sx.Cons(shtml.SymAttrHref, sx.MakeString(u.String()+href.GetValue()[1:])))
		return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
	})
	rebind(th, sz.SymLinkQuery, func(obj sx.Object) sx.Object {
		attr, assoc, rest := findA(obj)
		if attr == nil {
			return obj
		}
		hrefP := assoc.Assoc(shtml.SymAttrHref)
		if hrefP == nil {
			return obj
		}
		href, ok := sx.GetString(hrefP.Cdr())
		if !ok {
			return obj
		}
		ur, err := url.Parse(href.GetValue())
		if err != nil {
			return obj
		}
		q := ur.Query().Get(api.QueryKeyQuery)
		if q == "" {
			return obj
		}
		u := builder.NewURLBuilder('h').AppendQuery(q)
		assoc = assoc.Cons(sx.Cons(shtml.SymAttrHref, sx.MakeString(u.String())))
		return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
	})
	rebind(th, sz.SymLinkExternal, func(obj sx.Object) sx.Object {
		attr, assoc, rest := findA(obj)
		if attr == nil {
			return obj
		}
		assoc = assoc.Cons(sx.Cons(shtml.SymAttrClass, sx.MakeString("external"))).
			Cons(sx.Cons(shtml.SymAttrTarget, sx.MakeString("_blank"))).
			Cons(sx.Cons(shtml.SymAttrRel, sx.MakeString("noopener noreferrer")))
		return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
	})
	rebind(th, sz.SymEmbed, func(obj sx.Object) sx.Object {
		pair, isPair := sx.GetPair(obj)
		if !isPair || !shtml.SymIMG.IsEqual(pair.Car()) {
			return obj
		}
		attr, isPair := sx.GetPair(pair.Tail().Car())
		if !isPair || !sxhtml.SymAttr.IsEqual(attr.Car()) {
			return obj
		}
		srcP := attr.Tail().Assoc(shtml.SymAttrSrc)
		if srcP == nil {
			return obj
		}
		src, isString := sx.GetString(srcP.Cdr())
		if !isString {
			return obj
		}
		zid := api.ZettelID(src.GetValue())
		if !zid.IsValid() {
			return obj
		}
		u := builder.NewURLBuilder('z').SetZid(zid)
		imgAttr := attr.Tail().Cons(sx.Cons(shtml.SymAttrSrc, sx.MakeString(u.String()))).Cons(sxhtml.SymAttr)
		return pair.Tail().Tail().Cons(imgAttr).Cons(shtml.SymIMG)
	})

	return &htmlGenerator{
		tx:   szenc.NewTransformer(),
		th:   th,
		lang: lang,

Changes to web/adapter/webui/htmlmeta.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

package webui

import (
	"context"
	"errors"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/shtml"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/sx.fossil/sxhtml"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func (wui *WebUI) writeHTMLMetaValue(
	key, value string,
	getTextTitle getTextTitleFunc,
	evalMetadata evalMetadataFunc,
	gen *htmlGenerator,
) sx.Object {
	switch kt := meta.Type(key); kt {
	case meta.TypeCredential:
		return sx.String(value)
	case meta.TypeEmpty:
		return sx.String(value)
	case meta.TypeID:
		return wui.transformIdentifier(value, getTextTitle)
	case meta.TypeIDSet:
		return wui.transformIdentifierSet(meta.ListFromValue(value), getTextTitle)
	case meta.TypeNumber:
		return wui.transformKeyValueText(key, value, value)
	case meta.TypeString:
		return sx.String(value)
	case meta.TypeTagSet:
		return wui.transformTagSet(key, meta.ListFromValue(value))
	case meta.TypeTimestamp:
		if ts, ok := meta.TimeValue(value); ok {
			return sx.MakeList(
				sx.MakeSymbol("time"),
				sx.MakeList(
					sxhtml.SymAttr,
					sx.Cons(sx.MakeSymbol("datetime"), sx.String(ts.Format("2006-01-02T15:04:05"))),
				),
				sx.MakeList(sxhtml.SymNoEscape, sx.String(ts.Format("2006-01-02&nbsp;15:04:05"))),
			)
		}
		return sx.Nil()
	case meta.TypeURL:
		return wui.url2html(sx.String(value))
	case meta.TypeWord:
		return wui.transformKeyValueText(key, value, value)
	case meta.TypeZettelmarkup:
		return wui.transformZmkMetadata(value, evalMetadata, gen)
	default:
		return sx.MakeList(shtml.SymSTRONG, sx.String("Unhandled type: "), sx.String(kt.Name))
	}
}

func (wui *WebUI) transformIdentifier(val string, getTextTitle getTextTitleFunc) sx.Object {
	text := sx.String(val)
	zid, err := id.Parse(val)
	if err != nil {
		return text
	}
	title, found := getTextTitle(zid)
	switch {
	case found > 0:
		ub := wui.NewURLBuilder('h').SetZid(zid.ZettelID())
		attrs := sx.Nil()
		if title != "" {
			attrs = attrs.Cons(sx.Cons(shtml.SymAttrTitle, sx.String(title)))
		}
		attrs = attrs.Cons(sx.Cons(shtml.SymAttrHref, sx.String(ub.String()))).Cons(sxhtml.SymAttr)
		return sx.Nil().Cons(sx.String(zid.String())).Cons(attrs).Cons(shtml.SymA)
	case found == 0:
		return sx.MakeList(sx.MakeSymbol("s"), text)
	default: // case found < 0:
		return text
	}
}

func (wui *WebUI) transformIdentifierSet(vals []string, getTextTitle getTextTitleFunc) *sx.Pair {
	if len(vals) == 0 {
		return nil
	}
	const space = sx.String(" ")
	text := make(sx.Vector, 0, 2*len(vals))
	for _, val := range vals {
		text = append(text, space, wui.transformIdentifier(val, getTextTitle))
	}
	return sx.MakeList(text[1:]...).Cons(shtml.SymSPAN)
}

func (wui *WebUI) transformTagSet(key string, tags []string) *sx.Pair {
	if len(tags) == 0 {
		return nil
	}
	const space = sx.String(" ")
	text := make(sx.Vector, 0, 2*len(tags)+2)
	for _, tag := range tags {
		text = append(text, space, wui.transformKeyValueText(key, tag, tag))
	}
	if len(tags) > 1 {
		text = append(text, space, wui.transformKeyValuesText(key, tags, "(all)"))
	}







|
|
|
|
















|

|







|








|

|




|





|




|










|

|
|











|











|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

package webui

import (
	"context"
	"errors"

	"t73f.de/r/sx"
	"t73f.de/r/sxwebs/sxhtml"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/shtml"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func (wui *WebUI) writeHTMLMetaValue(
	key, value string,
	getTextTitle getTextTitleFunc,
	evalMetadata evalMetadataFunc,
	gen *htmlGenerator,
) sx.Object {
	switch kt := meta.Type(key); kt {
	case meta.TypeCredential:
		return sx.MakeString(value)
	case meta.TypeEmpty:
		return sx.MakeString(value)
	case meta.TypeID:
		return wui.transformIdentifier(value, getTextTitle)
	case meta.TypeIDSet:
		return wui.transformIdentifierSet(meta.ListFromValue(value), getTextTitle)
	case meta.TypeNumber:
		return wui.transformKeyValueText(key, value, value)
	case meta.TypeString:
		return sx.MakeString(value)
	case meta.TypeTagSet:
		return wui.transformTagSet(key, meta.ListFromValue(value))
	case meta.TypeTimestamp:
		if ts, ok := meta.TimeValue(value); ok {
			return sx.MakeList(
				sx.MakeSymbol("time"),
				sx.MakeList(
					sxhtml.SymAttr,
					sx.Cons(sx.MakeSymbol("datetime"), sx.MakeString(ts.Format("2006-01-02T15:04:05"))),
				),
				sx.MakeList(sxhtml.SymNoEscape, sx.MakeString(ts.Format("2006-01-02&nbsp;15:04:05"))),
			)
		}
		return sx.Nil()
	case meta.TypeURL:
		return wui.url2html(sx.MakeString(value))
	case meta.TypeWord:
		return wui.transformKeyValueText(key, value, value)
	case meta.TypeZettelmarkup:
		return wui.transformZmkMetadata(value, evalMetadata, gen)
	default:
		return sx.MakeList(shtml.SymSTRONG, sx.MakeString("Unhandled type: "), sx.MakeString(kt.Name))
	}
}

func (wui *WebUI) transformIdentifier(val string, getTextTitle getTextTitleFunc) sx.Object {
	text := sx.MakeString(val)
	zid, err := id.Parse(val)
	if err != nil {
		return text
	}
	title, found := getTextTitle(zid)
	switch {
	case found > 0:
		ub := wui.NewURLBuilder('h').SetZid(zid.ZettelID())
		attrs := sx.Nil()
		if title != "" {
			attrs = attrs.Cons(sx.Cons(shtml.SymAttrTitle, sx.MakeString(title)))
		}
		attrs = attrs.Cons(sx.Cons(shtml.SymAttrHref, sx.MakeString(ub.String()))).Cons(sxhtml.SymAttr)
		return sx.Nil().Cons(sx.MakeString(zid.String())).Cons(attrs).Cons(shtml.SymA)
	case found == 0:
		return sx.MakeList(sx.MakeSymbol("s"), text)
	default: // case found < 0:
		return text
	}
}

func (wui *WebUI) transformIdentifierSet(vals []string, getTextTitle getTextTitleFunc) *sx.Pair {
	if len(vals) == 0 {
		return nil
	}
	var space = sx.MakeString(" ")
	text := make(sx.Vector, 0, 2*len(vals))
	for _, val := range vals {
		text = append(text, space, wui.transformIdentifier(val, getTextTitle))
	}
	return sx.MakeList(text[1:]...).Cons(shtml.SymSPAN)
}

func (wui *WebUI) transformTagSet(key string, tags []string) *sx.Pair {
	if len(tags) == 0 {
		return nil
	}
	var space = sx.MakeString(" ")
	text := make(sx.Vector, 0, 2*len(tags)+2)
	for _, tag := range tags {
		text = append(text, space, wui.transformKeyValueText(key, tag, tag))
	}
	if len(tags) > 1 {
		text = append(text, space, wui.transformKeyValuesText(key, tags, "(all)"))
	}
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
}

func buildHref(ub *api.URLBuilder, text string) *sx.Pair {
	return sx.MakeList(
		shtml.SymA,
		sx.MakeList(
			sxhtml.SymAttr,
			sx.Cons(shtml.SymAttrHref, sx.String(ub.String())),
		),
		sx.String(text),
	)
}

type evalMetadataFunc = func(string) ast.InlineSlice

func createEvalMetadataFunc(ctx context.Context, evaluate *usecase.Evaluate) evalMetadataFunc {
	return func(value string) ast.InlineSlice { return evaluate.RunMetadata(ctx, value) }







|

|







137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
}

func buildHref(ub *api.URLBuilder, text string) *sx.Pair {
	return sx.MakeList(
		shtml.SymA,
		sx.MakeList(
			sxhtml.SymAttr,
			sx.Cons(shtml.SymAttrHref, sx.MakeString(ub.String())),
		),
		sx.MakeString(text),
	)
}

type evalMetadataFunc = func(string) ast.InlineSlice

func createEvalMetadataFunc(ctx context.Context, evaluate *usecase.Evaluate) evalMetadataFunc {
	return func(value string) ast.InlineSlice { return evaluate.RunMetadata(ctx, value) }

Changes to web/adapter/webui/lists.go.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
	"io"
	"net/http"
	"net/url"
	"slices"
	"strconv"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/shtml"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/sx.fossil/sxhtml"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoding/atom"
	"zettelstore.de/z/encoding/rss"
	"zettelstore.de/z/encoding/xml"
	"zettelstore.de/z/evaluator"
	"zettelstore.de/z/query"
	"zettelstore.de/z/usecase"







|
|
|
|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
	"io"
	"net/http"
	"net/url"
	"slices"
	"strconv"
	"strings"

	"t73f.de/r/sx"
	"t73f.de/r/sxwebs/sxhtml"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/shtml"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/encoding/atom"
	"zettelstore.de/z/encoding/rss"
	"zettelstore.de/z/encoding/xml"
	"zettelstore.de/z/evaluator"
	"zettelstore.de/z/query"
	"zettelstore.de/z/usecase"
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

		user := server.GetUser(ctx)
		env, rb := wui.createRenderEnv(
			ctx, "list",
			wui.rtConfig.Get(ctx, nil, api.KeyLang),
			wui.rtConfig.GetSiteName(), user)
		if q == nil {
			rb.bindString("heading", sx.String(wui.rtConfig.GetSiteName()))
		} else {
			var sb strings.Builder
			q.PrintHuman(&sb)
			rb.bindString("heading", sx.String(sb.String()))
		}
		rb.bindString("query-value", sx.String(q.String()))
		if tzl := q.GetMetaValues(api.KeyTags, false); len(tzl) > 0 {
			sxTzl, sxNoTzl := wui.transformTagZettelList(ctx, tagZettel, tzl)
			if !sx.IsNil(sxTzl) {
				rb.bindString("tag-zettel", sxTzl)
			}
			if !sx.IsNil(sxNoTzl) && wui.canCreate(ctx, user) {
				rb.bindString("create-tag-zettel", sxNoTzl)







|



|

|







94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

		user := server.GetUser(ctx)
		env, rb := wui.createRenderEnv(
			ctx, "list",
			wui.rtConfig.Get(ctx, nil, api.KeyLang),
			wui.rtConfig.GetSiteName(), user)
		if q == nil {
			rb.bindString("heading", sx.MakeString(wui.rtConfig.GetSiteName()))
		} else {
			var sb strings.Builder
			q.PrintHuman(&sb)
			rb.bindString("heading", sx.MakeString(sb.String()))
		}
		rb.bindString("query-value", sx.MakeString(q.String()))
		if tzl := q.GetMetaValues(api.KeyTags, false); len(tzl) > 0 {
			sxTzl, sxNoTzl := wui.transformTagZettelList(ctx, tagZettel, tzl)
			if !sx.IsNil(sxTzl) {
				rb.bindString("tag-zettel", sxTzl)
			}
			if !sx.IsNil(sxNoTzl) && wui.canCreate(ctx, user) {
				rb.bindString("create-tag-zettel", sxNoTzl)
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
		seed, found := q.GetSeed()
		if found {
			apiURL = apiURL.AppendKVQuery(api.QueryKeySeed, strconv.Itoa(seed))
		} else {
			seed = 0
		}
		if len(metaSeq) > 0 {
			rb.bindString("plain-url", sx.String(apiURL.String()))
			rb.bindString("data-url", sx.String(apiURL.AppendKVQuery(api.QueryKeyEncoding, api.EncodingData).String()))
			if wui.canCreate(ctx, user) {
				rb.bindString("create-url", sx.String(wui.createNewURL))
				rb.bindString("seed", sx.Int64(seed))
			}
		}
		if rb.err == nil {
			err = wui.renderSxnTemplate(ctx, w, id.ListTemplateZid, env)
		} else {
			err = rb.err







|
|

|







131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
		seed, found := q.GetSeed()
		if found {
			apiURL = apiURL.AppendKVQuery(api.QueryKeySeed, strconv.Itoa(seed))
		} else {
			seed = 0
		}
		if len(metaSeq) > 0 {
			rb.bindString("plain-url", sx.MakeString(apiURL.String()))
			rb.bindString("data-url", sx.MakeString(apiURL.AppendKVQuery(api.QueryKeyEncoding, api.EncodingData).String()))
			if wui.canCreate(ctx, user) {
				rb.bindString("create-url", sx.MakeString(wui.createNewURL))
				rb.bindString("seed", sx.Int64(seed))
			}
		}
		if rb.err == nil {
			err = wui.renderSxnTemplate(ctx, w, id.ListTemplateZid, env)
		} else {
			err = rb.err
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
}

func (wui *WebUI) prependZettelLink(sxZtl *sx.Pair, name string, u *api.URLBuilder) *sx.Pair {
	link := sx.MakeList(
		shtml.SymA,
		sx.MakeList(
			sxhtml.SymAttr,
			sx.Cons(shtml.SymAttrHref, sx.String(u.String())),
		),
		sx.String(name),
	)
	if sxZtl != nil {
		sxZtl = sxZtl.Cons(sx.String(", "))
	}
	return sxZtl.Cons(link)
}

func (wui *WebUI) renderRSS(ctx context.Context, w http.ResponseWriter, q *query.Query, ml []*meta.Meta) {
	var rssConfig rss.Configuration
	rssConfig.Setup(ctx, wui.rtConfig)







|

|


|







183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
}

func (wui *WebUI) prependZettelLink(sxZtl *sx.Pair, name string, u *api.URLBuilder) *sx.Pair {
	link := sx.MakeList(
		shtml.SymA,
		sx.MakeList(
			sxhtml.SymAttr,
			sx.Cons(shtml.SymAttrHref, sx.MakeString(u.String())),
		),
		sx.MakeString(name),
	)
	if sxZtl != nil {
		sxZtl = sxZtl.Cons(sx.MakeString(", "))
	}
	return sxZtl.Cons(link)
}

func (wui *WebUI) renderRSS(ctx context.Context, w http.ResponseWriter, q *query.Query, ml []*meta.Meta) {
	var rssConfig rss.Configuration
	rssConfig.Setup(ctx, wui.rtConfig)

Changes to web/adapter/webui/login.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

package webui

import (
	"context"
	"net/http"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/zettel/id"
)

// MakeGetLoginOutHandler creates a new HTTP handler to display the HTML login view,







|
|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

package webui

import (
	"context"
	"net/http"

	"t73f.de/r/sx"
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/zettel/id"
)

// MakeGetLoginOutHandler creates a new HTTP handler to display the HTML login view,

Changes to web/adapter/webui/rename_zettel.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package webui

import (
	"fmt"
	"net/http"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/id"
)








|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package webui

import (
	"fmt"
	"net/http"
	"strings"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/box"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/id"
)

Changes to web/adapter/webui/response.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package webui

import (
	"net/http"

	"zettelstore.de/client.fossil/api"
)

func (wui *WebUI) redirectFound(w http.ResponseWriter, r *http.Request, ub *api.URLBuilder) {
	us := ub.String()
	wui.log.Debug().Str("uri", us).Msg("redirect")
	http.Redirect(w, r, us, http.StatusFound)
}







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package webui

import (
	"net/http"

	"t73f.de/r/zsc/api"
)

func (wui *WebUI) redirectFound(w http.ResponseWriter, r *http.Request, ub *api.URLBuilder) {
	us := ub.String()
	wui.log.Debug().Str("uri", us).Msg("redirect")
	http.Redirect(w, r, us, http.StatusFound)
}

Changes to web/adapter/webui/sxn_code.go.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package webui

import (
	"context"
	"fmt"
	"io"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/sx.fossil/sxeval"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func (wui *WebUI) loadAllSxnCodeZettel(ctx context.Context) (id.Digraph, *sxeval.Binding, error) {
	// getMeta MUST currently use GetZettel, because GetMeta just uses the
	// Index, which might not be current.







|
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package webui

import (
	"context"
	"fmt"
	"io"

	"t73f.de/r/sx/sxeval"
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func (wui *WebUI) loadAllSxnCodeZettel(ctx context.Context) (id.Digraph, *sxeval.Binding, error) {
	// getMeta MUST currently use GetZettel, because GetMeta just uses the
	// Index, which might not be current.

Changes to web/adapter/webui/template.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
import (
	"bytes"
	"context"
	"fmt"
	"net/http"
	"net/url"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/shtml"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/sx.fossil/sxbuiltins"
	"zettelstore.de/sx.fossil/sxeval"
	"zettelstore.de/sx.fossil/sxhtml"
	"zettelstore.de/sx.fossil/sxreader"
	"zettelstore.de/z/box"
	"zettelstore.de/z/collect"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func (wui *WebUI) createRenderBinding() *sxeval.Binding {
	root := sxeval.MakeRootBinding(len(specials) + len(builtins) + 3)
	for _, syntax := range specials {
		root.BindSpecial(syntax)
	}
	for _, b := range builtins {
		root.BindBuiltin(b)
	}


	root.BindBuiltin(&sxeval.Builtin{
		Name:     "url-to-html",
		MinArity: 1,
		MaxArity: 1,
		TestPure: sxeval.AssertPure,
		Fn: func(_ *sxeval.Environment, args sx.Vector) (sx.Object, error) {
			text, err := sxbuiltins.GetString(args, 0)
			if err != nil {
				return nil, err
			}
			return wui.url2html(text), nil
		},
	})
	root.BindBuiltin(&sxeval.Builtin{
		Name:     "zid-content-path",
		MinArity: 1,
		MaxArity: 1,
		TestPure: sxeval.AssertPure,
		Fn: func(_ *sxeval.Environment, args sx.Vector) (sx.Object, error) {
			s, err := sxbuiltins.GetString(args, 0)
			if err != nil {
				return nil, err
			}
			zid, err := id.Parse(string(s))
			if err != nil {
				return nil, fmt.Errorf("parsing zettel identifier %q: %w", s, err)
			}
			ub := wui.NewURLBuilder('z').SetZid(zid.ZettelID())
			return sx.String(ub.String()), nil
		},
	})
	root.BindBuiltin(&sxeval.Builtin{
		Name:     "query->url",
		MinArity: 1,
		MaxArity: 1,
		TestPure: sxeval.AssertPure,
		Fn: func(_ *sxeval.Environment, args sx.Vector) (sx.Object, error) {
			qs, err := sxbuiltins.GetString(args, 0)
			if err != nil {
				return nil, err
			}
			u := wui.NewURLBuilder('h').AppendQuery(string(qs))
			return sx.String(u.String()), nil
		},
	})
	root.Freeze()
	return root
}

var (
	specials = []*sxeval.Special{
		&sxbuiltins.QuoteS, &sxbuiltins.QuasiquoteS, // quote, quasiquote
		&sxbuiltins.UnquoteS, &sxbuiltins.UnquoteSplicingS, // unquote, unquote-splicing
		&sxbuiltins.DefVarS, &sxbuiltins.DefConstS, // defvar, defconst
		&sxbuiltins.DefunS, &sxbuiltins.LambdaS, // defun, lambda
		&sxbuiltins.SetXS,     // set!
		&sxbuiltins.IfS,       // if
		&sxbuiltins.BeginS,    // begin
		&sxbuiltins.DefMacroS, // defmacro

	}
	builtins = []*sxeval.Builtin{
		&sxbuiltins.Equal,                // =
		&sxbuiltins.NumGreater,           // >
		&sxbuiltins.NullP,                // null?
		&sxbuiltins.PairP,                // pair?
		&sxbuiltins.Car, &sxbuiltins.Cdr, // car, cdr







|
|
|
|
|
|
|



















>
>





|
|











|
|



|

|


|







|
|



|
|










|





>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import (
	"bytes"
	"context"
	"fmt"
	"net/http"
	"net/url"

	"t73f.de/r/sx"
	"t73f.de/r/sx/sxbuiltins"
	"t73f.de/r/sx/sxeval"
	"t73f.de/r/sx/sxreader"
	"t73f.de/r/sxwebs/sxhtml"
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/shtml"
	"zettelstore.de/z/box"
	"zettelstore.de/z/collect"
	"zettelstore.de/z/config"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/web/adapter"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

func (wui *WebUI) createRenderBinding() *sxeval.Binding {
	root := sxeval.MakeRootBinding(len(specials) + len(builtins) + 3)
	for _, syntax := range specials {
		root.BindSpecial(syntax)
	}
	for _, b := range builtins {
		root.BindBuiltin(b)
	}
	_ = root.Bind(sx.MakeSymbol("NIL"), sx.Nil())
	_ = root.Bind(sx.MakeSymbol("T"), sx.MakeSymbol("T"))
	root.BindBuiltin(&sxeval.Builtin{
		Name:     "url-to-html",
		MinArity: 1,
		MaxArity: 1,
		TestPure: sxeval.AssertPure,
		Fn1: func(_ *sxeval.Environment, arg sx.Object) (sx.Object, error) {
			text, err := sxbuiltins.GetString(arg, 0)
			if err != nil {
				return nil, err
			}
			return wui.url2html(text), nil
		},
	})
	root.BindBuiltin(&sxeval.Builtin{
		Name:     "zid-content-path",
		MinArity: 1,
		MaxArity: 1,
		TestPure: sxeval.AssertPure,
		Fn1: func(_ *sxeval.Environment, arg sx.Object) (sx.Object, error) {
			s, err := sxbuiltins.GetString(arg, 0)
			if err != nil {
				return nil, err
			}
			zid, err := id.Parse(s.GetValue())
			if err != nil {
				return nil, fmt.Errorf("parsing zettel identifier %q: %w", s.GetValue(), err)
			}
			ub := wui.NewURLBuilder('z').SetZid(zid.ZettelID())
			return sx.MakeString(ub.String()), nil
		},
	})
	root.BindBuiltin(&sxeval.Builtin{
		Name:     "query->url",
		MinArity: 1,
		MaxArity: 1,
		TestPure: sxeval.AssertPure,
		Fn1: func(_ *sxeval.Environment, arg sx.Object) (sx.Object, error) {
			qs, err := sxbuiltins.GetString(arg, 0)
			if err != nil {
				return nil, err
			}
			u := wui.NewURLBuilder('h').AppendQuery(qs.GetValue())
			return sx.MakeString(u.String()), nil
		},
	})
	root.Freeze()
	return root
}

var (
	specials = []*sxeval.Special{
		&sxbuiltins.QuoteS, &sxbuiltins.QuasiquoteS, // quote, quasiquote
		&sxbuiltins.UnquoteS, &sxbuiltins.UnquoteSplicingS, // unquote, unquote-splicing
		&sxbuiltins.DefVarS,                     // defvar
		&sxbuiltins.DefunS, &sxbuiltins.LambdaS, // defun, lambda
		&sxbuiltins.SetXS,     // set!
		&sxbuiltins.IfS,       // if
		&sxbuiltins.BeginS,    // begin
		&sxbuiltins.DefMacroS, // defmacro
		&sxbuiltins.LetS,      // let
	}
	builtins = []*sxeval.Builtin{
		&sxbuiltins.Equal,                // =
		&sxbuiltins.NumGreater,           // >
		&sxbuiltins.NullP,                // null?
		&sxbuiltins.PairP,                // pair?
		&sxbuiltins.Car, &sxbuiltins.Cdr, // car, cdr
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
		&sxbuiltins.Defined,        // defined?
		&sxbuiltins.CurrentBinding, // current-binding
		&sxbuiltins.BindingLookup,  // binding-lookup
	}
)

func (wui *WebUI) url2html(text sx.String) sx.Object {
	if u, errURL := url.Parse(string(text)); errURL == nil {
		if us := u.String(); us != "" {
			return sx.MakeList(
				shtml.SymA,
				sx.MakeList(
					sxhtml.SymAttr,
					sx.Cons(shtml.SymAttrHref, sx.String(us)),
					sx.Cons(shtml.SymAttrTarget, sx.String("_blank")),
					sx.Cons(shtml.SymAttrRel, sx.String("noopener noreferrer")),
				),
				text)
		}
	}
	return text
}








|





|
|
|







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
		&sxbuiltins.Defined,        // defined?
		&sxbuiltins.CurrentBinding, // current-binding
		&sxbuiltins.BindingLookup,  // binding-lookup
	}
)

func (wui *WebUI) url2html(text sx.String) sx.Object {
	if u, errURL := url.Parse(text.GetValue()); errURL == nil {
		if us := u.String(); us != "" {
			return sx.MakeList(
				shtml.SymA,
				sx.MakeList(
					sxhtml.SymAttr,
					sx.Cons(shtml.SymAttrHref, sx.MakeString(us)),
					sx.Cons(shtml.SymAttrTarget, sx.MakeString("_blank")),
					sx.Cons(shtml.SymAttrRel, sx.MakeString("noopener noreferrer")),
				),
				text)
		}
	}
	return text
}

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

// createRenderEnv creates a new environment and populates it with all relevant data for the base template.
func (wui *WebUI) createRenderEnv(ctx context.Context, name, lang, title string, user *meta.Meta) (*sxeval.Binding, renderBinder) {
	userIsValid, userZettelURL, userIdent := wui.getUserRenderData(user)
	parentEnv, err := wui.getParentEnv(ctx)
	bind := parentEnv.MakeChildBinding(name, 128)
	rb := makeRenderBinder(bind, err)
	rb.bindString("lang", sx.String(lang))
	rb.bindString("css-base-url", sx.String(wui.cssBaseURL))
	rb.bindString("css-user-url", sx.String(wui.cssUserURL))
	rb.bindString("title", sx.String(title))
	rb.bindString("home-url", sx.String(wui.homeURL))
	rb.bindString("with-auth", sx.MakeBoolean(wui.withAuth))
	rb.bindString("user-is-valid", sx.MakeBoolean(userIsValid))
	rb.bindString("user-zettel-url", sx.String(userZettelURL))
	rb.bindString("user-ident", sx.String(userIdent))
	rb.bindString("login-url", sx.String(wui.loginURL))
	rb.bindString("logout-url", sx.String(wui.logoutURL))
	rb.bindString("list-zettel-url", sx.String(wui.listZettelURL))
	rb.bindString("list-roles-url", sx.String(wui.listRolesURL))
	rb.bindString("list-tags-url", sx.String(wui.listTagsURL))
	if wui.canRefresh(user) {
		rb.bindString("refresh-url", sx.String(wui.refreshURL))
	}
	rb.bindString("new-zettel-links", wui.fetchNewTemplatesSxn(ctx, user))
	rb.bindString("search-url", sx.String(wui.searchURL))
	rb.bindString("query-key-query", sx.String(api.QueryKeyQuery))
	rb.bindString("query-key-seed", sx.String(api.QueryKeySeed))
	rb.bindString("FOOTER", wui.calculateFooterSxn(ctx)) // TODO: use real footer
	rb.bindString("debug-mode", sx.MakeBoolean(wui.debug))
	rb.bindSymbol(symMetaHeader, sx.Nil())
	rb.bindSymbol(symDetail, sx.Nil())
	return bind, rb
}








|
|
|
|
|


|
|
|
|
|
|
|

|


|
|
|







166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200

// createRenderEnv creates a new environment and populates it with all relevant data for the base template.
func (wui *WebUI) createRenderEnv(ctx context.Context, name, lang, title string, user *meta.Meta) (*sxeval.Binding, renderBinder) {
	userIsValid, userZettelURL, userIdent := wui.getUserRenderData(user)
	parentEnv, err := wui.getParentEnv(ctx)
	bind := parentEnv.MakeChildBinding(name, 128)
	rb := makeRenderBinder(bind, err)
	rb.bindString("lang", sx.MakeString(lang))
	rb.bindString("css-base-url", sx.MakeString(wui.cssBaseURL))
	rb.bindString("css-user-url", sx.MakeString(wui.cssUserURL))
	rb.bindString("title", sx.MakeString(title))
	rb.bindString("home-url", sx.MakeString(wui.homeURL))
	rb.bindString("with-auth", sx.MakeBoolean(wui.withAuth))
	rb.bindString("user-is-valid", sx.MakeBoolean(userIsValid))
	rb.bindString("user-zettel-url", sx.MakeString(userZettelURL))
	rb.bindString("user-ident", sx.MakeString(userIdent))
	rb.bindString("login-url", sx.MakeString(wui.loginURL))
	rb.bindString("logout-url", sx.MakeString(wui.logoutURL))
	rb.bindString("list-zettel-url", sx.MakeString(wui.listZettelURL))
	rb.bindString("list-roles-url", sx.MakeString(wui.listRolesURL))
	rb.bindString("list-tags-url", sx.MakeString(wui.listTagsURL))
	if wui.canRefresh(user) {
		rb.bindString("refresh-url", sx.MakeString(wui.refreshURL))
	}
	rb.bindString("new-zettel-links", wui.fetchNewTemplatesSxn(ctx, user))
	rb.bindString("search-url", sx.MakeString(wui.searchURL))
	rb.bindString("query-key-query", sx.MakeString(api.QueryKeyQuery))
	rb.bindString("query-key-seed", sx.MakeString(api.QueryKeySeed))
	rb.bindString("FOOTER", wui.calculateFooterSxn(ctx)) // TODO: use real footer
	rb.bindString("debug-mode", sx.MakeBoolean(wui.debug))
	rb.bindSymbol(symMetaHeader, sx.Nil())
	rb.bindSymbol(symDetail, sx.Nil())
	return bind, rb
}

217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
}
func (rb *renderBinder) bindSymbol(sym *sx.Symbol, obj sx.Object) {
	if rb.err == nil {
		rb.err = rb.binding.Bind(sym, obj)
	}
}
func (rb *renderBinder) bindKeyValue(key string, value string) {
	rb.bindString("meta-"+key, sx.String(value))
	if kt := meta.Type(key); kt.IsSet {
		rb.bindString("set-meta-"+key, makeStringList(meta.ListFromValue(value)))
	}
}
func (rb *renderBinder) rebindResolved(key, defKey string) {
	if rb.err == nil {
		if obj, found := rb.binding.Resolve(sx.MakeSymbol(key)); found {
			rb.bindString(defKey, obj)
		}
	}
}

func (wui *WebUI) bindCommonZettelData(ctx context.Context, rb *renderBinder, user, m *meta.Meta, content *zettel.Content) {
	strZid := m.Zid.String()
	apiZid := api.ZettelID(strZid)
	newURLBuilder := wui.NewURLBuilder

	rb.bindString("zid", sx.String(strZid))
	rb.bindString("web-url", sx.String(newURLBuilder('h').SetZid(apiZid).String()))
	if content != nil && wui.canWrite(ctx, user, m, *content) {
		rb.bindString("edit-url", sx.String(newURLBuilder('e').SetZid(apiZid).String()))
	}
	rb.bindString("info-url", sx.String(newURLBuilder('i').SetZid(apiZid).String()))
	if wui.canCreate(ctx, user) {
		if content != nil && !content.IsBinary() {
			rb.bindString("copy-url", sx.String(newURLBuilder('c').SetZid(apiZid).AppendKVQuery(queryKeyAction, valueActionCopy).String()))
		}
		rb.bindString("version-url", sx.String(newURLBuilder('c').SetZid(apiZid).AppendKVQuery(queryKeyAction, valueActionVersion).String()))
		rb.bindString("child-url", sx.String(newURLBuilder('c').SetZid(apiZid).AppendKVQuery(queryKeyAction, valueActionChild).String()))
		rb.bindString("folge-url", sx.String(newURLBuilder('c').SetZid(apiZid).AppendKVQuery(queryKeyAction, valueActionFolge).String()))
	}
	if wui.canRename(ctx, user, m) {
		rb.bindString("rename-url", sx.String(newURLBuilder('b').SetZid(apiZid).String()))
	}
	if wui.canDelete(ctx, user, m) {
		rb.bindString("delete-url", sx.String(newURLBuilder('d').SetZid(apiZid).String()))
	}
	if val, found := m.Get(api.KeyUselessFiles); found {
		rb.bindString("useless", sx.Cons(sx.String(val), nil))
	}
	queryContext := strZid + " " + api.ContextDirective
	rb.bindString("context-url", sx.String(newURLBuilder('h').AppendQuery(queryContext).String()))
	queryContext += " " + api.FullDirective
	rb.bindString("context-full-url", sx.String(newURLBuilder('h').AppendQuery(queryContext).String()))
	if wui.canRefresh(user) {
		rb.bindString("reindex-url", sx.String(newURLBuilder('h').AppendQuery(
			strZid+" "+api.IdentDirective+api.ActionSeparator+api.ReIndexAction).String()))
	}

	// Ensure to have title, role, tags, and syntax included as "meta-*"
	rb.bindKeyValue(api.KeyTitle, m.GetDefault(api.KeyTitle, ""))
	rb.bindKeyValue(api.KeyRole, m.GetDefault(api.KeyRole, ""))
	rb.bindKeyValue(api.KeyTags, m.GetDefault(api.KeyTags, ""))
	rb.bindKeyValue(api.KeySyntax, m.GetDefault(api.KeySyntax, meta.DefaultSyntax))
	var metaPairs sx.ListBuilder
	for _, p := range m.ComputedPairs() {
		key, value := p.Key, p.Value
		metaPairs.Add(sx.Cons(sx.String(key), sx.String(value)))
		rb.bindKeyValue(key, value)
	}
	rb.bindString("metapairs", metaPairs.List())
}

func (wui *WebUI) fetchNewTemplatesSxn(ctx context.Context, user *meta.Meta) (lst *sx.Pair) {
	if !wui.canCreate(ctx, user) {







|

















|
|

|

|


|

|
|
|


|


|


|


|

|

|











|







220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
}
func (rb *renderBinder) bindSymbol(sym *sx.Symbol, obj sx.Object) {
	if rb.err == nil {
		rb.err = rb.binding.Bind(sym, obj)
	}
}
func (rb *renderBinder) bindKeyValue(key string, value string) {
	rb.bindString("meta-"+key, sx.MakeString(value))
	if kt := meta.Type(key); kt.IsSet {
		rb.bindString("set-meta-"+key, makeStringList(meta.ListFromValue(value)))
	}
}
func (rb *renderBinder) rebindResolved(key, defKey string) {
	if rb.err == nil {
		if obj, found := rb.binding.Resolve(sx.MakeSymbol(key)); found {
			rb.bindString(defKey, obj)
		}
	}
}

func (wui *WebUI) bindCommonZettelData(ctx context.Context, rb *renderBinder, user, m *meta.Meta, content *zettel.Content) {
	strZid := m.Zid.String()
	apiZid := api.ZettelID(strZid)
	newURLBuilder := wui.NewURLBuilder

	rb.bindString("zid", sx.MakeString(strZid))
	rb.bindString("web-url", sx.MakeString(newURLBuilder('h').SetZid(apiZid).String()))
	if content != nil && wui.canWrite(ctx, user, m, *content) {
		rb.bindString("edit-url", sx.MakeString(newURLBuilder('e').SetZid(apiZid).String()))
	}
	rb.bindString("info-url", sx.MakeString(newURLBuilder('i').SetZid(apiZid).String()))
	if wui.canCreate(ctx, user) {
		if content != nil && !content.IsBinary() {
			rb.bindString("copy-url", sx.MakeString(newURLBuilder('c').SetZid(apiZid).AppendKVQuery(queryKeyAction, valueActionCopy).String()))
		}
		rb.bindString("version-url", sx.MakeString(newURLBuilder('c').SetZid(apiZid).AppendKVQuery(queryKeyAction, valueActionVersion).String()))
		rb.bindString("child-url", sx.MakeString(newURLBuilder('c').SetZid(apiZid).AppendKVQuery(queryKeyAction, valueActionChild).String()))
		rb.bindString("folge-url", sx.MakeString(newURLBuilder('c').SetZid(apiZid).AppendKVQuery(queryKeyAction, valueActionFolge).String()))
	}
	if wui.canRename(ctx, user, m) {
		rb.bindString("rename-url", sx.MakeString(newURLBuilder('b').SetZid(apiZid).String()))
	}
	if wui.canDelete(ctx, user, m) {
		rb.bindString("delete-url", sx.MakeString(newURLBuilder('d').SetZid(apiZid).String()))
	}
	if val, found := m.Get(api.KeyUselessFiles); found {
		rb.bindString("useless", sx.Cons(sx.MakeString(val), nil))
	}
	queryContext := strZid + " " + api.ContextDirective
	rb.bindString("context-url", sx.MakeString(newURLBuilder('h').AppendQuery(queryContext).String()))
	queryContext += " " + api.FullDirective
	rb.bindString("context-full-url", sx.MakeString(newURLBuilder('h').AppendQuery(queryContext).String()))
	if wui.canRefresh(user) {
		rb.bindString("reindex-url", sx.MakeString(newURLBuilder('h').AppendQuery(
			strZid+" "+api.IdentDirective+api.ActionSeparator+api.ReIndexAction).String()))
	}

	// Ensure to have title, role, tags, and syntax included as "meta-*"
	rb.bindKeyValue(api.KeyTitle, m.GetDefault(api.KeyTitle, ""))
	rb.bindKeyValue(api.KeyRole, m.GetDefault(api.KeyRole, ""))
	rb.bindKeyValue(api.KeyTags, m.GetDefault(api.KeyTags, ""))
	rb.bindKeyValue(api.KeySyntax, m.GetDefault(api.KeySyntax, meta.DefaultSyntax))
	var metaPairs sx.ListBuilder
	for _, p := range m.ComputedPairs() {
		key, value := p.Key, p.Value
		metaPairs.Add(sx.Cons(sx.MakeString(key), sx.MakeString(value)))
		rb.bindKeyValue(key, value)
	}
	rb.bindString("metapairs", metaPairs.List())
}

func (wui *WebUI) fetchNewTemplatesSxn(ctx context.Context, user *meta.Meta) (lst *sx.Pair) {
	if !wui.canCreate(ctx, user) {
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
		z, err2 := wui.box.GetZettel(ctx, zid)
		if err2 != nil {
			continue
		}
		if !wui.policy.CanRead(user, z.Meta) {
			continue
		}
		text := sx.String(parser.NormalizedSpacedText(z.Meta.GetTitle()))
		link := sx.String(wui.NewURLBuilder('c').SetZid(zid.ZettelID()).
			AppendKVQuery(queryKeyAction, valueActionNew).String())

		lst = lst.Cons(sx.Cons(text, link))
	}
	return lst
}
func (wui *WebUI) calculateFooterSxn(ctx context.Context) *sx.Pair {







|
|







306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
		z, err2 := wui.box.GetZettel(ctx, zid)
		if err2 != nil {
			continue
		}
		if !wui.policy.CanRead(user, z.Meta) {
			continue
		}
		text := sx.MakeString(parser.NormalizedSpacedText(z.Meta.GetTitle()))
		link := sx.MakeString(wui.NewURLBuilder('c').SetZid(zid.ZettelID()).
			AppendKVQuery(queryKeyAction, valueActionNew).String())

		lst = lst.Cons(sx.Cons(text, link))
	}
	return lst
}
func (wui *WebUI) calculateFooterSxn(ctx context.Context) *sx.Pair {
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
		return err
	}
	if msg := wui.log.Debug(); msg != nil {
		// pageObj.String() can be expensive to calculate.
		msg.Str("page", pageObj.String()).Msg("render")
	}

	gen := sxhtml.NewGenerator(sxhtml.WithNewline)
	var sb bytes.Buffer
	_, err = gen.WriteHTML(&sb, pageObj)
	if err != nil {
		return err
	}
	wui.prepareAndWriteHeader(w, code)
	if _, err = w.Write(sb.Bytes()); err != nil {







|







394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
		return err
	}
	if msg := wui.log.Debug(); msg != nil {
		// pageObj.String() can be expensive to calculate.
		msg.Str("page", pageObj.String()).Msg("render")
	}

	gen := sxhtml.NewGenerator().SetNewline()
	var sb bytes.Buffer
	_, err = gen.WriteHTML(&sb, pageObj)
	if err != nil {
		return err
	}
	wui.prepareAndWriteHeader(w, code)
	if _, err = w.Write(sb.Bytes()); err != nil {
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
	if code == http.StatusInternalServerError {
		wui.log.Error().Msg(err.Error())
	} else {
		wui.log.Debug().Err(err).Msg("reportError")
	}
	user := server.GetUser(ctx)
	env, rb := wui.createRenderEnv(ctx, "error", api.ValueLangEN, "Error", user)
	rb.bindString("heading", sx.String(http.StatusText(code)))
	rb.bindString("message", sx.String(text))
	if rb.err == nil {
		rb.err = wui.renderSxnTemplateStatus(ctx, w, code, id.ErrorTemplateZid, env)
	}
	errSx := rb.err
	if errSx == nil {
		return
	}







|
|







416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
	if code == http.StatusInternalServerError {
		wui.log.Error().Msg(err.Error())
	} else {
		wui.log.Debug().Err(err).Msg("reportError")
	}
	user := server.GetUser(ctx)
	env, rb := wui.createRenderEnv(ctx, "error", api.ValueLangEN, "Error", user)
	rb.bindString("heading", sx.MakeString(http.StatusText(code)))
	rb.bindString("message", sx.MakeString(text))
	if rb.err == nil {
		rb.err = wui.renderSxnTemplateStatus(ctx, w, code, id.ErrorTemplateZid, env)
	}
	errSx := rb.err
	if errSx == nil {
		return
	}
444
445
446
447
448
449
450
451
452
453
454

func makeStringList(sl []string) *sx.Pair {
	if len(sl) == 0 {
		return nil
	}
	result := sx.Nil()
	for i := len(sl) - 1; i >= 0; i-- {
		result = result.Cons(sx.String(sl[i]))
	}
	return result
}







|



447
448
449
450
451
452
453
454
455
456
457

func makeStringList(sl []string) *sx.Pair {
	if len(sl) == 0 {
		return nil
	}
	result := sx.Nil()
	for i := len(sl) - 1; i >= 0; i-- {
		result = result.Cons(sx.MakeString(sl[i]))
	}
	return result
}

Changes to web/adapter/webui/webui.go.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

import (
	"context"
	"net/http"
	"sync"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/sx.fossil"
	"zettelstore.de/sx.fossil/sxeval"
	"zettelstore.de/sx.fossil/sxhtml"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/box"
	"zettelstore.de/z/config"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"







|
|
|
|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

import (
	"context"
	"net/http"
	"sync"
	"time"

	"t73f.de/r/sx"
	"t73f.de/r/sx/sxeval"
	"t73f.de/r/sxwebs/sxhtml"
	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/box"
	"zettelstore.de/z/config"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
		withAuth:      authz.WithAuth(),
		loginURL:      loginoutBase.String(),
		logoutURL:     loginoutBase.AppendKVQuery("logout", "").String(),
		searchURL:     ab.NewURLBuilder('h').String(),
		createNewURL:  ab.NewURLBuilder('c').String(),

		zettelBinding: nil,
		genHTML:       sxhtml.NewGenerator(sxhtml.WithNewline),
	}
	wui.rootBinding = wui.createRenderBinding()
	wui.observe(box.UpdateInfo{Box: mgr, Reason: box.OnReload, Zid: id.Invalid})
	mgr.RegisterObserver(wui.observe)
	return wui
}








|







116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
		withAuth:      authz.WithAuth(),
		loginURL:      loginoutBase.String(),
		logoutURL:     loginoutBase.AppendKVQuery("logout", "").String(),
		searchURL:     ab.NewURLBuilder('h').String(),
		createNewURL:  ab.NewURLBuilder('c').String(),

		zettelBinding: nil,
		genHTML:       sxhtml.NewGenerator().SetNewline(),
	}
	wui.rootBinding = wui.createRenderBinding()
	wui.observe(box.UpdateInfo{Box: mgr, Reason: box.OnReload, Zid: id.Invalid})
	mgr.RegisterObserver(wui.observe)
	return wui
}

Changes to web/content/content.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// It translates syntax values into content types, and vice versa.
package content

import (
	"mime"
	"net/http"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/meta"
)

const (
	UnknownMIME  = "application/octet-stream"
	mimeGIF      = "image/gif"







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// It translates syntax values into content types, and vice versa.
package content

import (
	"mime"
	"net/http"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/zettel"
	"zettelstore.de/z/zettel/meta"
)

const (
	UnknownMIME  = "application/octet-stream"
	mimeGIF      = "image/gif"

Changes to web/server/impl/impl.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package impl

import (
	"context"
	"net/http"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/meta"
)

type myServer struct {







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package impl

import (
	"context"
	"net/http"
	"time"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/web/server"
	"zettelstore.de/z/zettel/meta"
)

type myServer struct {

Changes to web/server/impl/router.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"io"
	"net/http"
	"regexp"
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/web/server"
)

type (







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"io"
	"net/http"
	"regexp"
	"strings"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/kernel"
	"zettelstore.de/z/logger"
	"zettelstore.de/z/web/server"
)

type (

Changes to web/server/server.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package server

import (
	"context"
	"net/http"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// UserRetriever allows to retrieve user data based on a given zettel identifier.
type UserRetriever interface {
	GetUser(ctx context.Context, zid id.Zid, ident string) (*meta.Meta, error)







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package server

import (
	"context"
	"net/http"
	"time"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

// UserRetriever allows to retrieve user data based on a given zettel identifier.
type UserRetriever interface {
	GetUser(ctx context.Context, zid id.Zid, ident string) (*meta.Meta, error)

Changes to www/build.md.

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
* `go run tools/devtools/devtools.go` install all needed software (see above).
* `go run tools/htmllint/htmllint.go [URL]` checks all generated HTML of a
  Zettelstore accessible at the given URL (default: http://localhost:23123).
* `go run tools/testapi/testapi.go` tests the API against a running
  Zettelstore, which is started automatically.

## A note on the use of Fossil
Zettelstore is managed by the Fossil version control system.
Fossil is an alternative to the ubiquitous Git version control system.
However, Go seems to prefer Git and popular platforms that just support Git.

Some dependencies of Zettelstore, namely [Zettelstore
client](https://zettelstore.de/client) and [sx](https://zettelstore.de/sx), are
also managed by Fossil.
Depending on your development setup, some error messages might occur.


If the error message mentions an environment variable called `GOVCS` you should
set it to the value `GOVCS=zettelstore.de:fossil` (alternatively more generous
to `GOVCS=*:all`).
Since the Go build system is coupled with Git and some special platforms, you
allow ot to download a Fossil repository from the host `zettelstore.de`.
The build tool set `GOVCS` to the right value, but you may use other `go`
commands that try to download a Fossil repository.

On some operating systems, namely Termux on Android, an error message might
state that an user cannot be determined (`cannot determine user`).
In this case, Fossil is allowed to download the repository, but cannot
associate it with an user name.
Set the environment variable `USER` to any user name, like:
`USER=nobody go run tools/build.go build`.







|
|
|


|
<
|
>



<
|
|
|
|


|
|
<
|

67
68
69
70
71
72
73
74
75
76
77
78
79

80
81
82
83
84

85
86
87
88
89
90
91
92

93
94
* `go run tools/devtools/devtools.go` install all needed software (see above).
* `go run tools/htmllint/htmllint.go [URL]` checks all generated HTML of a
  Zettelstore accessible at the given URL (default: http://localhost:23123).
* `go run tools/testapi/testapi.go` tests the API against a running
  Zettelstore, which is started automatically.

## A note on the use of Fossil
Zettelstore is managed by the Fossil version control system. Fossil is an
alternative to the ubiquitous Git version control system. However, Go seems to
prefer Git and popular platforms that just support Git.

Some dependencies of Zettelstore, namely [Zettelstore
client](https://t73f.de/r/zsc) and [Sx](https://t73f.de/r/sx), are also

managed by Fossil. Depending on your development setup, some error messages
might occur.

If the error message mentions an environment variable called `GOVCS` you should
set it to the value `GOVCS=zettelstore.de:fossil` (alternatively more generous

to `GOVCS=*:all`). Since the Go build system is coupled with Git and some
special platforms, you allow ot to download a Fossil repository from the host
`zettelstore.de`. The build tool set `GOVCS` to the right value, but you may
use other `go` commands that try to download a Fossil repository.

On some operating systems, namely Termux on Android, an error message might
state that an user cannot be determined (`cannot determine user`). In this
case, Fossil is allowed to download the repository, but cannot associate it

with an user name. Set the environment variable `USER` to any user name, like:
`USER=nobody go run tools/build.go build`.

Changes to www/changes.wiki.

1
2
3
4











5
6
7
8
9
10
11
<title>Change Log</title>

<a id="0_18"></a>
<h2>Changes for Version 0.18.0 (pending)</h2>












<a id="0_17"></a>
<h2>Changes for Version 0.17.0 (2024-03-04)</h2>
  *  Context search operates only on explicit references. Add the directive
     <code>FULL</code> to follow zettel tags additionally.
     (breaking)
  *  Context cost calculation has been changed. Prepare to retrieve different




>
>
>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<title>Change Log</title>

<a id="0_18"></a>
<h2>Changes for Version 0.18.0 (pending)</h2>
  *  Remove Sx macro <code>defunconst</code>. Use <code>defun</code> instead.
     (breaking: webui)
  *  Update Sx prelude: make macros more robust / more general. This might
     break your code in the future.
     (minor: webui)
  *  Add computed zettel &ldquo;Zettelstore Memory&rdquo; with zettel
     identifier <code>00000000000008</code>. It shows some statistics about
     memory usage.
     (minor: webui)
  *  Zettelstore client is now Go package t73f.de/r/zsc.
     (minor)

<a id="0_17"></a>
<h2>Changes for Version 0.17.0 (2024-03-04)</h2>
  *  Context search operates only on explicit references. Add the directive
     <code>FULL</code> to follow zettel tags additionally.
     (breaking)
  *  Context cost calculation has been changed. Prepare to retrieve different
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
     syntax will not be supported. The reason is the collision with URLs that
     also contain the characters <code>//</code>. The ZMK encoding of a zettel
     may help with the transition
     (<code>/v/{ZettelID}?_part=zettel&amp;_enc=zmk</code>, on the Info page of
     each zettel in the WebUI). Additionally, all deprecated uses of
     <code>//</code> will be rendered with a dashed box within the WebUI.
     (breaking: Zettelmarkup).
  *  API client software is now a [https://zettelstore.de/client/|separate]
     project.
     (breaking)
  *  Initial support for HTTP security headers (Content-Security-Policy,
     Permissions-Policy, Referrer-Policy, X-Content-Type-Options,
     X-Frame-Options). Header values are currently some constant values.
     (possibly breaking: api, webui)
  *  Remove visual Zettelmarkup (bold, striketrough). Semantic Zettelmarkup
     (strong, delete) is still allowed and replaces the visual elements







|
<







795
796
797
798
799
800
801
802

803
804
805
806
807
808
809
     syntax will not be supported. The reason is the collision with URLs that
     also contain the characters <code>//</code>. The ZMK encoding of a zettel
     may help with the transition
     (<code>/v/{ZettelID}?_part=zettel&amp;_enc=zmk</code>, on the Info page of
     each zettel in the WebUI). Additionally, all deprecated uses of
     <code>//</code> will be rendered with a dashed box within the WebUI.
     (breaking: Zettelmarkup).
  *  API client software is now a separate project.

     (breaking)
  *  Initial support for HTTP security headers (Content-Security-Policy,
     Permissions-Policy, Referrer-Policy, X-Content-Type-Options,
     X-Frame-Options). Header values are currently some constant values.
     (possibly breaking: api, webui)
  *  Remove visual Zettelmarkup (bold, striketrough). Semantic Zettelmarkup
     (strong, delete) is still allowed and replaces the visual elements

Changes to www/index.wiki.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
To get an initial impression, take a look at the
[https://zettelstore.de/manual/|manual]. It is a live example of the
zettelstore software, running in read-only mode.

The software, including the manual, is licensed under the
[/file?name=LICENSE.txt&ci=trunk|European Union Public License 1.2 (or later)].

  *  [https://zettelstore.de/client|Zettelstore Client] provides client
     software to access Zettelstore via its API more easily.
  *  [https://zettelstore.de/contrib|Zettelstore Contrib] contains contributed
     software, which often connects to Zettelstore via its API. Some of the
     software packages may be experimental.
  *  [https://zettelstore.de/sx|Sx] provides an evaluator for symbolic
     expressions, which is unsed for HTML templates and more.

[https://mastodon.social/tags/Zettelstore|Stay tuned]&nbsp;&hellip;
<hr>
<h3>Latest Release: 0.17.0 (2024-03-04)</h3>
  *  [./download.wiki|Download]
  *  [./changes.wiki#0_17|Change summary]
  *  [/timeline?p=v0.17.0&bt=v0.16.0&y=ci|Check-ins for version 0.17.0],







|
|



|
|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
To get an initial impression, take a look at the
[https://zettelstore.de/manual/|manual]. It is a live example of the
zettelstore software, running in read-only mode.

The software, including the manual, is licensed under the
[/file?name=LICENSE.txt&ci=trunk|European Union Public License 1.2 (or later)].

  *  [https://t73f.de/r/zsc|Zettelstore Client] provides client software to
     access Zettelstore via its API more easily.
  *  [https://zettelstore.de/contrib|Zettelstore Contrib] contains contributed
     software, which often connects to Zettelstore via its API. Some of the
     software packages may be experimental.
  *  [https://t73f.de/r/sx|Sx] provides an evaluator for symbolic
     expressions, which is used for HTML templates and more.

[https://mastodon.social/tags/Zettelstore|Stay tuned]&nbsp;&hellip;
<hr>
<h3>Latest Release: 0.17.0 (2024-03-04)</h3>
  *  [./download.wiki|Download]
  *  [./changes.wiki#0_17|Change summary]
  *  [/timeline?p=v0.17.0&bt=v0.16.0&y=ci|Check-ins for version 0.17.0],

Changes to zettel/content.go.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	"bytes"
	"encoding/base64"
	"errors"
	"io"
	"unicode"
	"unicode/utf8"

	"zettelstore.de/client.fossil/input"
)

// Content is just the content of a zettel.
type Content struct {
	data     []byte
	isBinary bool
}







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	"bytes"
	"encoding/base64"
	"errors"
	"io"
	"unicode"
	"unicode/utf8"

	"t73f.de/r/zsc/input"
)

// Content is just the content of a zettel.
type Content struct {
	data     []byte
	isBinary bool
}

Changes to zettel/id/id.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// zettel identifier.
package id

import (
	"strconv"
	"time"

	"zettelstore.de/client.fossil/api"
)

// Zid is the internal identifier of a zettel. Typically, it is a
// time stamp of the form "YYYYMMDDHHmmSS" converted to an unsigned integer.
// A zettelstore implementation should try to set the last two digits to zero,
// e.g. the seconds should be zero,
type Zid uint64







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// zettel identifier.
package id

import (
	"strconv"
	"time"

	"t73f.de/r/zsc/api"
)

// Zid is the internal identifier of a zettel. Typically, it is a
// time stamp of the form "YYYYMMDDHHmmSS" converted to an unsigned integer.
// A zettelstore implementation should try to set the last two digits to zero,
// e.g. the seconds should be zero,
type Zid uint64

Changes to zettel/meta/meta.go.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import (
	"regexp"
	"sort"
	"strings"
	"unicode"
	"unicode/utf8"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/client.fossil/maps"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/id"
)

type keyUsage int

const (







|
|
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import (
	"regexp"
	"sort"
	"strings"
	"unicode"
	"unicode/utf8"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"t73f.de/r/zsc/maps"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/id"
)

type keyUsage int

const (

Changes to zettel/meta/meta_test.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package meta

import (
	"strings"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/zettel/id"
)

const testID = id.Zid(98765432101234)

func TestKeyIsValid(t *testing.T) {
	t.Parallel()







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package meta

import (
	"strings"
	"testing"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/zettel/id"
)

const testID = id.Zid(98765432101234)

func TestKeyIsValid(t *testing.T) {
	t.Parallel()

Changes to zettel/meta/parse.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//-----------------------------------------------------------------------------

package meta

import (
	"strings"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/client.fossil/maps"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/id"
)

// NewFromInput parses the meta data of a zettel.
func NewFromInput(zid id.Zid, inp *input.Input) *Meta {
	if inp.Ch == '-' && inp.PeekN(0) == '-' && inp.PeekN(1) == '-' {







|
|
|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//-----------------------------------------------------------------------------

package meta

import (
	"strings"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"t73f.de/r/zsc/maps"
	"zettelstore.de/z/strfun"
	"zettelstore.de/z/zettel/id"
)

// NewFromInput parses the meta data of a zettel.
func NewFromInput(zid id.Zid, inp *input.Input) *Meta {
	if inp.Ch == '-' && inp.PeekN(0) == '-' && inp.PeekN(1) == '-' {

Changes to zettel/meta/parse_test.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

package meta_test

import (
	"strings"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/client.fossil/input"
	"zettelstore.de/z/zettel/meta"
)

func parseMetaStr(src string) *meta.Meta {
	return meta.NewFromInput(testID, input.NewInput([]byte(src)))
}








|
|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

package meta_test

import (
	"strings"
	"testing"

	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/input"
	"zettelstore.de/z/zettel/meta"
)

func parseMetaStr(src string) *meta.Meta {
	return meta.NewFromInput(testID, input.NewInput([]byte(src)))
}

Changes to zettel/meta/type.go.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"strconv"
	"strings"
	"sync"
	"time"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/zettel/id"
)

// DescriptionType is a description of a specific key type.
type DescriptionType struct {
	Name  string
	IsSet bool







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

import (
	"strconv"
	"strings"
	"sync"
	"time"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/zettel/id"
)

// DescriptionType is a description of a specific key type.
type DescriptionType struct {
	Name  string
	IsSet bool

Changes to zettel/meta/values.go.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package meta

import (
	"fmt"

	"zettelstore.de/client.fossil/api"
)

// Supported syntax values.
const (
	SyntaxCSS      = api.ValueSyntaxCSS
	SyntaxDraw     = api.ValueSyntaxDraw
	SyntaxGif      = api.ValueSyntaxGif







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//-----------------------------------------------------------------------------

package meta

import (
	"fmt"

	"t73f.de/r/zsc/api"
)

// Supported syntax values.
const (
	SyntaxCSS      = api.ValueSyntaxCSS
	SyntaxDraw     = api.ValueSyntaxDraw
	SyntaxGif      = api.ValueSyntaxGif

Changes to zettel/meta/write_test.go.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package meta_test

import (
	"strings"
	"testing"

	"zettelstore.de/client.fossil/api"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

const testID = id.Zid(98765432101234)

func newMeta(title string, tags []string, syntax string) *meta.Meta {







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

package meta_test

import (
	"strings"
	"testing"

	"t73f.de/r/zsc/api"
	"zettelstore.de/z/zettel/id"
	"zettelstore.de/z/zettel/meta"
)

const testID = id.Zid(98765432101234)

func newMeta(title string, tags []string, syntax string) *meta.Meta {