Zettelstore Client

Check-in Differences
Login

Check-in Differences

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

Difference From v0.21.0 To trunk

2025-08-06
17:17
Adapt to sxwebs changes; update webs dependency ... (Leaf check-in: 38bd9f293b user: stern tags: trunk)
2025-07-07
07:21
Update t73fde dependencies ... (check-in: be388711ad user: stern tags: trunk)
2025-05-02
14:34
Remove Zid for SxnPrelude ... (check-in: b5c74e61e1 user: stern tags: trunk)
2025-04-17
14:58
Version 0.21.0 ... (check-in: ef331b4f0c user: stern tags: release, trunk, v0.21.0)
09:21
Update changelog ... (check-in: 6f32e74255 user: stern tags: trunk)

Changes to api/const.go.
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
const (
	CommandAuthenticated = Command("authenticated")
	CommandRefresh       = Command("refresh")
)

// Supported search operator representations.
const (
	BackwardDirective = "BACKWARD" // Backward-only context
	ContextDirective  = "CONTEXT"  // Context directive
	CostDirective     = "COST"     // Maximum cost of a context operation


	ForwardDirective  = "FORWARD"  // Forward-only context
	FullDirective     = "FULL"     // Include tags in context
	IdentDirective    = "IDENT"    // Use only specified zettel
	ItemsDirective    = "ITEMS"    // Select list elements in a zettel
	MaxDirective      = "MAX"      // Maximum number of context results
	MinDirective      = "MIN"      // Minimum number of context results
	LimitDirective    = "LIMIT"    // Maximum number of zettel
	OffsetDirective   = "OFFSET"   // Offset to start returned zettel list
	OrDirective       = "OR"       // Combine several search expression with an "or"
	OrderDirective    = "ORDER"    // Specify metadata keys for the order of returned list
	PhraseDirective   = "PHRASE"   // Only unlinked zettel with given phrase
	PickDirective     = "PICK"     // Pick some random zettel
	RandomDirective   = "RANDOM"   // Order zettel list randomly
	ReverseDirective  = "REVERSE"  // Reverse the order of a zettel list


	UnlinkedDirective = "UNLINKED" // Search for zettel that contain a phase(s) but do not link

	ActionSeparator = "|" // Separates action list of previous elements of query expression

	KeysAction     = "KEYS"     // Provide metadata key used
	MinAction      = "MIN"      // Return only those values with a minimum amount of zettel
	MaxAction      = "MAX"      // Return only those values with a maximum amount of zettel







|


>
>
|



|









>
>







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
const (
	CommandAuthenticated = Command("authenticated")
	CommandRefresh       = Command("refresh")
)

// Supported search operator representations.
const (
	BackwardDirective = "BACKWARD" // Backward-only context / thread
	ContextDirective  = "CONTEXT"  // Context directive
	CostDirective     = "COST"     // Maximum cost of a context operation
	DirectedDirective = "DIRECTED" // Context/thread collection can have general directions
	FolgeDirective    = "FOLGE"    // Folge thread
	ForwardDirective  = "FORWARD"  // Forward-only context / thread
	FullDirective     = "FULL"     // Include tags in context
	IdentDirective    = "IDENT"    // Use only specified zettel
	ItemsDirective    = "ITEMS"    // Select list elements in a zettel
	MaxDirective      = "MAX"      // Maximum number of context / thread results
	MinDirective      = "MIN"      // Minimum number of context results
	LimitDirective    = "LIMIT"    // Maximum number of zettel
	OffsetDirective   = "OFFSET"   // Offset to start returned zettel list
	OrDirective       = "OR"       // Combine several search expression with an "or"
	OrderDirective    = "ORDER"    // Specify metadata keys for the order of returned list
	PhraseDirective   = "PHRASE"   // Only unlinked zettel with given phrase
	PickDirective     = "PICK"     // Pick some random zettel
	RandomDirective   = "RANDOM"   // Order zettel list randomly
	ReverseDirective  = "REVERSE"  // Reverse the order of a zettel list
	SequelDirective   = "SEQUEL"   // Sequel / branching thread
	ThreadDirective   = "THREAD"   // Both folge and Sequel thread
	UnlinkedDirective = "UNLINKED" // Search for zettel that contain a phase(s) but do not link

	ActionSeparator = "|" // Separates action list of previous elements of query expression

	KeysAction     = "KEYS"     // Provide metadata key used
	MinAction      = "MIN"      // Return only those values with a minimum amount of zettel
	MaxAction      = "MAX"      // Return only those values with a maximum amount of zettel
Changes to client/client_test.go.
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
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/client"
	"t73f.de/r/zsc/domain/id"
)

func TestZettelList(t *testing.T) {
	c := getClient()
	_, err := c.QueryZettel(context.Background(), "")
	if err != nil {
		t.Error(err)
		return
	}
}

func TestGetProtectedZettel(t *testing.T) {
	c := getClient()
	_, err := c.GetZettel(context.Background(), id.ZidStartupConfiguration, api.PartZettel)
	if err != nil {
		if cErr, ok := err.(*client.Error); ok && cErr.StatusCode == http.StatusForbidden {
			return
		} else {
			t.Error(err)
		}
		return
	}
}

func TestGetSzZettel(t *testing.T) {
	c := getClient()
	value, err := c.GetEvaluatedSz(context.Background(), id.ZidDefaultHome, api.PartContent)
	if err != nil {







|
<

<





|
<
|
<
<


<







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
	"t73f.de/r/zsc/api"
	"t73f.de/r/zsc/client"
	"t73f.de/r/zsc/domain/id"
)

func TestZettelList(t *testing.T) {
	c := getClient()
	if _, err := c.QueryZettel(context.Background(), ""); err != nil {

		t.Error(err)

	}
}

func TestGetProtectedZettel(t *testing.T) {
	c := getClient()
	if _, err := c.GetZettel(context.Background(), id.ZidStartupConfiguration, api.PartZettel); err != nil {

		if cErr, ok := err.(*client.Error); !ok || cErr.StatusCode != http.StatusForbidden {


			t.Error(err)
		}

	}
}

func TestGetSzZettel(t *testing.T) {
	c := getClient()
	value, err := c.GetEvaluatedSz(context.Background(), id.ZidDefaultHome, api.PartContent)
	if err != nil {
Changes to domain/id/id.go.
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

	// WebUI JS zettel are in the range 30000..39999

	// WebUI image zettel are in the range 40000..49999
	ZidEmoji = Zid(40001)

	// Other sxn code zettel are in the range 50000..59999
	ZidSxnPrelude = Zid(59900)

	// Predefined Zettelmarkup zettel are in the range 60000..69999
	ZidRoleZettelZettel        = Zid(60010)
	ZidRoleConfigurationZettel = Zid(60020)
	ZidRoleRoleZettel          = Zid(60030)
	ZidRoleTagZettel           = Zid(60040)








<







85
86
87
88
89
90
91

92
93
94
95
96
97
98

	// WebUI JS zettel are in the range 30000..39999

	// WebUI image zettel are in the range 40000..49999
	ZidEmoji = Zid(40001)

	// Other sxn code zettel are in the range 50000..59999


	// Predefined Zettelmarkup zettel are in the range 60000..69999
	ZidRoleZettelZettel        = Zid(60010)
	ZidRoleConfigurationZettel = Zid(60020)
	ZidRoleRoleZettel          = Zid(60030)
	ZidRoleTagZettel           = Zid(60040)

Changes to domain/id/idset/idset.go.
129
130
131
132
133
134
135
136




137
138
139
140
141
142
143
144

// IntersectOrSet removes all zettel identifier that are not in the other set.
// Both sets can be modified by this method. One of them is the set returned.
// It contains the intersection of both, if s is not nil.
//
// If s == nil, then the other set is always returned.
func (s *Set) IntersectOrSet(other *Set) *Set {
	if s == nil || other == nil {




		return other.Clone()
	}
	topos, spos, opos := 0, 0, 0
	for spos < len(s.seq) && opos < len(other.seq) {
		sz, oz := s.seq[spos], other.seq[opos]
		if sz < oz {
			spos++
			continue







|
>
>
>
>
|







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

// IntersectOrSet removes all zettel identifier that are not in the other set.
// Both sets can be modified by this method. One of them is the set returned.
// It contains the intersection of both, if s is not nil.
//
// If s == nil, then the other set is always returned.
func (s *Set) IntersectOrSet(other *Set) *Set {
	if s == nil {
		return other // no other.Clone(), since other != nil, i.e. "not found"
	}
	if other == nil {
		s.seq = s.seq[:0]
		return s
	}
	topos, spos, opos := 0, 0, 0
	for spos < len(s.seq) && opos < len(other.seq) {
		sz, oz := s.seq[spos], other.seq[opos]
		if sz < oz {
			spos++
			continue
Changes to domain/meta/meta.go.
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
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
201
202
203
	KeyFolge        = "folge"
	KeyFolgeRole    = "folge-role"
	KeyForward      = "forward"
	KeyLang         = "lang"
	KeyLicense      = "license"
	KeyModified     = "modified"
	KeyPrecursor    = "precursor"
	KeyPredecessor  = "predecessor"
	KeyPrequel      = "prequel"
	KeyPublished    = "published"
	KeyQuery        = "query"
	KeyReadOnly     = "read-only"
	KeySequel       = "sequel"
	KeySubordinates = "subordinates"
	KeySuccessors   = "successors"
	KeySummary      = "summary"
	KeySuperior     = "superior"
	KeyURL          = "url"
	KeyUselessFiles = "useless-files"
	KeyUserID       = "user-id"
	KeyUserRole     = "user-role"
	KeyVisibility   = "visibility"
)

// Supported keys.
func init() {
	registerKey(KeyID, TypeID, usageComputed, "")
	registerKey(KeyTitle, TypeEmpty, usageUser, "")
	registerKey(KeyRole, TypeWord, usageUser, "")
	registerKey(KeyTags, TypeTagSet, usageUser, "")
	registerKey(KeySyntax, TypeWord, usageUser, "")

	// Properties that are inverse keys
	registerKey(KeyFolge, TypeIDSet, usageProperty, "")
	registerKey(KeySequel, TypeIDSet, usageProperty, "")
	registerKey(KeySuccessors, TypeIDSet, usageProperty, "")
	registerKey(KeySubordinates, TypeIDSet, usageProperty, "")

	// Non-inverse keys
	registerKey(KeyAuthor, TypeString, usageUser, "")
	registerKey(KeyBack, TypeIDSet, usageProperty, "")
	registerKey(KeyBackward, TypeIDSet, usageProperty, "")
	registerKey(KeyBoxNumber, TypeNumber, usageProperty, "")
	registerKey(KeyCopyright, TypeString, usageUser, "")
	registerKey(KeyCreated, TypeTimestamp, usageComputed, "")
	registerKey(KeyCredential, TypeCredential, usageUser, "")
	registerKey(KeyDead, TypeIDSet, usageProperty, "")
	registerKey(KeyExpire, TypeTimestamp, usageUser, "")
	registerKey(KeyFolgeRole, TypeWord, usageUser, "")
	registerKey(KeyForward, TypeIDSet, usageProperty, "")
	registerKey(KeyLang, TypeWord, usageUser, "")
	registerKey(KeyLicense, TypeEmpty, usageUser, "")
	registerKey(KeyModified, TypeTimestamp, usageComputed, "")
	registerKey(KeyPrecursor, TypeIDSet, usageUser, KeyFolge)
	registerKey(KeyPredecessor, TypeID, usageUser, KeySuccessors)
	registerKey(KeyPrequel, TypeIDSet, usageUser, KeySequel)
	registerKey(KeyPublished, TypeTimestamp, usageProperty, "")
	registerKey(KeyQuery, TypeEmpty, usageUser, "")
	registerKey(KeyReadOnly, TypeWord, usageUser, "")
	registerKey(KeySummary, TypeString, usageUser, "")
	registerKey(KeySuperior, TypeIDSet, usageUser, KeySubordinates)
	registerKey(KeyURL, TypeURL, usageUser, "")







<






<




















<


















<







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
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
	KeyFolge        = "folge"
	KeyFolgeRole    = "folge-role"
	KeyForward      = "forward"
	KeyLang         = "lang"
	KeyLicense      = "license"
	KeyModified     = "modified"
	KeyPrecursor    = "precursor"

	KeyPrequel      = "prequel"
	KeyPublished    = "published"
	KeyQuery        = "query"
	KeyReadOnly     = "read-only"
	KeySequel       = "sequel"
	KeySubordinates = "subordinates"

	KeySummary      = "summary"
	KeySuperior     = "superior"
	KeyURL          = "url"
	KeyUselessFiles = "useless-files"
	KeyUserID       = "user-id"
	KeyUserRole     = "user-role"
	KeyVisibility   = "visibility"
)

// Supported keys.
func init() {
	registerKey(KeyID, TypeID, usageComputed, "")
	registerKey(KeyTitle, TypeEmpty, usageUser, "")
	registerKey(KeyRole, TypeWord, usageUser, "")
	registerKey(KeyTags, TypeTagSet, usageUser, "")
	registerKey(KeySyntax, TypeWord, usageUser, "")

	// Properties that are inverse keys
	registerKey(KeyFolge, TypeIDSet, usageProperty, "")
	registerKey(KeySequel, TypeIDSet, usageProperty, "")

	registerKey(KeySubordinates, TypeIDSet, usageProperty, "")

	// Non-inverse keys
	registerKey(KeyAuthor, TypeString, usageUser, "")
	registerKey(KeyBack, TypeIDSet, usageProperty, "")
	registerKey(KeyBackward, TypeIDSet, usageProperty, "")
	registerKey(KeyBoxNumber, TypeNumber, usageProperty, "")
	registerKey(KeyCopyright, TypeString, usageUser, "")
	registerKey(KeyCreated, TypeTimestamp, usageComputed, "")
	registerKey(KeyCredential, TypeCredential, usageUser, "")
	registerKey(KeyDead, TypeIDSet, usageProperty, "")
	registerKey(KeyExpire, TypeTimestamp, usageUser, "")
	registerKey(KeyFolgeRole, TypeWord, usageUser, "")
	registerKey(KeyForward, TypeIDSet, usageProperty, "")
	registerKey(KeyLang, TypeWord, usageUser, "")
	registerKey(KeyLicense, TypeEmpty, usageUser, "")
	registerKey(KeyModified, TypeTimestamp, usageComputed, "")
	registerKey(KeyPrecursor, TypeIDSet, usageUser, KeyFolge)

	registerKey(KeyPrequel, TypeIDSet, usageUser, KeySequel)
	registerKey(KeyPublished, TypeTimestamp, usageProperty, "")
	registerKey(KeyQuery, TypeEmpty, usageUser, "")
	registerKey(KeyReadOnly, TypeWord, usageUser, "")
	registerKey(KeySummary, TypeString, usageUser, "")
	registerKey(KeySuperior, TypeIDSet, usageUser, KeySubordinates)
	registerKey(KeyURL, TypeURL, usageUser, "")
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
	}
}

// SetNonEmpty stores the given value under the given key, if the value is non-empty.
// An empty value will delete the previous association.
func (m *Meta) SetNonEmpty(key string, value Value) {
	if value == "" {

		delete(m.pairs, key) // TODO: key != KeyID

	} else {
		m.Set(key, value.TrimSpace())
	}
}











// Get retrieves the string value of a given key. The bool value signals,
// whether there was a value stored or not.
func (m *Meta) Get(key string) (Value, bool) {
	if m == nil {

		return "", false
	}
	if key == KeyID {
		return Value(m.Zid.String()), true
	}
	value, ok := m.pairs[key]

	return value, ok
}

// GetDefault retrieves the string value of the given key. If no value was
// stored, the given default value is returned.
func (m *Meta) GetDefault(key string, def Value) Value {
	if value, found := m.Get(key); found {
		return value







>
|
>




>
>
>
>
>
>
>
>
>
>




|
>
|
|
|
|
|
<
>
|







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
	}
}

// SetNonEmpty stores the given value under the given key, if the value is non-empty.
// An empty value will delete the previous association.
func (m *Meta) SetNonEmpty(key string, value Value) {
	if value == "" {
		if key != KeyID {
			delete(m.pairs, key)
		}
	} else {
		m.Set(key, value.TrimSpace())
	}
}

// Has returns true, if the given key is used in the metadata.
func (m *Meta) Has(key string) bool {
	if m != nil {
		if _, found := m.pairs[key]; found || key == KeyID {
			return true
		}
	}
	return false
}

// Get retrieves the string value of a given key. The bool value signals,
// whether there was a value stored or not.
func (m *Meta) Get(key string) (Value, bool) {
	if m != nil {
		if value, found := m.pairs[key]; found {
			return value, true
		}
		if key == KeyID {
			return Value(m.Zid.String()), true
		}

	}
	return "", false
}

// GetDefault retrieves the string value of the given key. If no value was
// stored, the given default value is returned.
func (m *Meta) GetDefault(key string, def Value) Value {
	if value, found := m.Get(key); found {
		return value
Changes to domain/meta/type.go.
90
91
92
93
94
95
96


97
98
99
100
101
102
103

var (
	cachedTypedKeys = make(map[string]*DescriptionType)
	mxTypedKey      sync.RWMutex
	suffixTypes     = map[string]*DescriptionType{
		"-date":       TypeTimestamp,
		"-number":     TypeNumber,


		SuffixKeyRole: TypeWord,
		"-time":       TypeTimestamp,
		SuffixKeyURL:  TypeURL,
		"-zettel":     TypeID,
		"-zid":        TypeID,
		"-zids":       TypeIDSet,
	}







>
>







90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

var (
	cachedTypedKeys = make(map[string]*DescriptionType)
	mxTypedKey      sync.RWMutex
	suffixTypes     = map[string]*DescriptionType{
		"-date":       TypeTimestamp,
		"-number":     TypeNumber,
		"-ref":        TypeID,
		"-refs":       TypeIDSet,
		SuffixKeyRole: TypeWord,
		"-time":       TypeTimestamp,
		SuffixKeyURL:  TypeURL,
		"-zettel":     TypeID,
		"-zid":        TypeID,
		"-zids":       TypeIDSet,
	}
Changes to go.mod.
1
2
3
4
5
6
7
8
9
10
11
module t73f.de/r/zsc

go 1.24

require (
	t73f.de/r/sx v0.0.0-20250415161954-42ed9c4d6abc
	t73f.de/r/sxwebs v0.0.0-20250415162443-110f49c5a1ae
	t73f.de/r/webs v0.0.0-20250311182734-f263a38b32d5
	t73f.de/r/zero v0.0.0-20250226205915-c4194684acb7
	t73f.de/r/zsx v0.0.0-20250415162540-fc13b286b6ce
)





|
|
|
|
|

1
2
3
4
5
6
7
8
9
10
11
module t73f.de/r/zsc

go 1.24

require (
	t73f.de/r/sx v0.0.0-20250707071435-95b82f7d24bb
	t73f.de/r/sxwebs v0.0.0-20250806170342-a33d11a3e7c5
	t73f.de/r/webs v0.0.0-20250723141744-5e8deae4d17b
	t73f.de/r/zero v0.0.0-20250703105709-bb38976d4455
	t73f.de/r/zsx v0.0.0-20250707071920-5e29047e4db7
)
Changes to go.sum.
1
2
3
4
5
6
7
8
9
10
t73f.de/r/sx v0.0.0-20250415161954-42ed9c4d6abc h1:tlsP+47Rf8i9Zv1TqRnwfbQx3nN/F/92RkT6iCA6SVA=
t73f.de/r/sx v0.0.0-20250415161954-42ed9c4d6abc/go.mod h1:hzg05uSCMk3D/DWaL0pdlowfL2aWQeGIfD1S04vV+Xg=
t73f.de/r/sxwebs v0.0.0-20250415162443-110f49c5a1ae h1:K6nxN/bb0BCSiDffwNPGTF2uf5WcTdxcQXzByXNuJ7M=
t73f.de/r/sxwebs v0.0.0-20250415162443-110f49c5a1ae/go.mod h1:0LQ9T1svSg9ADY/6vQLKNUu6LqpPi8FGr7fd2qDT5H8=
t73f.de/r/webs v0.0.0-20250311182734-f263a38b32d5 h1:nnKfs/2i9n3S5VjbSj98odcwZKGcL96qPSIUATT/2P8=
t73f.de/r/webs v0.0.0-20250311182734-f263a38b32d5/go.mod h1:zk92hSKB4iWyT290+163seNzu350TA9XLATC9kOldqo=
t73f.de/r/zero v0.0.0-20250226205915-c4194684acb7 h1:OuzHSfniY8UzLmo5zp1w23Kd9h7x9CSXP2jQ+kppeqU=
t73f.de/r/zero v0.0.0-20250226205915-c4194684acb7/go.mod h1:T1vFcHoymUQcr7+vENBkS1yryZRZ3YB8uRtnMy8yRBA=
t73f.de/r/zsx v0.0.0-20250415162540-fc13b286b6ce h1:R9rtg4ecx4YYixsMmsh+wdcqLdY9GxoC5HZ9mMS33to=
t73f.de/r/zsx v0.0.0-20250415162540-fc13b286b6ce/go.mod h1:tXOlmsQBoY4mY7Plu0LCCMZNSJZJbng98fFarZXAWvM=
|
|
|
|
|
|
|
|
|
|
1
2
3
4
5
6
7
8
9
10
t73f.de/r/sx v0.0.0-20250707071435-95b82f7d24bb h1:cYvTOpaJinh/EPB7i8nx7PtT7hniuSP+NZr74P9U+fE=
t73f.de/r/sx v0.0.0-20250707071435-95b82f7d24bb/go.mod h1:uglbFdRHlcpQVVyCNh4Fd7jbKo8alGBCjRp0aZv8IIg=
t73f.de/r/sxwebs v0.0.0-20250806170342-a33d11a3e7c5 h1:7r1SP0h9QySrRDz9qYWZ/xuEEyJi3XYHtxRIoW2BhoM=
t73f.de/r/sxwebs v0.0.0-20250806170342-a33d11a3e7c5/go.mod h1:CvFDV0czGR0qFVdTYrdy8WIIu5OvA3tFD/td1mim/lA=
t73f.de/r/webs v0.0.0-20250723141744-5e8deae4d17b h1:LmJ0STcaUyGgH2jVa/nKifmJedl0njdnMwPxqX6zLQg=
t73f.de/r/webs v0.0.0-20250723141744-5e8deae4d17b/go.mod h1:b8/5E5Pe6WSWqh+T+sxLO5ZLiGVkuL5tgh86kx2OAIg=
t73f.de/r/zero v0.0.0-20250703105709-bb38976d4455 h1:TFRPPexX2WrwuF03hC+Be2ONx2bPzMMBlNDn0rk88eI=
t73f.de/r/zero v0.0.0-20250703105709-bb38976d4455/go.mod h1:Ovx7CYsjz45BNuIEMGZfqA7NdQxERydJqUGnOBoQaXQ=
t73f.de/r/zsx v0.0.0-20250707071920-5e29047e4db7 h1:ERxpb1Hqln+NXoZDK6sqjmX3BzeoLO+O64f4bK0B6dk=
t73f.de/r/zsx v0.0.0-20250707071920-5e29047e4db7/go.mod h1:64/AjQ1GnEBoBhXI1D0bDMGDj7JCbtZUTT3WoA7kS0s=
Changes to sexp/sexp.go.
17
18
19
20
21
22
23

24
25
26
27
28
29
30

import (
	"errors"
	"fmt"
	"sort"

	"t73f.de/r/sx"

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

// EncodeZettel transforms zettel data into a sx object.
func EncodeZettel(zettel api.ZettelData) sx.Object {
	return sx.MakeList(
		sx.MakeSymbol("zettel"),







>







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

import (
	"errors"
	"fmt"
	"sort"

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

// EncodeZettel transforms zettel data into a sx object.
func EncodeZettel(zettel api.ZettelData) sx.Object {
	return sx.MakeList(
		sx.MakeSymbol("zettel"),
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
		Content:  contentVals[1].(sx.String).GetValue(),
	}, nil
}

// EncodeMetaRights translates metadata/rights into a sx object.
func EncodeMetaRights(mr api.MetaRights) *sx.Pair {
	return sx.MakeList(
		sx.SymbolList,
		meta2sz(mr.Meta),
		sx.MakeList(sx.MakeSymbol("rights"), sx.Int64(int64(mr.Rights))),
	)
}

func meta2sz(m api.ZettelMeta) sx.Object {
	var result sx.ListBuilder







|







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
		Content:  contentVals[1].(sx.String).GetValue(),
	}, nil
}

// EncodeMetaRights translates metadata/rights into a sx object.
func EncodeMetaRights(mr api.MetaRights) *sx.Pair {
	return sx.MakeList(
		sx.MakeSymbol(sxbuiltins.List.Name),
		meta2sz(mr.Meta),
		sx.MakeList(sx.MakeSymbol("rights"), sx.Int64(int64(mr.Rights))),
	)
}

func meta2sz(m api.ZettelMeta) sx.Object {
	var result sx.ListBuilder
Changes to shtml/shtml.go.
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
	keys := a.Keys()
	for i := len(keys) - 1; i >= 0; i-- {
		key := keys[i]
		if key != zsx.DefaultAttribute && isValidName(key) {
			plist = plist.Cons(sx.Cons(sx.MakeSymbol(key), sx.MakeString(a[key])))
		}
	}
	if plist == nil {
		return nil
	}
	return plist.Cons(sxhtml.SymAttr)
}

// Evaluate a metadata s-expression into a list of HTML s-expressions.
func (ev *Evaluator) Evaluate(lst *sx.Pair, env *Environment) (*sx.Pair, error) {
	result := ev.Eval(lst, env)
	if err := env.err; err != nil {
		return nil, err







<
|
<
<







68
69
70
71
72
73
74

75


76
77
78
79
80
81
82
	keys := a.Keys()
	for i := len(keys) - 1; i >= 0; i-- {
		key := keys[i]
		if key != zsx.DefaultAttribute && isValidName(key) {
			plist = plist.Cons(sx.Cons(sx.MakeSymbol(key), sx.MakeString(a[key])))
		}
	}

	return plist


}

// Evaluate a metadata s-expression into a list of HTML s-expressions.
func (ev *Evaluator) Evaluate(lst *sx.Pair, env *Environment) (*sx.Pair, error) {
	result := ev.Eval(lst, env)
	if err := env.err; err != nil {
		return nil, err
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
	if env.err != nil || len(env.endnotes) == 0 {
		return nil
	}

	var result sx.ListBuilder
	result.AddN(
		SymOL,
		sx.Nil().Cons(sx.Cons(SymAttrClass, sx.MakeString("zs-endnotes"))).Cons(sxhtml.SymAttr),
	)
	for i, fni := range env.endnotes {
		noteNum := strconv.Itoa(i + 1)
		attrs := fni.attrs.Cons(sx.Cons(SymAttrClass, sx.MakeString("zs-endnote"))).
			Cons(sx.Cons(SymAttrValue, sx.MakeString(noteNum))).
			Cons(sx.Cons(SymAttrID, sx.MakeString("fn:"+fni.noteID))).
			Cons(sx.Cons(SymAttrRole, sx.MakeString("doc-endnote"))).
			Cons(sxhtml.SymAttr)

		backref := sx.Nil().Cons(sx.MakeString("\u21a9\ufe0e")).
			Cons(sx.Nil().
				Cons(sx.Cons(SymAttrClass, sx.MakeString("zs-endnote-backref"))).
				Cons(sx.Cons(SymAttrHref, sx.MakeString("#fnref:"+fni.noteID))).
				Cons(sx.Cons(SymAttrRole, sx.MakeString("doc-backlink"))).
				Cons(sxhtml.SymAttr)).
			Cons(SymA)

		var li sx.ListBuilder
		li.AddN(SymLI, attrs)
		li.ExtendBang(fni.noteHx)
		li.AddN(sx.MakeString(" "), backref)
		result.Add(li.List())







|






|
<





|
<







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
	if env.err != nil || len(env.endnotes) == 0 {
		return nil
	}

	var result sx.ListBuilder
	result.AddN(
		SymOL,
		sx.Nil().Cons(sx.Cons(SymAttrClass, sx.MakeString("zs-endnotes"))),
	)
	for i, fni := range env.endnotes {
		noteNum := strconv.Itoa(i + 1)
		attrs := fni.attrs.Cons(sx.Cons(SymAttrClass, sx.MakeString("zs-endnote"))).
			Cons(sx.Cons(SymAttrValue, sx.MakeString(noteNum))).
			Cons(sx.Cons(SymAttrID, sx.MakeString("fn:"+fni.noteID))).
			Cons(sx.Cons(SymAttrRole, sx.MakeString("doc-endnote")))


		backref := sx.Nil().Cons(sx.MakeString("\u21a9\ufe0e")).
			Cons(sx.Nil().
				Cons(sx.Cons(SymAttrClass, sx.MakeString("zs-endnote-backref"))).
				Cons(sx.Cons(SymAttrHref, sx.MakeString("#fnref:"+fni.noteID))).
				Cons(sx.Cons(SymAttrRole, sx.MakeString("doc-backlink")))).

			Cons(SymA)

		var li sx.ListBuilder
		li.AddN(SymLI, attrs)
		li.ExtendBang(fni.noteHx)
		li.AddN(sx.MakeString(" "), backref)
		result.Add(li.List())
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657

		noteNum := strconv.Itoa(len(env.endnotes) + 1)
		noteID := ev.unique + noteNum
		env.endnotes = append(env.endnotes, endnoteInfo{
			noteID: noteID, noteAST: args[1:], noteHx: nil, attrs: attrPlist})
		hrefAttr := sx.Nil().Cons(sx.Cons(SymAttrRole, sx.MakeString("doc-noteref"))).
			Cons(sx.Cons(SymAttrHref, sx.MakeString("#fn:"+noteID))).
			Cons(sx.Cons(SymAttrClass, sx.MakeString("zs-noteref"))).
			Cons(sxhtml.SymAttr)
		href := sx.Nil().Cons(sx.MakeString(noteNum)).Cons(hrefAttr).Cons(SymA)
		supAttr := sx.Nil().Cons(sx.Cons(SymAttrID, sx.MakeString("fnref:"+noteID))).Cons(sxhtml.SymAttr)
		return sx.Nil().Cons(href).Cons(supAttr).Cons(symSUP)
	})

	ev.bind(zsx.SymFormatDelete, 1, ev.makeFormatFn(symDEL))
	ev.bind(zsx.SymFormatEmph, 1, ev.makeFormatFn(symEM))
	ev.bind(zsx.SymFormatInsert, 1, ev.makeFormatFn(symINS))
	ev.bind(zsx.SymFormatMark, 1, ev.makeFormatFn(symMARK))







|
<

|







635
636
637
638
639
640
641
642

643
644
645
646
647
648
649
650
651

		noteNum := strconv.Itoa(len(env.endnotes) + 1)
		noteID := ev.unique + noteNum
		env.endnotes = append(env.endnotes, endnoteInfo{
			noteID: noteID, noteAST: args[1:], noteHx: nil, attrs: attrPlist})
		hrefAttr := sx.Nil().Cons(sx.Cons(SymAttrRole, sx.MakeString("doc-noteref"))).
			Cons(sx.Cons(SymAttrHref, sx.MakeString("#fn:"+noteID))).
			Cons(sx.Cons(SymAttrClass, sx.MakeString("zs-noteref")))

		href := sx.Nil().Cons(sx.MakeString(noteNum)).Cons(hrefAttr).Cons(SymA)
		supAttr := sx.Nil().Cons(sx.Cons(SymAttrID, sx.MakeString("fnref:"+noteID)))
		return sx.Nil().Cons(href).Cons(supAttr).Cons(symSUP)
	})

	ev.bind(zsx.SymFormatDelete, 1, ev.makeFormatFn(symDEL))
	ev.bind(zsx.SymFormatEmph, 1, ev.makeFormatFn(symEM))
	ev.bind(zsx.SymFormatInsert, 1, ev.makeFormatFn(symINS))
	ev.bind(zsx.SymFormatMark, 1, ev.makeFormatFn(symMARK))
Changes to sz/zmk/zmk_test.go.
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
			}
		})
	}
}

type astWalker struct{}

func (astWalker) VisitBefore(node *sx.Pair, env *sx.Pair) (sx.Object, bool) { return sx.Nil(), false }
func (astWalker) VisitAfter(node *sx.Pair, env *sx.Pair) sx.Object          { return node }

func TestEdges(t *testing.T) {
	t.Parallel()
	checkTcs(t, TestCases{
		{"\"\"\"\n; \n0{{0}}{0}\n\"\"\"", "(BLOCK (REGION-VERSE () ((DESCRIPTION () ()) (PARA (TEXT \"0\") (EMBED ((\"0\" . \"\")) (HOSTED \"0\") \"\")))))"},
	})
}







|
|







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
			}
		})
	}
}

type astWalker struct{}

func (astWalker) VisitBefore(*sx.Pair, *sx.Pair) (sx.Object, bool) { return sx.Nil(), false }
func (astWalker) VisitAfter(node *sx.Pair, _ *sx.Pair) sx.Object   { return node }

func TestEdges(t *testing.T) {
	t.Parallel()
	checkTcs(t, TestCases{
		{"\"\"\"\n; \n0{{0}}{0}\n\"\"\"", "(BLOCK (REGION-VERSE () ((DESCRIPTION () ()) (PARA (TEXT \"0\") (EMBED ((\"0\" . \"\")) (HOSTED \"0\") \"\")))))"},
	})
}