detached 1cda60e

refactor(systemviews): manage favorites with tags only

commit
1cda60e267dd1942dd4112b31b2650b298cab3fd
author
brice · · 2026-06-28 07:46 UTC
parent
45b8a06
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
14 files · +426 -1412
M cmd/systemviews/bootstrap_test.go +0 -16
@@ -64,7 +64,6 @@ func TestNewBridgeApp_WiresFavoritesService(t *testing.T) {
6464
6565 bridge := &bridgeServiceRecorder{
6666 favorites: []*entity.Favorite{{Title: "Example", URL: "https://example.com"}},
67- folders: []*entity.Folder{{Name: "Read Later"}},
6867 tags: []*entity.Tag{{Name: "Go"}},
6968 }
7069 app := newBridgeApp(&recordingDOM{}, "dumb://favorites", bridge)
@@ -141,7 +140,6 @@ type bridgeServiceRecorder struct {
141140
142141 historyEntries []*entity.HistoryEntry
143142 favorites []*entity.Favorite
144- folders []*entity.Folder
145143 tags []*entity.Tag
146144 currentConfig dto.SystemviewConfigPayload
147145 keybindings port.KeybindingsConfig
@@ -199,26 +197,12 @@ func (*bridgeServiceRecorder) UpdateFavorite(context.Context, dto.FavoriteUpdate
199197
200198 func (*bridgeServiceRecorder) DeleteFavorite(context.Context, int64) error { return nil }
201199
202-func (f *bridgeServiceRecorder) ListFolders(context.Context) ([]*entity.Folder, error) {
203- return f.folders, nil
204-}
205-
206200 func (f *bridgeServiceRecorder) ListTags(context.Context) ([]*entity.Tag, error) {
207201 return f.tags, nil
208202 }
209203
210204 func (*bridgeServiceRecorder) SetShortcut(context.Context, int64, *int) error { return nil }
211205
212-func (*bridgeServiceRecorder) SetFolder(context.Context, int64, *int64) error { return nil }
213-
214-func (*bridgeServiceRecorder) CreateFolder(context.Context, string, string, *int64) (*entity.Folder, error) {
215- return nil, nil
216-}
217-
218-func (*bridgeServiceRecorder) UpdateFolder(context.Context, int64, string, string) error { return nil }
219-
220-func (*bridgeServiceRecorder) DeleteFolder(context.Context, int64) error { return nil }
221-
222206 func (*bridgeServiceRecorder) CreateTag(context.Context, string, string) (*entity.Tag, error) {
223207 return nil, nil
224208 }
@@ -64,7 +64,6 @@ func TestNewBridgeApp_WiresFavoritesService(t *testing.T) {
64 64
65 bridge := &bridgeServiceRecorder{65 bridge := &bridgeServiceRecorder{
66 favorites: []*entity.Favorite{{Title: "Example", URL: "https://example.com"}},66 favorites: []*entity.Favorite{{Title: "Example", URL: "https://example.com"}},
67- folders: []*entity.Folder{{Name: "Read Later"}},
68 tags: []*entity.Tag{{Name: "Go"}},67 tags: []*entity.Tag{{Name: "Go"}},
69 }68 }
70 app := newBridgeApp(&recordingDOM{}, "dumb://favorites", bridge)69 app := newBridgeApp(&recordingDOM{}, "dumb://favorites", bridge)
@@ -141,7 +140,6 @@ type bridgeServiceRecorder struct {
141 140
142 historyEntries []*entity.HistoryEntry141 historyEntries []*entity.HistoryEntry
143 favorites []*entity.Favorite142 favorites []*entity.Favorite
144- folders []*entity.Folder
145 tags []*entity.Tag143 tags []*entity.Tag
146 currentConfig dto.SystemviewConfigPayload144 currentConfig dto.SystemviewConfigPayload
147 keybindings port.KeybindingsConfig145 keybindings port.KeybindingsConfig
@@ -199,26 +197,12 @@ func (*bridgeServiceRecorder) UpdateFavorite(context.Context, dto.FavoriteUpdate
199 197
200 func (*bridgeServiceRecorder) DeleteFavorite(context.Context, int64) error { return nil }198 func (*bridgeServiceRecorder) DeleteFavorite(context.Context, int64) error { return nil }
201 199
202-func (f *bridgeServiceRecorder) ListFolders(context.Context) ([]*entity.Folder, error) {
203- return f.folders, nil
204-}
205-
206 func (f *bridgeServiceRecorder) ListTags(context.Context) ([]*entity.Tag, error) {200 func (f *bridgeServiceRecorder) ListTags(context.Context) ([]*entity.Tag, error) {
207 return f.tags, nil201 return f.tags, nil
208 }202 }
209 203
210 func (*bridgeServiceRecorder) SetShortcut(context.Context, int64, *int) error { return nil }204 func (*bridgeServiceRecorder) SetShortcut(context.Context, int64, *int) error { return nil }
211 205
212-func (*bridgeServiceRecorder) SetFolder(context.Context, int64, *int64) error { return nil }
213-
214-func (*bridgeServiceRecorder) CreateFolder(context.Context, string, string, *int64) (*entity.Folder, error) {
215- return nil, nil
216-}
217-
218-func (*bridgeServiceRecorder) UpdateFolder(context.Context, int64, string, string) error { return nil }
219-
220-func (*bridgeServiceRecorder) DeleteFolder(context.Context, int64) error { return nil }
221-
222 func (*bridgeServiceRecorder) CreateTag(context.Context, string, string) (*entity.Tag, error) {206 func (*bridgeServiceRecorder) CreateTag(context.Context, string, string) (*entity.Tag, error) {
223 return nil, nil207 return nil, nil
224 }208 }
M internal/infrastructure/handlers/homepage/favorites.go +0 -63
@@ -12,17 +12,6 @@ import (
1212 "github.com/bnema/dumber/internal/logging"
1313 )
1414
15-func folderIDFromInt64(id *int64) (*entity.FolderID, error) {
16- if id == nil {
17- return nil, nil
18- }
19- if *id <= 0 {
20- return nil, fmt.Errorf("folder id must be positive")
21- }
22- folderID := entity.FolderID(*id)
23- return &folderID, nil
24-}
25-
2615 func tagIDsFromInt64s(ids []int64) ([]entity.TagID, error) {
2716 out := make([]entity.TagID, 0, len(ids))
2817 for _, id := range ids {
@@ -69,7 +58,6 @@ type favoriteCreateRequest struct {
6958 URL string `json:"url"`
7059 Title string `json:"title"`
7160 FaviconURL string `json:"favicon_url"`
72- FolderID *int64 `json:"folder_id"`
7361 Tags []int64 `json:"tags"`
7462 }
7563
@@ -91,10 +79,6 @@ func (h *FavoritesHandlers) HandleCreate() port.WebUIMessageHandler {
9179 if trimmedURL == "" {
9280 return NewErrorResponse(req.RequestID, fmt.Errorf("URL is required")), nil
9381 }
94- folderID, err := folderIDFromInt64(req.FolderID)
95- if err != nil {
96- return NewErrorResponse(req.RequestID, err), nil
97- }
9882 tags, err := tagIDsFromInt64s(req.Tags)
9983 if err != nil {
10084 return NewErrorResponse(req.RequestID, err), nil
@@ -104,7 +88,6 @@ func (h *FavoritesHandlers) HandleCreate() port.WebUIMessageHandler {
10488 URL: trimmedURL,
10589 Title: req.Title,
10690 FaviconURL: req.FaviconURL,
107- FolderID: folderID,
10891 Tags: tags,
10992 }
11093 favorite, err := h.favoritesUC.AddFavorite(ctx, input)
@@ -121,7 +104,6 @@ type favoriteUpdateRequest struct {
121104 ID int64 `json:"id"`
122105 Title string `json:"title"`
123106 FaviconURL string `json:"favicon_url"`
124- FolderID *int64 `json:"folder_id"`
125107 ShortcutKey *int `json:"shortcut_key"`
126108 }
127109
@@ -144,16 +126,10 @@ func (h *FavoritesHandlers) HandleUpdate() port.WebUIMessageHandler {
144126 return NewErrorResponse(req.RequestID, fmt.Errorf("favorite id must be positive")), nil
145127 }
146128
147- folderID, err := folderIDFromInt64(req.FolderID)
148- if err != nil {
149- return NewErrorResponse(req.RequestID, err), nil
150- }
151-
152129 favorite, err := h.favoritesUC.UpdateFavorite(ctx, dto.FavoriteUpdateInput{
153130 ID: entity.FavoriteID(req.ID),
154131 Title: req.Title,
155132 FaviconURL: req.FaviconURL,
156- FolderID: folderID,
157133 ShortcutKey: req.ShortcutKey,
158134 })
159135 if err != nil {
@@ -259,42 +235,3 @@ func (h *FavoritesHandlers) HandleGetByShortcut() port.WebUIMessageHandler {
259235 return NewSuccessResponse(req.RequestID, favorite), nil
260236 })
261237 }
262-
263-// setFolderRequest is the payload for favorite_set_folder messages.
264-type setFolderRequest struct {
265- RequestID string `json:"requestId"`
266- FavoriteID int64 `json:"favorite_id"`
267- FolderID *int64 `json:"folder_id"` // null to move to root
268-}
269-
270-// HandleSetFolder handles favorite_set_folder messages.
271-func (h *FavoritesHandlers) HandleSetFolder() port.WebUIMessageHandler {
272- return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
273- log := logging.FromContext(ctx)
274-
275- var req setFolderRequest
276- if err := json.Unmarshal(payload, &req); err != nil {
277- return NewErrorResponse("", err), nil
278- }
279-
280- log.Debug().
281- Str("request_id", req.RequestID).
282- Int64("favorite_id", req.FavoriteID).
283- Msg("handling favorite_set_folder")
284-
285- if req.FavoriteID <= 0 {
286- return NewErrorResponse(req.RequestID, fmt.Errorf("invalid favorite_id")), nil
287- }
288-
289- folderID, err := folderIDFromInt64(req.FolderID)
290- if err != nil {
291- return NewErrorResponse(req.RequestID, err), nil
292- }
293-
294- if err := h.favoritesUC.Move(ctx, entity.FavoriteID(req.FavoriteID), folderID); err != nil {
295- return NewErrorResponse(req.RequestID, err), nil
296- }
297-
298- return NewSuccessResponse(req.RequestID, nil), nil
299- })
300-}
@@ -12,17 +12,6 @@ import (
12 "github.com/bnema/dumber/internal/logging"12 "github.com/bnema/dumber/internal/logging"
13 )13 )
14 14
15-func folderIDFromInt64(id *int64) (*entity.FolderID, error) {
16- if id == nil {
17- return nil, nil
18- }
19- if *id <= 0 {
20- return nil, fmt.Errorf("folder id must be positive")
21- }
22- folderID := entity.FolderID(*id)
23- return &folderID, nil
24-}
25-
26 func tagIDsFromInt64s(ids []int64) ([]entity.TagID, error) {15 func tagIDsFromInt64s(ids []int64) ([]entity.TagID, error) {
27 out := make([]entity.TagID, 0, len(ids))16 out := make([]entity.TagID, 0, len(ids))
28 for _, id := range ids {17 for _, id := range ids {
@@ -69,7 +58,6 @@ type favoriteCreateRequest struct {
69 URL string `json:"url"`58 URL string `json:"url"`
70 Title string `json:"title"`59 Title string `json:"title"`
71 FaviconURL string `json:"favicon_url"`60 FaviconURL string `json:"favicon_url"`
72- FolderID *int64 `json:"folder_id"`
73 Tags []int64 `json:"tags"`61 Tags []int64 `json:"tags"`
74 }62 }
75 63
@@ -91,10 +79,6 @@ func (h *FavoritesHandlers) HandleCreate() port.WebUIMessageHandler {
91 if trimmedURL == "" {79 if trimmedURL == "" {
92 return NewErrorResponse(req.RequestID, fmt.Errorf("URL is required")), nil80 return NewErrorResponse(req.RequestID, fmt.Errorf("URL is required")), nil
93 }81 }
94- folderID, err := folderIDFromInt64(req.FolderID)
95- if err != nil {
96- return NewErrorResponse(req.RequestID, err), nil
97- }
98 tags, err := tagIDsFromInt64s(req.Tags)82 tags, err := tagIDsFromInt64s(req.Tags)
99 if err != nil {83 if err != nil {
100 return NewErrorResponse(req.RequestID, err), nil84 return NewErrorResponse(req.RequestID, err), nil
@@ -104,7 +88,6 @@ func (h *FavoritesHandlers) HandleCreate() port.WebUIMessageHandler {
104 URL: trimmedURL,88 URL: trimmedURL,
105 Title: req.Title,89 Title: req.Title,
106 FaviconURL: req.FaviconURL,90 FaviconURL: req.FaviconURL,
107- FolderID: folderID,
108 Tags: tags,91 Tags: tags,
109 }92 }
110 favorite, err := h.favoritesUC.AddFavorite(ctx, input)93 favorite, err := h.favoritesUC.AddFavorite(ctx, input)
@@ -121,7 +104,6 @@ type favoriteUpdateRequest struct {
121 ID int64 `json:"id"`104 ID int64 `json:"id"`
122 Title string `json:"title"`105 Title string `json:"title"`
123 FaviconURL string `json:"favicon_url"`106 FaviconURL string `json:"favicon_url"`
124- FolderID *int64 `json:"folder_id"`
125 ShortcutKey *int `json:"shortcut_key"`107 ShortcutKey *int `json:"shortcut_key"`
126 }108 }
127 109
@@ -144,16 +126,10 @@ func (h *FavoritesHandlers) HandleUpdate() port.WebUIMessageHandler {
144 return NewErrorResponse(req.RequestID, fmt.Errorf("favorite id must be positive")), nil126 return NewErrorResponse(req.RequestID, fmt.Errorf("favorite id must be positive")), nil
145 }127 }
146 128
147- folderID, err := folderIDFromInt64(req.FolderID)
148- if err != nil {
149- return NewErrorResponse(req.RequestID, err), nil
150- }
151-
152 favorite, err := h.favoritesUC.UpdateFavorite(ctx, dto.FavoriteUpdateInput{129 favorite, err := h.favoritesUC.UpdateFavorite(ctx, dto.FavoriteUpdateInput{
153 ID: entity.FavoriteID(req.ID),130 ID: entity.FavoriteID(req.ID),
154 Title: req.Title,131 Title: req.Title,
155 FaviconURL: req.FaviconURL,132 FaviconURL: req.FaviconURL,
156- FolderID: folderID,
157 ShortcutKey: req.ShortcutKey,133 ShortcutKey: req.ShortcutKey,
158 })134 })
159 if err != nil {135 if err != nil {
@@ -259,42 +235,3 @@ func (h *FavoritesHandlers) HandleGetByShortcut() port.WebUIMessageHandler {
259 return NewSuccessResponse(req.RequestID, favorite), nil235 return NewSuccessResponse(req.RequestID, favorite), nil
260 })236 })
261 }237 }
262-
263-// setFolderRequest is the payload for favorite_set_folder messages.
264-type setFolderRequest struct {
265- RequestID string `json:"requestId"`
266- FavoriteID int64 `json:"favorite_id"`
267- FolderID *int64 `json:"folder_id"` // null to move to root
268-}
269-
270-// HandleSetFolder handles favorite_set_folder messages.
271-func (h *FavoritesHandlers) HandleSetFolder() port.WebUIMessageHandler {
272- return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
273- log := logging.FromContext(ctx)
274-
275- var req setFolderRequest
276- if err := json.Unmarshal(payload, &req); err != nil {
277- return NewErrorResponse("", err), nil
278- }
279-
280- log.Debug().
281- Str("request_id", req.RequestID).
282- Int64("favorite_id", req.FavoriteID).
283- Msg("handling favorite_set_folder")
284-
285- if req.FavoriteID <= 0 {
286- return NewErrorResponse(req.RequestID, fmt.Errorf("invalid favorite_id")), nil
287- }
288-
289- folderID, err := folderIDFromInt64(req.FolderID)
290- if err != nil {
291- return NewErrorResponse(req.RequestID, err), nil
292- }
293-
294- if err := h.favoritesUC.Move(ctx, entity.FavoriteID(req.FavoriteID), folderID); err != nil {
295- return NewErrorResponse(req.RequestID, err), nil
296- }
297-
298- return NewSuccessResponse(req.RequestID, nil), nil
299- })
300-}
A internal/infrastructure/handlers/homepage/favorites_test.go +73 -0
@@ -0,0 +1,73 @@
1+package homepage
2+
3+import (
4+ "context"
5+ "encoding/json"
6+ "testing"
7+
8+ "github.com/bnema/dumber/internal/application/dto"
9+ "github.com/bnema/dumber/internal/application/port"
10+ portmocks "github.com/bnema/dumber/internal/application/port/mocks"
11+ "github.com/bnema/dumber/internal/domain/entity"
12+ "github.com/stretchr/testify/mock"
13+ "github.com/stretchr/testify/require"
14+)
15+
16+func TestHandleFavoriteCreatePassesTagsOnlyPayload(t *testing.T) {
17+ t.Parallel()
18+
19+ favorites := portmocks.NewMockHomepageFavorites(t)
20+ favorites.EXPECT().
21+ AddFavorite(mock.Anything, dto.FavoriteCreateInput{
22+ URL: "https://example.com",
23+ Title: "Example",
24+ Tags: []entity.TagID{7, 9},
25+ }).
26+ Return(&entity.Favorite{ID: 1, URL: "https://example.com", Title: "Example"}, nil).
27+ Once()
28+
29+ handler := NewFavoritesHandlers(favorites).HandleCreate()
30+ got, err := handler.Handle(context.Background(), port.WebViewID(0), json.RawMessage(`{"requestId":"req-1","url":" https://example.com ","title":"Example","tags":[7,9]}`))
31+ require.NoError(t, err)
32+
33+ resp, ok := got.(Response)
34+ require.True(t, ok)
35+ require.True(t, resp.Success)
36+}
37+
38+func TestHandleFavoriteCreateRejectsInvalidTagIDs(t *testing.T) {
39+ t.Parallel()
40+
41+ handler := NewFavoritesHandlers(portmocks.NewMockHomepageFavorites(t)).HandleCreate()
42+ got, err := handler.Handle(context.Background(), port.WebViewID(0), json.RawMessage(`{"requestId":"req-2","url":"https://example.com","tags":[0]}`))
43+ require.NoError(t, err)
44+
45+ resp, ok := got.(Response)
46+ require.True(t, ok)
47+ require.False(t, resp.Success)
48+ require.Contains(t, resp.Error, "tag id must be positive")
49+}
50+
51+func TestHandleFavoriteUpdatePassesTagsOnlyPayload(t *testing.T) {
52+ t.Parallel()
53+
54+ shortcut := 4
55+ favorites := portmocks.NewMockHomepageFavorites(t)
56+ favorites.EXPECT().
57+ UpdateFavorite(mock.Anything, dto.FavoriteUpdateInput{
58+ ID: entity.FavoriteID(42),
59+ Title: "Updated",
60+ FaviconURL: "https://example.com/favicon.ico",
61+ ShortcutKey: &shortcut,
62+ }).
63+ Return(&entity.Favorite{ID: 42, URL: "https://example.com", Title: "Updated", ShortcutKey: &shortcut}, nil).
64+ Once()
65+
66+ handler := NewFavoritesHandlers(favorites).HandleUpdate()
67+ got, err := handler.Handle(context.Background(), port.WebViewID(0), json.RawMessage(`{"requestId":"req-3","id":42,"title":"Updated","favicon_url":"https://example.com/favicon.ico","shortcut_key":4}`))
68+ require.NoError(t, err)
69+
70+ resp, ok := got.(Response)
71+ require.True(t, ok)
72+ require.True(t, resp.Success)
73+}
@@ -0,0 +1,73 @@
1+package homepage
2+
3+import (
4+ "context"
5+ "encoding/json"
6+ "testing"
7+
8+ "github.com/bnema/dumber/internal/application/dto"
9+ "github.com/bnema/dumber/internal/application/port"
10+ portmocks "github.com/bnema/dumber/internal/application/port/mocks"
11+ "github.com/bnema/dumber/internal/domain/entity"
12+ "github.com/stretchr/testify/mock"
13+ "github.com/stretchr/testify/require"
14+)
15+
16+func TestHandleFavoriteCreatePassesTagsOnlyPayload(t *testing.T) {
17+ t.Parallel()
18+
19+ favorites := portmocks.NewMockHomepageFavorites(t)
20+ favorites.EXPECT().
21+ AddFavorite(mock.Anything, dto.FavoriteCreateInput{
22+ URL: "https://example.com",
23+ Title: "Example",
24+ Tags: []entity.TagID{7, 9},
25+ }).
26+ Return(&entity.Favorite{ID: 1, URL: "https://example.com", Title: "Example"}, nil).
27+ Once()
28+
29+ handler := NewFavoritesHandlers(favorites).HandleCreate()
30+ got, err := handler.Handle(context.Background(), port.WebViewID(0), json.RawMessage(`{"requestId":"req-1","url":" https://example.com ","title":"Example","tags":[7,9]}`))
31+ require.NoError(t, err)
32+
33+ resp, ok := got.(Response)
34+ require.True(t, ok)
35+ require.True(t, resp.Success)
36+}
37+
38+func TestHandleFavoriteCreateRejectsInvalidTagIDs(t *testing.T) {
39+ t.Parallel()
40+
41+ handler := NewFavoritesHandlers(portmocks.NewMockHomepageFavorites(t)).HandleCreate()
42+ got, err := handler.Handle(context.Background(), port.WebViewID(0), json.RawMessage(`{"requestId":"req-2","url":"https://example.com","tags":[0]}`))
43+ require.NoError(t, err)
44+
45+ resp, ok := got.(Response)
46+ require.True(t, ok)
47+ require.False(t, resp.Success)
48+ require.Contains(t, resp.Error, "tag id must be positive")
49+}
50+
51+func TestHandleFavoriteUpdatePassesTagsOnlyPayload(t *testing.T) {
52+ t.Parallel()
53+
54+ shortcut := 4
55+ favorites := portmocks.NewMockHomepageFavorites(t)
56+ favorites.EXPECT().
57+ UpdateFavorite(mock.Anything, dto.FavoriteUpdateInput{
58+ ID: entity.FavoriteID(42),
59+ Title: "Updated",
60+ FaviconURL: "https://example.com/favicon.ico",
61+ ShortcutKey: &shortcut,
62+ }).
63+ Return(&entity.Favorite{ID: 42, URL: "https://example.com", Title: "Updated", ShortcutKey: &shortcut}, nil).
64+ Once()
65+
66+ handler := NewFavoritesHandlers(favorites).HandleUpdate()
67+ got, err := handler.Handle(context.Background(), port.WebViewID(0), json.RawMessage(`{"requestId":"req-3","id":42,"title":"Updated","favicon_url":"https://example.com/favicon.ico","shortcut_key":4}`))
68+ require.NoError(t, err)
69+
70+ resp, ok := got.(Response)
71+ require.True(t, ok)
72+ require.True(t, resp.Success)
73+}
M internal/infrastructure/handlers/homepage/folders.go +1 -144
@@ -1,146 +1,3 @@
11 package homepage
22
3-import (
4- "context"
5- "encoding/json"
6-
7- "github.com/bnema/dumber/internal/application/port"
8- "github.com/bnema/dumber/internal/domain/entity"
9- "github.com/bnema/dumber/internal/logging"
10-)
11-
12-// FolderHandlers handles folder-related messages from the homepage.
13-type FolderHandlers struct {
14- favoritesUC port.HomepageFavorites
15-}
16-
17-// NewFolderHandlers creates a new FolderHandlers instance.
18-func NewFolderHandlers(favoritesUC port.HomepageFavorites) *FolderHandlers {
19- return &FolderHandlers{favoritesUC: favoritesUC}
20-}
21-
22-// HandleList handles folder_list messages.
23-func (h *FolderHandlers) HandleList() port.WebUIMessageHandler {
24- return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
25- log := logging.FromContext(ctx)
26-
27- requestID := ParseRequestID(payload)
28-
29- log.Debug().
30- Str("request_id", requestID).
31- Msg("handling folder_list")
32-
33- folders, err := h.favoritesUC.GetAllFolders(ctx)
34- if err != nil {
35- return NewErrorResponse(requestID, err), nil
36- }
37-
38- return NewSuccessResponse(requestID, folders), nil
39- })
40-}
41-
42-// createFolderRequest is the payload for folder_create messages.
43-type createFolderRequest struct {
44- RequestID string `json:"requestId"`
45- Name string `json:"name"`
46- Icon *string `json:"icon"`
47- ParentID *int64 `json:"parent_id"`
48-}
49-
50-// HandleCreate handles folder_create messages.
51-func (h *FolderHandlers) HandleCreate() port.WebUIMessageHandler {
52- return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
53- log := logging.FromContext(ctx)
54-
55- var req createFolderRequest
56- if err := json.Unmarshal(payload, &req); err != nil {
57- return NewErrorResponse("", err), nil
58- }
59-
60- log.Debug().
61- Str("request_id", req.RequestID).
62- Str("name", req.Name).
63- Msg("handling folder_create")
64-
65- parentID, err := folderIDFromInt64(req.ParentID)
66- if err != nil {
67- return NewErrorResponse(req.RequestID, err), nil
68- }
69-
70- folder, err := h.favoritesUC.CreateFolder(ctx, req.Name, optionalStringValue(req.Icon), parentID)
71- if err != nil {
72- return NewErrorResponse(req.RequestID, err), nil
73- }
74-
75- return NewSuccessResponse(req.RequestID, folder), nil
76- })
77-}
78-
79-// deleteFolderRequest is the payload for folder_delete messages.
80-type deleteFolderRequest struct {
81- RequestID string `json:"requestId"`
82- ID int64 `json:"id"`
83-}
84-
85-// HandleDelete handles folder_delete messages.
86-func (h *FolderHandlers) HandleDelete() port.WebUIMessageHandler {
87- return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
88- log := logging.FromContext(ctx)
89-
90- var req deleteFolderRequest
91- if err := json.Unmarshal(payload, &req); err != nil {
92- return NewErrorResponse("", err), nil
93- }
94-
95- log.Debug().
96- Str("request_id", req.RequestID).
97- Int64("id", req.ID).
98- Msg("handling folder_delete")
99-
100- if err := h.favoritesUC.DeleteFolder(ctx, entity.FolderID(req.ID)); err != nil {
101- return NewErrorResponse(req.RequestID, err), nil
102- }
103-
104- return NewSuccessResponse(req.RequestID, nil), nil
105- })
106-}
107-
108-// updateFolderRequest is the payload for folder_update messages.
109-type updateFolderRequest struct {
110- RequestID string `json:"requestId"`
111- ID int64 `json:"id"`
112- Name string `json:"name"`
113- Icon *string `json:"icon"`
114-}
115-
116-// HandleUpdate handles folder_update messages.
117-// It delegates to the port.HomepageFavorites interface's UpdateFolder method.
118-func optionalStringValue(value *string) string {
119- if value == nil {
120- return ""
121- }
122- return *value
123-}
124-
125-func (h *FolderHandlers) HandleUpdate() port.WebUIMessageHandler {
126- return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
127- log := logging.FromContext(ctx)
128-
129- var req updateFolderRequest
130- if err := json.Unmarshal(payload, &req); err != nil {
131- return NewErrorResponse("", err), nil
132- }
133-
134- log.Debug().
135- Str("request_id", req.RequestID).
136- Int64("id", req.ID).
137- Str("name", req.Name).
138- Msg("handling folder_update")
139-
140- if err := h.favoritesUC.UpdateFolder(ctx, entity.FolderID(req.ID), req.Name, optionalStringValue(req.Icon)); err != nil {
141- return NewErrorResponse(req.RequestID, err), nil
142- }
143-
144- return NewSuccessResponse(req.RequestID, nil), nil
145- })
146-}
3+// Folder homepage handlers were removed with tags-only favorites.
@@ -1,146 +1,3 @@
1 package homepage1 package homepage
2 2
3-import (3+// Folder homepage handlers were removed with tags-only favorites.
4- "context"
5- "encoding/json"
6-
7- "github.com/bnema/dumber/internal/application/port"
8- "github.com/bnema/dumber/internal/domain/entity"
9- "github.com/bnema/dumber/internal/logging"
10-)
11-
12-// FolderHandlers handles folder-related messages from the homepage.
13-type FolderHandlers struct {
14- favoritesUC port.HomepageFavorites
15-}
16-
17-// NewFolderHandlers creates a new FolderHandlers instance.
18-func NewFolderHandlers(favoritesUC port.HomepageFavorites) *FolderHandlers {
19- return &FolderHandlers{favoritesUC: favoritesUC}
20-}
21-
22-// HandleList handles folder_list messages.
23-func (h *FolderHandlers) HandleList() port.WebUIMessageHandler {
24- return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
25- log := logging.FromContext(ctx)
26-
27- requestID := ParseRequestID(payload)
28-
29- log.Debug().
30- Str("request_id", requestID).
31- Msg("handling folder_list")
32-
33- folders, err := h.favoritesUC.GetAllFolders(ctx)
34- if err != nil {
35- return NewErrorResponse(requestID, err), nil
36- }
37-
38- return NewSuccessResponse(requestID, folders), nil
39- })
40-}
41-
42-// createFolderRequest is the payload for folder_create messages.
43-type createFolderRequest struct {
44- RequestID string `json:"requestId"`
45- Name string `json:"name"`
46- Icon *string `json:"icon"`
47- ParentID *int64 `json:"parent_id"`
48-}
49-
50-// HandleCreate handles folder_create messages.
51-func (h *FolderHandlers) HandleCreate() port.WebUIMessageHandler {
52- return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
53- log := logging.FromContext(ctx)
54-
55- var req createFolderRequest
56- if err := json.Unmarshal(payload, &req); err != nil {
57- return NewErrorResponse("", err), nil
58- }
59-
60- log.Debug().
61- Str("request_id", req.RequestID).
62- Str("name", req.Name).
63- Msg("handling folder_create")
64-
65- parentID, err := folderIDFromInt64(req.ParentID)
66- if err != nil {
67- return NewErrorResponse(req.RequestID, err), nil
68- }
69-
70- folder, err := h.favoritesUC.CreateFolder(ctx, req.Name, optionalStringValue(req.Icon), parentID)
71- if err != nil {
72- return NewErrorResponse(req.RequestID, err), nil
73- }
74-
75- return NewSuccessResponse(req.RequestID, folder), nil
76- })
77-}
78-
79-// deleteFolderRequest is the payload for folder_delete messages.
80-type deleteFolderRequest struct {
81- RequestID string `json:"requestId"`
82- ID int64 `json:"id"`
83-}
84-
85-// HandleDelete handles folder_delete messages.
86-func (h *FolderHandlers) HandleDelete() port.WebUIMessageHandler {
87- return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
88- log := logging.FromContext(ctx)
89-
90- var req deleteFolderRequest
91- if err := json.Unmarshal(payload, &req); err != nil {
92- return NewErrorResponse("", err), nil
93- }
94-
95- log.Debug().
96- Str("request_id", req.RequestID).
97- Int64("id", req.ID).
98- Msg("handling folder_delete")
99-
100- if err := h.favoritesUC.DeleteFolder(ctx, entity.FolderID(req.ID)); err != nil {
101- return NewErrorResponse(req.RequestID, err), nil
102- }
103-
104- return NewSuccessResponse(req.RequestID, nil), nil
105- })
106-}
107-
108-// updateFolderRequest is the payload for folder_update messages.
109-type updateFolderRequest struct {
110- RequestID string `json:"requestId"`
111- ID int64 `json:"id"`
112- Name string `json:"name"`
113- Icon *string `json:"icon"`
114-}
115-
116-// HandleUpdate handles folder_update messages.
117-// It delegates to the port.HomepageFavorites interface's UpdateFolder method.
118-func optionalStringValue(value *string) string {
119- if value == nil {
120- return ""
121- }
122- return *value
123-}
124-
125-func (h *FolderHandlers) HandleUpdate() port.WebUIMessageHandler {
126- return port.WebUIMessageHandlerFunc(func(ctx context.Context, _ port.WebViewID, payload json.RawMessage) (any, error) {
127- log := logging.FromContext(ctx)
128-
129- var req updateFolderRequest
130- if err := json.Unmarshal(payload, &req); err != nil {
131- return NewErrorResponse("", err), nil
132- }
133-
134- log.Debug().
135- Str("request_id", req.RequestID).
136- Int64("id", req.ID).
137- Str("name", req.Name).
138- Msg("handling folder_update")
139-
140- if err := h.favoritesUC.UpdateFolder(ctx, entity.FolderID(req.ID), req.Name, optionalStringValue(req.Icon)); err != nil {
141- return NewErrorResponse(req.RequestID, err), nil
142- }
143-
144- return NewSuccessResponse(req.RequestID, nil), nil
145- })
146-}
M internal/infrastructure/handlers/homepage/registry.go +0 -9
@@ -48,15 +48,6 @@ func RegisterHandlers(ctx context.Context, router port.WebUIHandlerRouter, cfg C
4848 handlers["favorite_delete"] = favoritesHandlers.HandleDelete()
4949 handlers["favorite_set_shortcut"] = favoritesHandlers.HandleSetShortcut()
5050 handlers["favorite_get_by_shortcut"] = favoritesHandlers.HandleGetByShortcut()
51- handlers["favorite_set_folder"] = favoritesHandlers.HandleSetFolder()
52-
53- // Folder handlers
54- folderHandlers := NewFolderHandlers(cfg.FavoritesUC)
55- handlers["folder_list"] = folderHandlers.HandleList()
56- handlers["folder_create"] = folderHandlers.HandleCreate()
57- handlers["folder_delete"] = folderHandlers.HandleDelete()
58- handlers["folder_update"] = folderHandlers.HandleUpdate() // Requires UpdateFolder()
59-
6051 // Tag handlers
6152 tagHandlers := NewTagHandlers(cfg.FavoritesUC)
6253 handlers["tag_list"] = tagHandlers.HandleList()
@@ -48,15 +48,6 @@ func RegisterHandlers(ctx context.Context, router port.WebUIHandlerRouter, cfg C
48 handlers["favorite_delete"] = favoritesHandlers.HandleDelete()48 handlers["favorite_delete"] = favoritesHandlers.HandleDelete()
49 handlers["favorite_set_shortcut"] = favoritesHandlers.HandleSetShortcut()49 handlers["favorite_set_shortcut"] = favoritesHandlers.HandleSetShortcut()
50 handlers["favorite_get_by_shortcut"] = favoritesHandlers.HandleGetByShortcut()50 handlers["favorite_get_by_shortcut"] = favoritesHandlers.HandleGetByShortcut()
51- handlers["favorite_set_folder"] = favoritesHandlers.HandleSetFolder()
52-
53- // Folder handlers
54- folderHandlers := NewFolderHandlers(cfg.FavoritesUC)
55- handlers["folder_list"] = folderHandlers.HandleList()
56- handlers["folder_create"] = folderHandlers.HandleCreate()
57- handlers["folder_delete"] = folderHandlers.HandleDelete()
58- handlers["folder_update"] = folderHandlers.HandleUpdate() // Requires UpdateFolder()
59-
60 // Tag handlers51 // Tag handlers
61 tagHandlers := NewTagHandlers(cfg.FavoritesUC)52 tagHandlers := NewTagHandlers(cfg.FavoritesUC)
62 handlers["tag_list"] = tagHandlers.HandleList()53 handlers["tag_list"] = tagHandlers.HandleList()
M internal/infrastructure/systemviewsbridge/browser_transport_js.go +2 -2
@@ -25,8 +25,8 @@ func NewBrowserClient() *Client {
2525 func callbackPlanForMessage(msgType string) (callbackPlan, bool) {
2626 switch msgType {
2727 case "history_timeline", "history_timeline_by_domain", "history_timeline_window", "history_search_fts", "history_delete_entry", "history_delete_range", "history_stats", "history_analytics",
28- "history_domain_stats", "history_delete_domain", "favorite_list", "favorite_create", "favorite_update", "favorite_delete", "folder_list", "tag_list",
29- "favorite_set_shortcut", "favorite_set_folder", "folder_create", "folder_update", "folder_delete",
28+ "history_domain_stats", "history_delete_domain", "favorite_list", "favorite_create", "favorite_update", "favorite_delete", "tag_list",
29+ "favorite_set_shortcut",
3030 "tag_create", "tag_update", "tag_delete", "tag_assign", "tag_remove":
3131 return callbackPlan{success: "__dumber_homepage_response", failure: "__dumber_error"}, true
3232 case "save_config":
@@ -25,8 +25,8 @@ func NewBrowserClient() *Client {
25 func callbackPlanForMessage(msgType string) (callbackPlan, bool) {25 func callbackPlanForMessage(msgType string) (callbackPlan, bool) {
26 switch msgType {26 switch msgType {
27 case "history_timeline", "history_timeline_by_domain", "history_timeline_window", "history_search_fts", "history_delete_entry", "history_delete_range", "history_stats", "history_analytics",27 case "history_timeline", "history_timeline_by_domain", "history_timeline_window", "history_search_fts", "history_delete_entry", "history_delete_range", "history_stats", "history_analytics",
28- "history_domain_stats", "history_delete_domain", "favorite_list", "favorite_create", "favorite_update", "favorite_delete", "folder_list", "tag_list",28+ "history_domain_stats", "history_delete_domain", "favorite_list", "favorite_create", "favorite_update", "favorite_delete", "tag_list",
29- "favorite_set_shortcut", "favorite_set_folder", "folder_create", "folder_update", "folder_delete",29+ "favorite_set_shortcut",
30 "tag_create", "tag_update", "tag_delete", "tag_assign", "tag_remove":30 "tag_create", "tag_update", "tag_delete", "tag_assign", "tag_remove":
31 return callbackPlan{success: "__dumber_homepage_response", failure: "__dumber_error"}, true31 return callbackPlan{success: "__dumber_homepage_response", failure: "__dumber_error"}, true
32 case "save_config":32 case "save_config":
M internal/infrastructure/systemviewsbridge/client.go +0 -79
@@ -272,14 +272,12 @@ func favoriteCreatePayload(input dto.FavoriteCreateInput) any {
272272 URL string `json:"url"`
273273 Title string `json:"title"`
274274 FaviconURL string `json:"favicon_url"`
275- FolderID *int64 `json:"folder_id"`
276275 Tags []int64 `json:"tags"`
277276 }{
278277 RequestID: nextRequestID(),
279278 URL: input.URL,
280279 Title: input.Title,
281280 FaviconURL: input.FaviconURL,
282- FolderID: folderIDPayload(input.FolderID),
283281 Tags: tagIDPayloads(input.Tags),
284282 }
285283 }
@@ -290,26 +288,16 @@ func favoriteUpdatePayload(input dto.FavoriteUpdateInput) any {
290288 ID int64 `json:"id"`
291289 Title string `json:"title"`
292290 FaviconURL string `json:"favicon_url"`
293- FolderID *int64 `json:"folder_id"`
294291 ShortcutKey *int `json:"shortcut_key"`
295292 }{
296293 RequestID: nextRequestID(),
297294 ID: int64(input.ID),
298295 Title: input.Title,
299296 FaviconURL: input.FaviconURL,
300- FolderID: folderIDPayload(input.FolderID),
301297 ShortcutKey: input.ShortcutKey,
302298 }
303299 }
304300
305-func folderIDPayload(id *entity.FolderID) *int64 {
306- if id == nil {
307- return nil
308- }
309- value := int64(*id)
310- return &value
311-}
312-
313301 func tagIDPayloads(ids []entity.TagID) []int64 {
314302 out := make([]int64, 0, len(ids))
315303 for _, id := range ids {
@@ -318,12 +306,6 @@ func tagIDPayloads(ids []entity.TagID) []int64 {
318306 return out
319307 }
320308
321-func (c *Client) ListFolders(ctx context.Context) ([]*entity.Folder, error) {
322- return request[[]*entity.Folder](c, ctx, "folder_list", struct {
323- RequestID string `json:"requestId"`
324- }{RequestID: nextRequestID()})
325-}
326-
327309 func (c *Client) ListTags(ctx context.Context) ([]*entity.Tag, error) {
328310 return request[[]*entity.Tag](c, ctx, "tag_list", struct {
329311 RequestID string `json:"requestId"`
@@ -342,67 +324,6 @@ func (c *Client) SetShortcut(ctx context.Context, favoriteID int64, shortcutKey
342324 return err
343325 }
344326
345-func (c *Client) SetFolder(ctx context.Context, favoriteID int64, folderID *int64) error {
346- if favoriteID <= 0 {
347- return fmt.Errorf("set folder favorite id must be positive, got %d", favoriteID)
348- }
349- _, err := request[struct{}](c, ctx, "favorite_set_folder", struct {
350- RequestID string `json:"requestId"`
351- FavoriteID int64 `json:"favorite_id"`
352- FolderID *int64 `json:"folder_id"`
353- }{RequestID: nextRequestID(), FavoriteID: favoriteID, FolderID: folderID})
354- return err
355-}
356-
357-func (c *Client) CreateFolder(ctx context.Context, name, icon string, parentID *int64) (*entity.Folder, error) {
358- name = strings.TrimSpace(name)
359- if name == "" {
360- return nil, fmt.Errorf("folder name is required")
361- }
362- var iconPtr *string
363- if trimmedIcon := strings.TrimSpace(icon); trimmedIcon != "" {
364- iconPtr = &trimmedIcon
365- }
366- return request[*entity.Folder](c, ctx, "folder_create", struct {
367- RequestID string `json:"requestId"`
368- Name string `json:"name"`
369- Icon *string `json:"icon"`
370- ParentID *int64 `json:"parent_id,omitempty"`
371- }{RequestID: nextRequestID(), Name: name, Icon: iconPtr, ParentID: parentID})
372-}
373-
374-func (c *Client) UpdateFolder(ctx context.Context, id int64, name, icon string) error {
375- if id <= 0 {
376- return fmt.Errorf("update folder id must be positive, got %d", id)
377- }
378- name = strings.TrimSpace(name)
379- if name == "" {
380- return fmt.Errorf("folder name is required")
381- }
382- var iconPtr *string
383- if trimmed := strings.TrimSpace(icon); trimmed != "" {
384- iconPtr = &trimmed
385- }
386- _, err := request[struct{}](c, ctx, "folder_update", struct {
387- RequestID string `json:"requestId"`
388- ID int64 `json:"id"`
389- Name string `json:"name"`
390- Icon *string `json:"icon"`
391- }{RequestID: nextRequestID(), ID: id, Name: name, Icon: iconPtr})
392- return err
393-}
394-
395-func (c *Client) DeleteFolder(ctx context.Context, id int64) error {
396- if id <= 0 {
397- return fmt.Errorf("delete folder id must be positive, got %d", id)
398- }
399- _, err := request[struct{}](c, ctx, "folder_delete", struct {
400- RequestID string `json:"requestId"`
401- ID int64 `json:"id"`
402- }{RequestID: nextRequestID(), ID: id})
403- return err
404-}
405-
406327 func (c *Client) CreateTag(ctx context.Context, name, color string) (*entity.Tag, error) {
407328 name = strings.TrimSpace(name)
408329 if name == "" {
@@ -272,14 +272,12 @@ func favoriteCreatePayload(input dto.FavoriteCreateInput) any {
272 URL string `json:"url"`272 URL string `json:"url"`
273 Title string `json:"title"`273 Title string `json:"title"`
274 FaviconURL string `json:"favicon_url"`274 FaviconURL string `json:"favicon_url"`
275- FolderID *int64 `json:"folder_id"`
276 Tags []int64 `json:"tags"`275 Tags []int64 `json:"tags"`
277 }{276 }{
278 RequestID: nextRequestID(),277 RequestID: nextRequestID(),
279 URL: input.URL,278 URL: input.URL,
280 Title: input.Title,279 Title: input.Title,
281 FaviconURL: input.FaviconURL,280 FaviconURL: input.FaviconURL,
282- FolderID: folderIDPayload(input.FolderID),
283 Tags: tagIDPayloads(input.Tags),281 Tags: tagIDPayloads(input.Tags),
284 }282 }
285 }283 }
@@ -290,26 +288,16 @@ func favoriteUpdatePayload(input dto.FavoriteUpdateInput) any {
290 ID int64 `json:"id"`288 ID int64 `json:"id"`
291 Title string `json:"title"`289 Title string `json:"title"`
292 FaviconURL string `json:"favicon_url"`290 FaviconURL string `json:"favicon_url"`
293- FolderID *int64 `json:"folder_id"`
294 ShortcutKey *int `json:"shortcut_key"`291 ShortcutKey *int `json:"shortcut_key"`
295 }{292 }{
296 RequestID: nextRequestID(),293 RequestID: nextRequestID(),
297 ID: int64(input.ID),294 ID: int64(input.ID),
298 Title: input.Title,295 Title: input.Title,
299 FaviconURL: input.FaviconURL,296 FaviconURL: input.FaviconURL,
300- FolderID: folderIDPayload(input.FolderID),
301 ShortcutKey: input.ShortcutKey,297 ShortcutKey: input.ShortcutKey,
302 }298 }
303 }299 }
304 300
305-func folderIDPayload(id *entity.FolderID) *int64 {
306- if id == nil {
307- return nil
308- }
309- value := int64(*id)
310- return &value
311-}
312-
313 func tagIDPayloads(ids []entity.TagID) []int64 {301 func tagIDPayloads(ids []entity.TagID) []int64 {
314 out := make([]int64, 0, len(ids))302 out := make([]int64, 0, len(ids))
315 for _, id := range ids {303 for _, id := range ids {
@@ -318,12 +306,6 @@ func tagIDPayloads(ids []entity.TagID) []int64 {
318 return out306 return out
319 }307 }
320 308
321-func (c *Client) ListFolders(ctx context.Context) ([]*entity.Folder, error) {
322- return request[[]*entity.Folder](c, ctx, "folder_list", struct {
323- RequestID string `json:"requestId"`
324- }{RequestID: nextRequestID()})
325-}
326-
327 func (c *Client) ListTags(ctx context.Context) ([]*entity.Tag, error) {309 func (c *Client) ListTags(ctx context.Context) ([]*entity.Tag, error) {
328 return request[[]*entity.Tag](c, ctx, "tag_list", struct {310 return request[[]*entity.Tag](c, ctx, "tag_list", struct {
329 RequestID string `json:"requestId"`311 RequestID string `json:"requestId"`
@@ -342,67 +324,6 @@ func (c *Client) SetShortcut(ctx context.Context, favoriteID int64, shortcutKey
342 return err324 return err
343 }325 }
344 326
345-func (c *Client) SetFolder(ctx context.Context, favoriteID int64, folderID *int64) error {
346- if favoriteID <= 0 {
347- return fmt.Errorf("set folder favorite id must be positive, got %d", favoriteID)
348- }
349- _, err := request[struct{}](c, ctx, "favorite_set_folder", struct {
350- RequestID string `json:"requestId"`
351- FavoriteID int64 `json:"favorite_id"`
352- FolderID *int64 `json:"folder_id"`
353- }{RequestID: nextRequestID(), FavoriteID: favoriteID, FolderID: folderID})
354- return err
355-}
356-
357-func (c *Client) CreateFolder(ctx context.Context, name, icon string, parentID *int64) (*entity.Folder, error) {
358- name = strings.TrimSpace(name)
359- if name == "" {
360- return nil, fmt.Errorf("folder name is required")
361- }
362- var iconPtr *string
363- if trimmedIcon := strings.TrimSpace(icon); trimmedIcon != "" {
364- iconPtr = &trimmedIcon
365- }
366- return request[*entity.Folder](c, ctx, "folder_create", struct {
367- RequestID string `json:"requestId"`
368- Name string `json:"name"`
369- Icon *string `json:"icon"`
370- ParentID *int64 `json:"parent_id,omitempty"`
371- }{RequestID: nextRequestID(), Name: name, Icon: iconPtr, ParentID: parentID})
372-}
373-
374-func (c *Client) UpdateFolder(ctx context.Context, id int64, name, icon string) error {
375- if id <= 0 {
376- return fmt.Errorf("update folder id must be positive, got %d", id)
377- }
378- name = strings.TrimSpace(name)
379- if name == "" {
380- return fmt.Errorf("folder name is required")
381- }
382- var iconPtr *string
383- if trimmed := strings.TrimSpace(icon); trimmed != "" {
384- iconPtr = &trimmed
385- }
386- _, err := request[struct{}](c, ctx, "folder_update", struct {
387- RequestID string `json:"requestId"`
388- ID int64 `json:"id"`
389- Name string `json:"name"`
390- Icon *string `json:"icon"`
391- }{RequestID: nextRequestID(), ID: id, Name: name, Icon: iconPtr})
392- return err
393-}
394-
395-func (c *Client) DeleteFolder(ctx context.Context, id int64) error {
396- if id <= 0 {
397- return fmt.Errorf("delete folder id must be positive, got %d", id)
398- }
399- _, err := request[struct{}](c, ctx, "folder_delete", struct {
400- RequestID string `json:"requestId"`
401- ID int64 `json:"id"`
402- }{RequestID: nextRequestID(), ID: id})
403- return err
404-}
405-
406 func (c *Client) CreateTag(ctx context.Context, name, color string) (*entity.Tag, error) {327 func (c *Client) CreateTag(ctx context.Context, name, color string) (*entity.Tag, error) {
407 name = strings.TrimSpace(name)328 name = strings.TrimSpace(name)
408 if name == "" {329 if name == "" {
M internal/infrastructure/systemviewsbridge/client_test.go +0 -139
@@ -268,32 +268,6 @@ func TestClientListDecodesFavorites(t *testing.T) {
268268 }
269269 }
270270
271-func TestClientListFoldersDecodesFolders(t *testing.T) {
272- t.Parallel()
273-
274- native := newTransportRecorder(t, true, []byte(`{"requestId":"req-12","success":true,"data":[{"id":9,"name":"Read Later"}]}`))
275- client := NewClient(native, nil)
276-
277- folders, err := client.ListFolders(context.Background())
278- if err != nil {
279- t.Fatalf("ListFolders() error = %v", err)
280- }
281- if len(folders) != 1 {
282- t.Fatalf("ListFolders() len = %d, want 1", len(folders))
283- }
284- if folders[0].Name != "Read Later" {
285- t.Fatalf("ListFolders() folder = %+v", folders[0])
286- }
287-
288- var msg port.WebUIMessage
289- if err := json.Unmarshal(native.last, &msg); err != nil {
290- t.Fatalf("unmarshal sent envelope: %v", err)
291- }
292- if msg.Type != "folder_list" {
293- t.Fatalf("sent type = %q, want %q", msg.Type, "folder_list")
294- }
295-}
296-
297271 func TestClientListTagsDecodesTags(t *testing.T) {
298272 t.Parallel()
299273
@@ -470,61 +444,6 @@ func TestClientRequestUsesDefaultTimeoutWhenCallerHasNoDeadline(t *testing.T) {
470444 }
471445 }
472446
473-func TestClientCreateFolderIncludesParentID(t *testing.T) {
474- t.Parallel()
475-
476- parentID := int64(42)
477- transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-16","success":true,"data":{"id":9,"name":"Nested","icon":"📁","parent_id":42}}`))
478- client := NewClient(transport, nil)
479-
480- folder, err := client.CreateFolder(context.Background(), "Nested", " 📁 ", &parentID)
481- if err != nil {
482- t.Fatalf("CreateFolder() error = %v", err)
483- }
484- if folder == nil {
485- t.Fatal("CreateFolder() returned nil folder")
486- }
487- if folder.Name != "Nested" {
488- t.Fatalf("CreateFolder() folder name = %q, want %q", folder.Name, "Nested")
489- }
490- if folder.Icon != "📁" {
491- t.Fatalf("CreateFolder() folder icon = %q, want 📁", folder.Icon)
492- }
493- if folder.ParentID == nil || int64(*folder.ParentID) != parentID {
494- t.Fatalf("CreateFolder() folder parent_id = %#v, want %d", folder.ParentID, parentID)
495- }
496-
497- var msg port.WebUIMessage
498- if err := json.Unmarshal(transport.last, &msg); err != nil {
499- t.Fatalf("unmarshal sent envelope: %v", err)
500- }
501- if msg.Type != "folder_create" {
502- t.Fatalf("sent type = %q, want %q", msg.Type, "folder_create")
503- }
504-
505- var payload struct {
506- RequestID string `json:"requestId"`
507- Name string `json:"name"`
508- Icon *string `json:"icon"`
509- ParentID *int64 `json:"parent_id"`
510- }
511- if err := json.Unmarshal(msg.Payload, &payload); err != nil {
512- t.Fatalf("unmarshal sent payload: %v", err)
513- }
514- if payload.ParentID == nil || *payload.ParentID != parentID {
515- t.Fatalf("sent parent_id = %#v, want %d", payload.ParentID, parentID)
516- }
517- if payload.Name != "Nested" {
518- t.Fatalf("sent name = %q, want %q", payload.Name, "Nested")
519- }
520- if payload.Icon == nil || *payload.Icon != "📁" {
521- t.Fatalf("sent icon = %#v, want 📁", payload.Icon)
522- }
523- if payload.RequestID == "" {
524- t.Fatal("sent requestId was empty")
525- }
526-}
527-
528447 func TestClientDomainStatsRejectsNegativeLimit(t *testing.T) {
529448 t.Parallel()
530449
@@ -555,59 +474,6 @@ func TestClientDeleteDomainRejectsEmptyDomain(t *testing.T) {
555474 }
556475 }
557476
558-func TestClientUpdateFolderTrimsIcon(t *testing.T) {
559- t.Parallel()
560-
561- transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-18","success":true}`))
562- client := NewClient(transport, nil)
563-
564- if err := client.UpdateFolder(context.Background(), 9, "Read", " 📚 "); err != nil {
565- t.Fatalf("UpdateFolder() error = %v", err)
566- }
567-
568- var msg port.WebUIMessage
569- if err := json.Unmarshal(transport.last, &msg); err != nil {
570- t.Fatalf("unmarshal sent envelope: %v", err)
571- }
572- if msg.Type != "folder_update" {
573- t.Fatalf("sent type = %q, want folder_update", msg.Type)
574- }
575- var payload struct {
576- Icon *string `json:"icon"`
577- }
578- if err := json.Unmarshal(msg.Payload, &payload); err != nil {
579- t.Fatalf("unmarshal sent payload: %v", err)
580- }
581- if payload.Icon == nil || *payload.Icon != "📚" {
582- t.Fatalf("sent icon = %#v, want 📚", payload.Icon)
583- }
584-}
585-
586-func TestClientUpdateFolderOmitsBlankIcon(t *testing.T) {
587- t.Parallel()
588-
589- transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-19","success":true}`))
590- client := NewClient(transport, nil)
591-
592- if err := client.UpdateFolder(context.Background(), 9, "Read", " "); err != nil {
593- t.Fatalf("UpdateFolder() error = %v", err)
594- }
595-
596- var msg port.WebUIMessage
597- if err := json.Unmarshal(transport.last, &msg); err != nil {
598- t.Fatalf("unmarshal sent envelope: %v", err)
599- }
600- var payload struct {
601- Icon *string `json:"icon"`
602- }
603- if err := json.Unmarshal(msg.Payload, &payload); err != nil {
604- t.Fatalf("unmarshal sent payload: %v", err)
605- }
606- if payload.Icon != nil {
607- t.Fatalf("sent icon = %#v, want nil", payload.Icon)
608- }
609-}
610-
611477 func TestClientDeleteEntryRejectsInvalidID(t *testing.T) {
612478 t.Parallel()
613479
@@ -719,11 +585,6 @@ func TestClientRejectsInvalidFavoriteMutationIDs(t *testing.T) {
719585 }{
720586 {name: "delete favorite", call: func() error { return client.DeleteFavorite(context.Background(), 0) }},
721587 {name: "set shortcut", call: func() error { return client.SetShortcut(context.Background(), 0, nil) }},
722- {name: "set folder", call: func() error { return client.SetFolder(context.Background(), 0, nil) }},
723- {name: "create folder", call: func() error { _, err := client.CreateFolder(context.Background(), " ", "", nil); return err }},
724- {name: "update folder id", call: func() error { return client.UpdateFolder(context.Background(), 0, "Folder", "") }},
725- {name: "update folder name", call: func() error { return client.UpdateFolder(context.Background(), 1, " ", "") }},
726- {name: "delete folder", call: func() error { return client.DeleteFolder(context.Background(), 0) }},
727588 {name: "create tag", call: func() error { _, err := client.CreateTag(context.Background(), " ", ""); return err }},
728589 {name: "update tag", call: func() error { return client.UpdateTag(context.Background(), 0, "Tag", "") }},
729590 {name: "update tag name", call: func() error { return client.UpdateTag(context.Background(), 1, " ", "") }},
@@ -268,32 +268,6 @@ func TestClientListDecodesFavorites(t *testing.T) {
268 }268 }
269 }269 }
270 270
271-func TestClientListFoldersDecodesFolders(t *testing.T) {
272- t.Parallel()
273-
274- native := newTransportRecorder(t, true, []byte(`{"requestId":"req-12","success":true,"data":[{"id":9,"name":"Read Later"}]}`))
275- client := NewClient(native, nil)
276-
277- folders, err := client.ListFolders(context.Background())
278- if err != nil {
279- t.Fatalf("ListFolders() error = %v", err)
280- }
281- if len(folders) != 1 {
282- t.Fatalf("ListFolders() len = %d, want 1", len(folders))
283- }
284- if folders[0].Name != "Read Later" {
285- t.Fatalf("ListFolders() folder = %+v", folders[0])
286- }
287-
288- var msg port.WebUIMessage
289- if err := json.Unmarshal(native.last, &msg); err != nil {
290- t.Fatalf("unmarshal sent envelope: %v", err)
291- }
292- if msg.Type != "folder_list" {
293- t.Fatalf("sent type = %q, want %q", msg.Type, "folder_list")
294- }
295-}
296-
297 func TestClientListTagsDecodesTags(t *testing.T) {271 func TestClientListTagsDecodesTags(t *testing.T) {
298 t.Parallel()272 t.Parallel()
299 273
@@ -470,61 +444,6 @@ func TestClientRequestUsesDefaultTimeoutWhenCallerHasNoDeadline(t *testing.T) {
470 }444 }
471 }445 }
472 446
473-func TestClientCreateFolderIncludesParentID(t *testing.T) {
474- t.Parallel()
475-
476- parentID := int64(42)
477- transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-16","success":true,"data":{"id":9,"name":"Nested","icon":"📁","parent_id":42}}`))
478- client := NewClient(transport, nil)
479-
480- folder, err := client.CreateFolder(context.Background(), "Nested", " 📁 ", &parentID)
481- if err != nil {
482- t.Fatalf("CreateFolder() error = %v", err)
483- }
484- if folder == nil {
485- t.Fatal("CreateFolder() returned nil folder")
486- }
487- if folder.Name != "Nested" {
488- t.Fatalf("CreateFolder() folder name = %q, want %q", folder.Name, "Nested")
489- }
490- if folder.Icon != "📁" {
491- t.Fatalf("CreateFolder() folder icon = %q, want 📁", folder.Icon)
492- }
493- if folder.ParentID == nil || int64(*folder.ParentID) != parentID {
494- t.Fatalf("CreateFolder() folder parent_id = %#v, want %d", folder.ParentID, parentID)
495- }
496-
497- var msg port.WebUIMessage
498- if err := json.Unmarshal(transport.last, &msg); err != nil {
499- t.Fatalf("unmarshal sent envelope: %v", err)
500- }
501- if msg.Type != "folder_create" {
502- t.Fatalf("sent type = %q, want %q", msg.Type, "folder_create")
503- }
504-
505- var payload struct {
506- RequestID string `json:"requestId"`
507- Name string `json:"name"`
508- Icon *string `json:"icon"`
509- ParentID *int64 `json:"parent_id"`
510- }
511- if err := json.Unmarshal(msg.Payload, &payload); err != nil {
512- t.Fatalf("unmarshal sent payload: %v", err)
513- }
514- if payload.ParentID == nil || *payload.ParentID != parentID {
515- t.Fatalf("sent parent_id = %#v, want %d", payload.ParentID, parentID)
516- }
517- if payload.Name != "Nested" {
518- t.Fatalf("sent name = %q, want %q", payload.Name, "Nested")
519- }
520- if payload.Icon == nil || *payload.Icon != "📁" {
521- t.Fatalf("sent icon = %#v, want 📁", payload.Icon)
522- }
523- if payload.RequestID == "" {
524- t.Fatal("sent requestId was empty")
525- }
526-}
527-
528 func TestClientDomainStatsRejectsNegativeLimit(t *testing.T) {447 func TestClientDomainStatsRejectsNegativeLimit(t *testing.T) {
529 t.Parallel()448 t.Parallel()
530 449
@@ -555,59 +474,6 @@ func TestClientDeleteDomainRejectsEmptyDomain(t *testing.T) {
555 }474 }
556 }475 }
557 476
558-func TestClientUpdateFolderTrimsIcon(t *testing.T) {
559- t.Parallel()
560-
561- transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-18","success":true}`))
562- client := NewClient(transport, nil)
563-
564- if err := client.UpdateFolder(context.Background(), 9, "Read", " 📚 "); err != nil {
565- t.Fatalf("UpdateFolder() error = %v", err)
566- }
567-
568- var msg port.WebUIMessage
569- if err := json.Unmarshal(transport.last, &msg); err != nil {
570- t.Fatalf("unmarshal sent envelope: %v", err)
571- }
572- if msg.Type != "folder_update" {
573- t.Fatalf("sent type = %q, want folder_update", msg.Type)
574- }
575- var payload struct {
576- Icon *string `json:"icon"`
577- }
578- if err := json.Unmarshal(msg.Payload, &payload); err != nil {
579- t.Fatalf("unmarshal sent payload: %v", err)
580- }
581- if payload.Icon == nil || *payload.Icon != "📚" {
582- t.Fatalf("sent icon = %#v, want 📚", payload.Icon)
583- }
584-}
585-
586-func TestClientUpdateFolderOmitsBlankIcon(t *testing.T) {
587- t.Parallel()
588-
589- transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-19","success":true}`))
590- client := NewClient(transport, nil)
591-
592- if err := client.UpdateFolder(context.Background(), 9, "Read", " "); err != nil {
593- t.Fatalf("UpdateFolder() error = %v", err)
594- }
595-
596- var msg port.WebUIMessage
597- if err := json.Unmarshal(transport.last, &msg); err != nil {
598- t.Fatalf("unmarshal sent envelope: %v", err)
599- }
600- var payload struct {
601- Icon *string `json:"icon"`
602- }
603- if err := json.Unmarshal(msg.Payload, &payload); err != nil {
604- t.Fatalf("unmarshal sent payload: %v", err)
605- }
606- if payload.Icon != nil {
607- t.Fatalf("sent icon = %#v, want nil", payload.Icon)
608- }
609-}
610-
611 func TestClientDeleteEntryRejectsInvalidID(t *testing.T) {477 func TestClientDeleteEntryRejectsInvalidID(t *testing.T) {
612 t.Parallel()478 t.Parallel()
613 479
@@ -719,11 +585,6 @@ func TestClientRejectsInvalidFavoriteMutationIDs(t *testing.T) {
719 }{585 }{
720 {name: "delete favorite", call: func() error { return client.DeleteFavorite(context.Background(), 0) }},586 {name: "delete favorite", call: func() error { return client.DeleteFavorite(context.Background(), 0) }},
721 {name: "set shortcut", call: func() error { return client.SetShortcut(context.Background(), 0, nil) }},587 {name: "set shortcut", call: func() error { return client.SetShortcut(context.Background(), 0, nil) }},
722- {name: "set folder", call: func() error { return client.SetFolder(context.Background(), 0, nil) }},
723- {name: "create folder", call: func() error { _, err := client.CreateFolder(context.Background(), " ", "", nil); return err }},
724- {name: "update folder id", call: func() error { return client.UpdateFolder(context.Background(), 0, "Folder", "") }},
725- {name: "update folder name", call: func() error { return client.UpdateFolder(context.Background(), 1, " ", "") }},
726- {name: "delete folder", call: func() error { return client.DeleteFolder(context.Background(), 0) }},
727 {name: "create tag", call: func() error { _, err := client.CreateTag(context.Background(), " ", ""); return err }},588 {name: "create tag", call: func() error { _, err := client.CreateTag(context.Background(), " ", ""); return err }},
728 {name: "update tag", call: func() error { return client.UpdateTag(context.Background(), 0, "Tag", "") }},589 {name: "update tag", call: func() error { return client.UpdateTag(context.Background(), 0, "Tag", "") }},
729 {name: "update tag name", call: func() error { return client.UpdateTag(context.Background(), 1, " ", "") }},590 {name: "update tag name", call: func() error { return client.UpdateTag(context.Background(), 1, " ", "") }},
M internal/ui/systemviews/app.go +45 -56
@@ -24,44 +24,43 @@ type Dependencies struct {
2424 type App struct {
2525 // mu protects rendered route state; actionMu protects worker channels.
2626 // The two locks must not be held at the same time.
27- mu sync.Mutex
28- actionMu sync.Mutex
29- deps Dependencies
30- currentRoute Route
31- shellTheme shellTheme
32- historyEntries []*entity.HistoryEntry
33- historyStats *entity.HistoryStats
34- historyAnalytics *entity.HistoryAnalytics
35- historyDomainStats []*entity.DomainStat
36- historyQuery string
37- historyDomainFilter string
38- historyOffset int
39- historyWindowBefore time.Time
40- historyWindowAfter time.Time
41- historyCursorID int64
42- historyHasMore bool
43- historyNotice string
44- historyError string
45- favorites []*entity.Favorite
46- folders []*entity.Folder
47- tags []*entity.Tag
48- favoriteFolderFilter *entity.FolderID
49- favoriteTagFilter *entity.TagID
50- favoritesNotice string
51- favoritesError string
52- config *dto.SystemviewConfigPayload
53- keybindings port.KeybindingsConfig
54- configNotice string
55- configError string
56- renderedHTML string
57- renderGeneration uint64
58- closed bool
59- actionQueue chan DOMAction
60- actionErrorQueue chan error
61- actionCtx context.Context
62- actionCancel context.CancelFunc
63- actionClosed bool
64- actionWG sync.WaitGroup
27+ mu sync.Mutex
28+ actionMu sync.Mutex
29+ deps Dependencies
30+ currentRoute Route
31+ shellTheme shellTheme
32+ historyEntries []*entity.HistoryEntry
33+ historyStats *entity.HistoryStats
34+ historyAnalytics *entity.HistoryAnalytics
35+ historyDomainStats []*entity.DomainStat
36+ historyQuery string
37+ historyDomainFilter string
38+ historyOffset int
39+ historyWindowBefore time.Time
40+ historyWindowAfter time.Time
41+ historyCursorID int64
42+ historyHasMore bool
43+ historyNotice string
44+ historyError string
45+ favorites []*entity.Favorite
46+ tags []*entity.Tag
47+ favoriteTagFilter *entity.TagID
48+ favoriteUntaggedFilter bool
49+ favoritesNotice string
50+ favoritesError string
51+ config *dto.SystemviewConfigPayload
52+ keybindings port.KeybindingsConfig
53+ configNotice string
54+ configError string
55+ renderedHTML string
56+ renderGeneration uint64
57+ closed bool
58+ actionQueue chan DOMAction
59+ actionErrorQueue chan error
60+ actionCtx context.Context
61+ actionCancel context.CancelFunc
62+ actionClosed bool
63+ actionWG sync.WaitGroup
6564 }
6665
6766 const (
@@ -513,7 +512,6 @@ func routeSubtitle(route Route) string {
513512
514513 func (a *App) loadHistoryLoadingRoute() {
515514 a.favorites = nil
516- a.folders = nil
517515 a.tags = nil
518516 a.historyEntries = nil
519517 a.historyStats = nil
@@ -698,7 +696,6 @@ func loadHistoryWindowSnapshot(ctx context.Context, snapshot historyRouteSnapsho
698696
699697 func (a *App) commitHistoryRouteResultLocked(result historyRouteResult) {
700698 a.favorites = nil
701- a.folders = nil
702699 a.tags = nil
703700 a.historyEntries = result.entries
704701 a.historyStats = result.stats
@@ -727,28 +724,22 @@ func (a *App) loadFavoritesRoute(ctx context.Context) error {
727724 if err != nil {
728725 return err
729726 }
730- folders, err := a.deps.Favorites.ListFolders(ctx)
731- if err != nil {
732- return err
733- }
734727 tags, err := a.deps.Favorites.ListTags(ctx)
735728 if err != nil {
736729 return err
737730 }
738731
739732 a.historyEntries = nil
740- favorites = filterFavorites(favorites, a.favoriteFolderFilter, a.favoriteTagFilter)
733+ favorites = filterFavorites(favorites, a.favoriteTagFilter, a.favoriteUntaggedFilter)
741734 a.favorites = favorites
742- a.folders = folders
743735 a.tags = tags
744736 data := favoritesRenderData{
745- Favorites: favorites,
746- Folders: folders,
747- Tags: tags,
748- FolderFilter: a.favoriteFolderFilter,
749- TagFilter: a.favoriteTagFilter,
750- Notice: a.favoritesNotice,
751- Error: a.favoritesError,
737+ Favorites: favorites,
738+ Tags: tags,
739+ TagFilter: a.favoriteTagFilter,
740+ UntaggedFilter: a.favoriteUntaggedFilter,
741+ Notice: a.favoritesNotice,
742+ Error: a.favoritesError,
752743 }
753744 a.renderedHTML = renderAppFrame(renderedPage{
754745 route: RouteFavorites,
@@ -782,7 +773,6 @@ func (a *App) loadConfigRoute(ctx context.Context) error {
782773
783774 a.historyEntries = nil
784775 a.favorites = nil
785- a.folders = nil
786776 a.tags = nil
787777 a.config = &config
788778 a.keybindings = keybindings
@@ -835,10 +825,9 @@ func (a *App) resetRouteState() {
835825 a.historyNotice = ""
836826 a.historyError = ""
837827 a.favorites = nil
838- a.folders = nil
839828 a.tags = nil
840- a.favoriteFolderFilter = nil
841829 a.favoriteTagFilter = nil
830+ a.favoriteUntaggedFilter = false
842831 a.favoritesNotice = ""
843832 a.favoritesError = ""
844833 a.config = nil
@@ -24,44 +24,43 @@ type Dependencies struct {
24 type App struct {24 type App struct {
25 // mu protects rendered route state; actionMu protects worker channels.25 // mu protects rendered route state; actionMu protects worker channels.
26 // The two locks must not be held at the same time.26 // The two locks must not be held at the same time.
27- mu sync.Mutex27+ mu sync.Mutex
28- actionMu sync.Mutex28+ actionMu sync.Mutex
29- deps Dependencies29+ deps Dependencies
30- currentRoute Route30+ currentRoute Route
31- shellTheme shellTheme31+ shellTheme shellTheme
32- historyEntries []*entity.HistoryEntry32+ historyEntries []*entity.HistoryEntry
33- historyStats *entity.HistoryStats33+ historyStats *entity.HistoryStats
34- historyAnalytics *entity.HistoryAnalytics34+ historyAnalytics *entity.HistoryAnalytics
35- historyDomainStats []*entity.DomainStat35+ historyDomainStats []*entity.DomainStat
36- historyQuery string36+ historyQuery string
37- historyDomainFilter string37+ historyDomainFilter string
38- historyOffset int38+ historyOffset int
39- historyWindowBefore time.Time39+ historyWindowBefore time.Time
40- historyWindowAfter time.Time40+ historyWindowAfter time.Time
41- historyCursorID int6441+ historyCursorID int64
42- historyHasMore bool42+ historyHasMore bool
43- historyNotice string43+ historyNotice string
44- historyError string44+ historyError string
45- favorites []*entity.Favorite45+ favorites []*entity.Favorite
46- folders []*entity.Folder46+ tags []*entity.Tag
47- tags []*entity.Tag47+ favoriteTagFilter *entity.TagID
48- favoriteFolderFilter *entity.FolderID48+ favoriteUntaggedFilter bool
49- favoriteTagFilter *entity.TagID49+ favoritesNotice string
50- favoritesNotice string50+ favoritesError string
51- favoritesError string51+ config *dto.SystemviewConfigPayload
52- config *dto.SystemviewConfigPayload52+ keybindings port.KeybindingsConfig
53- keybindings port.KeybindingsConfig53+ configNotice string
54- configNotice string54+ configError string
55- configError string55+ renderedHTML string
56- renderedHTML string56+ renderGeneration uint64
57- renderGeneration uint6457+ closed bool
58- closed bool58+ actionQueue chan DOMAction
59- actionQueue chan DOMAction59+ actionErrorQueue chan error
60- actionErrorQueue chan error60+ actionCtx context.Context
61- actionCtx context.Context61+ actionCancel context.CancelFunc
62- actionCancel context.CancelFunc62+ actionClosed bool
63- actionClosed bool63+ actionWG sync.WaitGroup
64- actionWG sync.WaitGroup
65 }64 }
66 65
67 const (66 const (
@@ -513,7 +512,6 @@ func routeSubtitle(route Route) string {
513 512
514 func (a *App) loadHistoryLoadingRoute() {513 func (a *App) loadHistoryLoadingRoute() {
515 a.favorites = nil514 a.favorites = nil
516- a.folders = nil
517 a.tags = nil515 a.tags = nil
518 a.historyEntries = nil516 a.historyEntries = nil
519 a.historyStats = nil517 a.historyStats = nil
@@ -698,7 +696,6 @@ func loadHistoryWindowSnapshot(ctx context.Context, snapshot historyRouteSnapsho
698 696
699 func (a *App) commitHistoryRouteResultLocked(result historyRouteResult) {697 func (a *App) commitHistoryRouteResultLocked(result historyRouteResult) {
700 a.favorites = nil698 a.favorites = nil
701- a.folders = nil
702 a.tags = nil699 a.tags = nil
703 a.historyEntries = result.entries700 a.historyEntries = result.entries
704 a.historyStats = result.stats701 a.historyStats = result.stats
@@ -727,28 +724,22 @@ func (a *App) loadFavoritesRoute(ctx context.Context) error {
727 if err != nil {724 if err != nil {
728 return err725 return err
729 }726 }
730- folders, err := a.deps.Favorites.ListFolders(ctx)
731- if err != nil {
732- return err
733- }
734 tags, err := a.deps.Favorites.ListTags(ctx)727 tags, err := a.deps.Favorites.ListTags(ctx)
735 if err != nil {728 if err != nil {
736 return err729 return err
737 }730 }
738 731
739 a.historyEntries = nil732 a.historyEntries = nil
740- favorites = filterFavorites(favorites, a.favoriteFolderFilter, a.favoriteTagFilter)733+ favorites = filterFavorites(favorites, a.favoriteTagFilter, a.favoriteUntaggedFilter)
741 a.favorites = favorites734 a.favorites = favorites
742- a.folders = folders
743 a.tags = tags735 a.tags = tags
744 data := favoritesRenderData{736 data := favoritesRenderData{
745- Favorites: favorites,737+ Favorites: favorites,
746- Folders: folders,738+ Tags: tags,
747- Tags: tags,739+ TagFilter: a.favoriteTagFilter,
748- FolderFilter: a.favoriteFolderFilter,740+ UntaggedFilter: a.favoriteUntaggedFilter,
749- TagFilter: a.favoriteTagFilter,741+ Notice: a.favoritesNotice,
750- Notice: a.favoritesNotice,742+ Error: a.favoritesError,
751- Error: a.favoritesError,
752 }743 }
753 a.renderedHTML = renderAppFrame(renderedPage{744 a.renderedHTML = renderAppFrame(renderedPage{
754 route: RouteFavorites,745 route: RouteFavorites,
@@ -782,7 +773,6 @@ func (a *App) loadConfigRoute(ctx context.Context) error {
782 773
783 a.historyEntries = nil774 a.historyEntries = nil
784 a.favorites = nil775 a.favorites = nil
785- a.folders = nil
786 a.tags = nil776 a.tags = nil
787 a.config = &config777 a.config = &config
788 a.keybindings = keybindings778 a.keybindings = keybindings
@@ -835,10 +825,9 @@ func (a *App) resetRouteState() {
835 a.historyNotice = ""825 a.historyNotice = ""
836 a.historyError = ""826 a.historyError = ""
837 a.favorites = nil827 a.favorites = nil
838- a.folders = nil
839 a.tags = nil828 a.tags = nil
840- a.favoriteFolderFilter = nil
841 a.favoriteTagFilter = nil829 a.favoriteTagFilter = nil
830+ a.favoriteUntaggedFilter = false
842 a.favoritesNotice = ""831 a.favoritesNotice = ""
843 a.favoritesError = ""832 a.favoritesError = ""
844 a.config = nil833 a.config = nil
M internal/ui/systemviews/app_test.go +13 -67
@@ -517,7 +517,6 @@ func TestAppLoadInitialFavoritesRouteRendersData(t *testing.T) {
517517
518518 favorites := &recordingFavoritesService{
519519 favorites: []*entity.Favorite{{URL: "https://example.com", Title: "Example"}},
520- folders: []*entity.Folder{{Name: "Read Later"}},
521520 tags: []*entity.Tag{{Name: "Go"}},
522521 }
523522
@@ -529,14 +528,11 @@ func TestAppLoadInitialFavoritesRouteRendersData(t *testing.T) {
529528 require.NoError(t, app.LoadInitial(context.Background()))
530529 assert.Equal(t, RouteFavorites, app.CurrentRoute())
531530 assert.Len(t, app.favorites, 1)
532- assert.Len(t, app.folders, 1)
533531 assert.Len(t, app.tags, 1)
534532 assert.Contains(t, app.renderedHTML, "Favorites")
535533 assert.Contains(t, app.renderedHTML, "Example")
536- assert.Contains(t, app.renderedHTML, "Read Later")
537534 assert.Contains(t, app.renderedHTML, "Go")
538535 assert.True(t, favorites.calledList)
539- assert.True(t, favorites.calledFolders)
540536 assert.True(t, favorites.calledTags)
541537
542538 // Shell frame present, no full document wrapper.
@@ -553,11 +549,9 @@ func TestAppLoadInitialFavoritesRouteRendersData(t *testing.T) {
553549 func TestAppLoadInitialFavoritesRouteRendersCRUDControls(t *testing.T) {
554550 t.Parallel()
555551
556- folderID := entity.FolderID(1)
557552 shortcut := 3
558553 favorites := &recordingFavoritesService{
559- favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example", FolderID: &folderID, ShortcutKey: &shortcut}},
560- folders: []*entity.Folder{{ID: folderID, Name: "Read Later", Icon: "📚"}},
554+ favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example", ShortcutKey: &shortcut}},
561555 tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}},
562556 }
563557
@@ -567,16 +561,16 @@ func TestAppLoadInitialFavoritesRouteRendersCRUDControls(t *testing.T) {
567561 assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.create"`)
568562 assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.update"`)
569563 assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.delete"`)
570- assert.Contains(t, app.renderedHTML, `data-sv-action="folder.create"`)
571- assert.Contains(t, app.renderedHTML, `data-sv-action="folder.update"`)
572- assert.Contains(t, app.renderedHTML, `data-sv-action="folder.delete"`)
564+ assert.NotContains(t, app.renderedHTML, `data-sv-action="folder.create"`)
565+ assert.NotContains(t, app.renderedHTML, `data-sv-action="folder.update"`)
566+ assert.NotContains(t, app.renderedHTML, `data-sv-action="folder.delete"`)
573567 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.create"`)
574568 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.update"`)
575569 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.delete"`)
576570 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.assign"`)
571+ assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.filterUntagged"`)
577572 assert.Contains(t, app.renderedHTML, `name="tags"`)
578573 assert.Contains(t, app.renderedHTML, `value="7"`)
579- assert.Contains(t, app.renderedHTML, "Read Later")
580574 assert.Contains(t, app.renderedHTML, "Go")
581575 assert.Contains(t, app.renderedHTML, "Shortcut 3")
582576 }
@@ -585,18 +579,15 @@ func TestAppHandleFavoriteActionsRefreshesDOM(t *testing.T) {
585579 dom := &recordingDOM{}
586580 favorites := &recordingFavoritesService{
587581 favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example"}},
588- folders: []*entity.Folder{{ID: 5, Name: "Read Later"}},
589582 tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}},
590583 }
591584 app := NewApp(Dependencies{DOM: dom, Favorites: favorites, LocationURI: "dumb://favorites"})
592585
593586 require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{
594587 Action: favoriteActionCreate,
595- Data: map[string]string{"url": "https://new.example", "title": "New", "folder_id": "5", "tags": "7"},
588+ Data: map[string]string{"url": "https://new.example", "title": "New", "tags": "7"},
596589 }))
597- require.NotNil(t, favorites.createdFavorite.FolderID)
598590 assert.Equal(t, "https://new.example", favorites.createdFavorite.URL)
599- assert.Equal(t, entity.FolderID(5), *favorites.createdFavorite.FolderID)
600591 assert.Equal(t, []entity.TagID{7}, favorites.createdFavorite.Tags)
601592 assert.Contains(t, dom.HTML(), "Added favorite New")
602593
@@ -617,29 +608,13 @@ func TestAppHandleFavoriteActionsRefreshesDOM(t *testing.T) {
617608 assert.Equal(t, int64(42), favorites.assignedFavoriteID)
618609 assert.Equal(t, int64(7), favorites.assignedTagID)
619610
620- require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{
621- Action: favoriteActionFilterFolder,
622- Data: map[string]string{"folderId": "5"},
623- }))
624- require.NotNil(t, app.favoriteFolderFilter)
625- assert.Equal(t, entity.FolderID(5), *app.favoriteFolderFilter)
626- assert.Nil(t, app.favoriteTagFilter)
627-
628611 require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{
629612 Action: favoriteActionFilterTag,
630613 Data: map[string]string{"tagId": "7"},
631614 }))
632615 require.NotNil(t, app.favoriteTagFilter)
633616 assert.Equal(t, entity.TagID(7), *app.favoriteTagFilter)
634- assert.Nil(t, app.favoriteFolderFilter)
635-
636- require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{
637- Action: folderActionCreate,
638- Data: map[string]string{"name": "Reading", "icon": "📚"},
639- }))
640- assert.Equal(t, "Reading", favorites.createdFolder)
641- assert.Equal(t, "📚", favorites.createdFolderIcon)
642- assert.Contains(t, dom.HTML(), "Created folder 📚 Reading")
617+ assert.False(t, app.favoriteUntaggedFilter)
643618
644619 require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{
645620 Action: favoriteActionDelete,
@@ -1199,20 +1174,14 @@ func (s *recordingHistoryService) DeleteDomain(_ context.Context, domain string)
11991174
12001175 // Recording fixture to capture favorites state for stateful render assertions.
12011176 type recordingFavoritesService struct {
1202- calledList bool
1203- calledFolders bool
1204- calledTags bool
1205- favorites []*entity.Favorite
1206- folders []*entity.Folder
1207- tags []*entity.Tag
1177+ calledList bool
1178+ calledTags bool
1179+ favorites []*entity.Favorite
1180+ tags []*entity.Tag
12081181
12091182 createdFavorite dto.FavoriteCreateInput
12101183 updatedFavorite dto.FavoriteUpdateInput
12111184 deletedFavorite int64
1212- createdFolder string
1213- createdFolderIcon string
1214- updatedFolderID int64
1215- deletedFolderID int64
12161185 createdTag string
12171186 updatedTagID int64
12181187 deletedTagID int64
@@ -1227,11 +1196,6 @@ func (s *recordingFavoritesService) List(context.Context) ([]*entity.Favorite, e
12271196 return s.favorites, nil
12281197 }
12291198
1230-func (s *recordingFavoritesService) ListFolders(context.Context) ([]*entity.Folder, error) {
1231- s.calledFolders = true
1232- return s.folders, nil
1233-}
1234-
12351199 func (s *recordingFavoritesService) ListTags(context.Context) ([]*entity.Tag, error) {
12361200 s.calledTags = true
12371201 return s.tags, nil
@@ -1239,12 +1203,12 @@ func (s *recordingFavoritesService) ListTags(context.Context) ([]*entity.Tag, er
12391203
12401204 func (s *recordingFavoritesService) CreateFavorite(_ context.Context, input dto.FavoriteCreateInput) (*entity.Favorite, error) {
12411205 s.createdFavorite = input
1242- return &entity.Favorite{ID: 99, URL: input.URL, Title: input.Title, FolderID: input.FolderID}, nil
1206+ return &entity.Favorite{ID: 99, URL: input.URL, Title: input.Title}, nil
12431207 }
12441208
12451209 func (s *recordingFavoritesService) UpdateFavorite(_ context.Context, input dto.FavoriteUpdateInput) (*entity.Favorite, error) {
12461210 s.updatedFavorite = input
1247- return &entity.Favorite{ID: input.ID, URL: "https://example.com", Title: input.Title, FaviconURL: input.FaviconURL, FolderID: input.FolderID, ShortcutKey: input.ShortcutKey}, nil
1211+ return &entity.Favorite{ID: input.ID, URL: "https://example.com", Title: input.Title, FaviconURL: input.FaviconURL, ShortcutKey: input.ShortcutKey}, nil
12481212 }
12491213
12501214 func (s *recordingFavoritesService) DeleteFavorite(_ context.Context, id int64) error {
@@ -1254,24 +1218,6 @@ func (s *recordingFavoritesService) DeleteFavorite(_ context.Context, id int64)
12541218
12551219 func (s *recordingFavoritesService) SetShortcut(context.Context, int64, *int) error { return nil }
12561220
1257-func (s *recordingFavoritesService) SetFolder(context.Context, int64, *int64) error { return nil }
1258-
1259-func (s *recordingFavoritesService) CreateFolder(_ context.Context, name, icon string, _ *int64) (*entity.Folder, error) {
1260- s.createdFolder = name
1261- s.createdFolderIcon = icon
1262- return &entity.Folder{ID: 77, Name: name, Icon: icon}, nil
1263-}
1264-
1265-func (s *recordingFavoritesService) UpdateFolder(_ context.Context, id int64, _, _ string) error {
1266- s.updatedFolderID = id
1267- return nil
1268-}
1269-
1270-func (s *recordingFavoritesService) DeleteFolder(_ context.Context, id int64) error {
1271- s.deletedFolderID = id
1272- return nil
1273-}
1274-
12751221 func (s *recordingFavoritesService) CreateTag(_ context.Context, name, color string) (*entity.Tag, error) {
12761222 s.createdTag = name
12771223 return &entity.Tag{ID: 55, Name: name, Color: color}, nil
@@ -517,7 +517,6 @@ func TestAppLoadInitialFavoritesRouteRendersData(t *testing.T) {
517 517
518 favorites := &recordingFavoritesService{518 favorites := &recordingFavoritesService{
519 favorites: []*entity.Favorite{{URL: "https://example.com", Title: "Example"}},519 favorites: []*entity.Favorite{{URL: "https://example.com", Title: "Example"}},
520- folders: []*entity.Folder{{Name: "Read Later"}},
521 tags: []*entity.Tag{{Name: "Go"}},520 tags: []*entity.Tag{{Name: "Go"}},
522 }521 }
523 522
@@ -529,14 +528,11 @@ func TestAppLoadInitialFavoritesRouteRendersData(t *testing.T) {
529 require.NoError(t, app.LoadInitial(context.Background()))528 require.NoError(t, app.LoadInitial(context.Background()))
530 assert.Equal(t, RouteFavorites, app.CurrentRoute())529 assert.Equal(t, RouteFavorites, app.CurrentRoute())
531 assert.Len(t, app.favorites, 1)530 assert.Len(t, app.favorites, 1)
532- assert.Len(t, app.folders, 1)
533 assert.Len(t, app.tags, 1)531 assert.Len(t, app.tags, 1)
534 assert.Contains(t, app.renderedHTML, "Favorites")532 assert.Contains(t, app.renderedHTML, "Favorites")
535 assert.Contains(t, app.renderedHTML, "Example")533 assert.Contains(t, app.renderedHTML, "Example")
536- assert.Contains(t, app.renderedHTML, "Read Later")
537 assert.Contains(t, app.renderedHTML, "Go")534 assert.Contains(t, app.renderedHTML, "Go")
538 assert.True(t, favorites.calledList)535 assert.True(t, favorites.calledList)
539- assert.True(t, favorites.calledFolders)
540 assert.True(t, favorites.calledTags)536 assert.True(t, favorites.calledTags)
541 537
542 // Shell frame present, no full document wrapper.538 // Shell frame present, no full document wrapper.
@@ -553,11 +549,9 @@ func TestAppLoadInitialFavoritesRouteRendersData(t *testing.T) {
553 func TestAppLoadInitialFavoritesRouteRendersCRUDControls(t *testing.T) {549 func TestAppLoadInitialFavoritesRouteRendersCRUDControls(t *testing.T) {
554 t.Parallel()550 t.Parallel()
555 551
556- folderID := entity.FolderID(1)
557 shortcut := 3552 shortcut := 3
558 favorites := &recordingFavoritesService{553 favorites := &recordingFavoritesService{
559- favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example", FolderID: &folderID, ShortcutKey: &shortcut}},554+ favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example", ShortcutKey: &shortcut}},
560- folders: []*entity.Folder{{ID: folderID, Name: "Read Later", Icon: "📚"}},
561 tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}},555 tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}},
562 }556 }
563 557
@@ -567,16 +561,16 @@ func TestAppLoadInitialFavoritesRouteRendersCRUDControls(t *testing.T) {
567 assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.create"`)561 assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.create"`)
568 assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.update"`)562 assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.update"`)
569 assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.delete"`)563 assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.delete"`)
570- assert.Contains(t, app.renderedHTML, `data-sv-action="folder.create"`)564+ assert.NotContains(t, app.renderedHTML, `data-sv-action="folder.create"`)
571- assert.Contains(t, app.renderedHTML, `data-sv-action="folder.update"`)565+ assert.NotContains(t, app.renderedHTML, `data-sv-action="folder.update"`)
572- assert.Contains(t, app.renderedHTML, `data-sv-action="folder.delete"`)566+ assert.NotContains(t, app.renderedHTML, `data-sv-action="folder.delete"`)
573 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.create"`)567 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.create"`)
574 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.update"`)568 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.update"`)
575 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.delete"`)569 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.delete"`)
576 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.assign"`)570 assert.Contains(t, app.renderedHTML, `data-sv-action="tag.assign"`)
571+ assert.Contains(t, app.renderedHTML, `data-sv-action="favorite.filterUntagged"`)
577 assert.Contains(t, app.renderedHTML, `name="tags"`)572 assert.Contains(t, app.renderedHTML, `name="tags"`)
578 assert.Contains(t, app.renderedHTML, `value="7"`)573 assert.Contains(t, app.renderedHTML, `value="7"`)
579- assert.Contains(t, app.renderedHTML, "Read Later")
580 assert.Contains(t, app.renderedHTML, "Go")574 assert.Contains(t, app.renderedHTML, "Go")
581 assert.Contains(t, app.renderedHTML, "Shortcut 3")575 assert.Contains(t, app.renderedHTML, "Shortcut 3")
582 }576 }
@@ -585,18 +579,15 @@ func TestAppHandleFavoriteActionsRefreshesDOM(t *testing.T) {
585 dom := &recordingDOM{}579 dom := &recordingDOM{}
586 favorites := &recordingFavoritesService{580 favorites := &recordingFavoritesService{
587 favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example"}},581 favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example"}},
588- folders: []*entity.Folder{{ID: 5, Name: "Read Later"}},
589 tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}},582 tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}},
590 }583 }
591 app := NewApp(Dependencies{DOM: dom, Favorites: favorites, LocationURI: "dumb://favorites"})584 app := NewApp(Dependencies{DOM: dom, Favorites: favorites, LocationURI: "dumb://favorites"})
592 585
593 require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{586 require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{
594 Action: favoriteActionCreate,587 Action: favoriteActionCreate,
595- Data: map[string]string{"url": "https://new.example", "title": "New", "folder_id": "5", "tags": "7"},588+ Data: map[string]string{"url": "https://new.example", "title": "New", "tags": "7"},
596 }))589 }))
597- require.NotNil(t, favorites.createdFavorite.FolderID)
598 assert.Equal(t, "https://new.example", favorites.createdFavorite.URL)590 assert.Equal(t, "https://new.example", favorites.createdFavorite.URL)
599- assert.Equal(t, entity.FolderID(5), *favorites.createdFavorite.FolderID)
600 assert.Equal(t, []entity.TagID{7}, favorites.createdFavorite.Tags)591 assert.Equal(t, []entity.TagID{7}, favorites.createdFavorite.Tags)
601 assert.Contains(t, dom.HTML(), "Added favorite New")592 assert.Contains(t, dom.HTML(), "Added favorite New")
602 593
@@ -617,29 +608,13 @@ func TestAppHandleFavoriteActionsRefreshesDOM(t *testing.T) {
617 assert.Equal(t, int64(42), favorites.assignedFavoriteID)608 assert.Equal(t, int64(42), favorites.assignedFavoriteID)
618 assert.Equal(t, int64(7), favorites.assignedTagID)609 assert.Equal(t, int64(7), favorites.assignedTagID)
619 610
620- require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{
621- Action: favoriteActionFilterFolder,
622- Data: map[string]string{"folderId": "5"},
623- }))
624- require.NotNil(t, app.favoriteFolderFilter)
625- assert.Equal(t, entity.FolderID(5), *app.favoriteFolderFilter)
626- assert.Nil(t, app.favoriteTagFilter)
627-
628 require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{611 require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{
629 Action: favoriteActionFilterTag,612 Action: favoriteActionFilterTag,
630 Data: map[string]string{"tagId": "7"},613 Data: map[string]string{"tagId": "7"},
631 }))614 }))
632 require.NotNil(t, app.favoriteTagFilter)615 require.NotNil(t, app.favoriteTagFilter)
633 assert.Equal(t, entity.TagID(7), *app.favoriteTagFilter)616 assert.Equal(t, entity.TagID(7), *app.favoriteTagFilter)
634- assert.Nil(t, app.favoriteFolderFilter)617+ assert.False(t, app.favoriteUntaggedFilter)
635-
636- require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{
637- Action: folderActionCreate,
638- Data: map[string]string{"name": "Reading", "icon": "📚"},
639- }))
640- assert.Equal(t, "Reading", favorites.createdFolder)
641- assert.Equal(t, "📚", favorites.createdFolderIcon)
642- assert.Contains(t, dom.HTML(), "Created folder 📚 Reading")
643 618
644 require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{619 require.NoError(t, app.HandleDOMAction(context.Background(), DOMAction{
645 Action: favoriteActionDelete,620 Action: favoriteActionDelete,
@@ -1199,20 +1174,14 @@ func (s *recordingHistoryService) DeleteDomain(_ context.Context, domain string)
1199 1174
1200 // Recording fixture to capture favorites state for stateful render assertions.1175 // Recording fixture to capture favorites state for stateful render assertions.
1201 type recordingFavoritesService struct {1176 type recordingFavoritesService struct {
1202- calledList bool1177+ calledList bool
1203- calledFolders bool1178+ calledTags bool
1204- calledTags bool1179+ favorites []*entity.Favorite
1205- favorites []*entity.Favorite1180+ tags []*entity.Tag
1206- folders []*entity.Folder
1207- tags []*entity.Tag
1208 1181
1209 createdFavorite dto.FavoriteCreateInput1182 createdFavorite dto.FavoriteCreateInput
1210 updatedFavorite dto.FavoriteUpdateInput1183 updatedFavorite dto.FavoriteUpdateInput
1211 deletedFavorite int641184 deletedFavorite int64
1212- createdFolder string
1213- createdFolderIcon string
1214- updatedFolderID int64
1215- deletedFolderID int64
1216 createdTag string1185 createdTag string
1217 updatedTagID int641186 updatedTagID int64
1218 deletedTagID int641187 deletedTagID int64
@@ -1227,11 +1196,6 @@ func (s *recordingFavoritesService) List(context.Context) ([]*entity.Favorite, e
1227 return s.favorites, nil1196 return s.favorites, nil
1228 }1197 }
1229 1198
1230-func (s *recordingFavoritesService) ListFolders(context.Context) ([]*entity.Folder, error) {
1231- s.calledFolders = true
1232- return s.folders, nil
1233-}
1234-
1235 func (s *recordingFavoritesService) ListTags(context.Context) ([]*entity.Tag, error) {1199 func (s *recordingFavoritesService) ListTags(context.Context) ([]*entity.Tag, error) {
1236 s.calledTags = true1200 s.calledTags = true
1237 return s.tags, nil1201 return s.tags, nil
@@ -1239,12 +1203,12 @@ func (s *recordingFavoritesService) ListTags(context.Context) ([]*entity.Tag, er
1239 1203
1240 func (s *recordingFavoritesService) CreateFavorite(_ context.Context, input dto.FavoriteCreateInput) (*entity.Favorite, error) {1204 func (s *recordingFavoritesService) CreateFavorite(_ context.Context, input dto.FavoriteCreateInput) (*entity.Favorite, error) {
1241 s.createdFavorite = input1205 s.createdFavorite = input
1242- return &entity.Favorite{ID: 99, URL: input.URL, Title: input.Title, FolderID: input.FolderID}, nil1206+ return &entity.Favorite{ID: 99, URL: input.URL, Title: input.Title}, nil
1243 }1207 }
1244 1208
1245 func (s *recordingFavoritesService) UpdateFavorite(_ context.Context, input dto.FavoriteUpdateInput) (*entity.Favorite, error) {1209 func (s *recordingFavoritesService) UpdateFavorite(_ context.Context, input dto.FavoriteUpdateInput) (*entity.Favorite, error) {
1246 s.updatedFavorite = input1210 s.updatedFavorite = input
1247- return &entity.Favorite{ID: input.ID, URL: "https://example.com", Title: input.Title, FaviconURL: input.FaviconURL, FolderID: input.FolderID, ShortcutKey: input.ShortcutKey}, nil1211+ return &entity.Favorite{ID: input.ID, URL: "https://example.com", Title: input.Title, FaviconURL: input.FaviconURL, ShortcutKey: input.ShortcutKey}, nil
1248 }1212 }
1249 1213
1250 func (s *recordingFavoritesService) DeleteFavorite(_ context.Context, id int64) error {1214 func (s *recordingFavoritesService) DeleteFavorite(_ context.Context, id int64) error {
@@ -1254,24 +1218,6 @@ func (s *recordingFavoritesService) DeleteFavorite(_ context.Context, id int64)
1254 1218
1255 func (s *recordingFavoritesService) SetShortcut(context.Context, int64, *int) error { return nil }1219 func (s *recordingFavoritesService) SetShortcut(context.Context, int64, *int) error { return nil }
1256 1220
1257-func (s *recordingFavoritesService) SetFolder(context.Context, int64, *int64) error { return nil }
1258-
1259-func (s *recordingFavoritesService) CreateFolder(_ context.Context, name, icon string, _ *int64) (*entity.Folder, error) {
1260- s.createdFolder = name
1261- s.createdFolderIcon = icon
1262- return &entity.Folder{ID: 77, Name: name, Icon: icon}, nil
1263-}
1264-
1265-func (s *recordingFavoritesService) UpdateFolder(_ context.Context, id int64, _, _ string) error {
1266- s.updatedFolderID = id
1267- return nil
1268-}
1269-
1270-func (s *recordingFavoritesService) DeleteFolder(_ context.Context, id int64) error {
1271- s.deletedFolderID = id
1272- return nil
1273-}
1274-
1275 func (s *recordingFavoritesService) CreateTag(_ context.Context, name, color string) (*entity.Tag, error) {1221 func (s *recordingFavoritesService) CreateTag(_ context.Context, name, color string) (*entity.Tag, error) {
1276 s.createdTag = name1222 s.createdTag = name
1277 return &entity.Tag{ID: 55, Name: name, Color: color}, nil1223 return &entity.Tag{ID: 55, Name: name, Color: color}, nil
M internal/ui/systemviews/favorite_actions.go +20 -95
@@ -12,20 +12,17 @@ import (
1212 )
1313
1414 const (
15- favoriteActionCreate = "favorite.create"
16- favoriteActionUpdate = "favorite.update"
17- favoriteActionDelete = "favorite.delete"
18- favoriteActionFilterFolder = "favorite.filterFolder"
19- favoriteActionFilterTag = "favorite.filterTag"
20- favoriteActionClearFilters = "favorite.clearFilters"
21- folderActionCreate = "folder.create"
22- folderActionUpdate = "folder.update"
23- folderActionDelete = "folder.delete"
24- tagActionCreate = "tag.create"
25- tagActionUpdate = "tag.update"
26- tagActionDelete = "tag.delete"
27- tagActionAssign = "tag.assign"
28- tagActionRemove = "tag.remove"
15+ favoriteActionCreate = "favorite.create"
16+ favoriteActionUpdate = "favorite.update"
17+ favoriteActionDelete = "favorite.delete"
18+ favoriteActionFilterTag = "favorite.filterTag"
19+ favoriteActionFilterUntagged = "favorite.filterUntagged"
20+ favoriteActionClearFilters = "favorite.clearFilters"
21+ tagActionCreate = "tag.create"
22+ tagActionUpdate = "tag.update"
23+ tagActionDelete = "tag.delete"
24+ tagActionAssign = "tag.assign"
25+ tagActionRemove = "tag.remove"
2926 )
3027
3128 //nolint:gocyclo,funlen // Mechanical dispatcher keeps action routing in one place.
@@ -36,10 +33,6 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
3633 data := event.Data
3734 switch event.Action {
3835 case favoriteActionCreate:
39- folderID, err := optionalFolderID(data["folder_id"])
40- if err != nil {
41- return err
42- }
4336 favoriteURL, err := validateFavoriteURL(data["url"])
4437 if err != nil {
4538 return err
@@ -49,10 +42,9 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
4942 return err
5043 }
5144 favorite, err := a.deps.Favorites.CreateFavorite(ctx, dto.FavoriteCreateInput{
52- URL: favoriteURL,
53- Title: strings.TrimSpace(data["title"]),
54- FolderID: folderID,
55- Tags: tags,
45+ URL: favoriteURL,
46+ Title: strings.TrimSpace(data["title"]),
47+ Tags: tags,
5648 })
5749 if err != nil {
5850 return err
@@ -63,10 +55,6 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
6355 if err != nil {
6456 return err
6557 }
66- folderID, err := optionalFolderID(data["folder_id"])
67- if err != nil {
68- return err
69- }
7058 shortcut, err := optionalShortcut(data["shortcut_key"])
7159 if err != nil {
7260 return err
@@ -75,7 +63,6 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
7563 ID: entity.FavoriteID(id),
7664 Title: strings.TrimSpace(data["title"]),
7765 FaviconURL: strings.TrimSpace(data["favicon_url"]),
78- FolderID: folderID,
7966 ShortcutKey: shortcut,
8067 })
8168 if err != nil {
@@ -91,14 +78,6 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
9178 return err
9279 }
9380 a.favoritesNotice = "Deleted favorite"
94- case favoriteActionFilterFolder:
95- folderID, err := filterFolderID(firstActionValue(data, "folderId", "folder_id"))
96- if err != nil {
97- return err
98- }
99- a.favoriteFolderFilter = folderID
100- a.favoriteTagFilter = nil
101- a.favoritesNotice = ""
10281 case favoriteActionFilterTag:
10382 tagID, err := parsePositiveInt64(firstActionValue(data, "tagId", "tag_id"), "tag id")
10483 if err != nil {
@@ -106,45 +85,16 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
10685 }
10786 id := entity.TagID(tagID)
10887 a.favoriteTagFilter = &id
109- a.favoriteFolderFilter = nil
88+ a.favoriteUntaggedFilter = false
89+ a.favoritesNotice = ""
90+ case favoriteActionFilterUntagged:
91+ a.favoriteTagFilter = nil
92+ a.favoriteUntaggedFilter = true
11093 a.favoritesNotice = ""
11194 case favoriteActionClearFilters:
112- a.favoriteFolderFilter = nil
11395 a.favoriteTagFilter = nil
96+ a.favoriteUntaggedFilter = false
11497 a.favoritesNotice = ""
115- case folderActionCreate:
116- name := strings.TrimSpace(data["name"])
117- if name == "" {
118- return fmt.Errorf("folder name is required")
119- }
120- folder, err := a.deps.Favorites.CreateFolder(ctx, name, strings.TrimSpace(data["icon"]), nil)
121- if err != nil {
122- return err
123- }
124- a.favoritesNotice = "Created folder " + folderDisplayName(folder)
125- case folderActionUpdate:
126- id, err := parsePositiveInt64(data["id"], "folder id")
127- if err != nil {
128- return err
129- }
130- name := strings.TrimSpace(data["name"])
131- if name == "" {
132- return fmt.Errorf("folder name is required")
133- }
134- if err := a.deps.Favorites.UpdateFolder(ctx, id, name, strings.TrimSpace(data["icon"])); err != nil {
135- return err
136- }
137- a.favoritesNotice = "Saved folder " + name
138- case folderActionDelete:
139- id, err := parsePositiveInt64(data["id"], "folder id")
140- if err != nil {
141- return err
142- }
143- if err := a.deps.Favorites.DeleteFolder(ctx, id); err != nil {
144- return err
145- }
146- a.favoriteFolderFilter = nil
147- a.favoritesNotice = "Deleted folder"
14898 case tagActionCreate:
14999 name := strings.TrimSpace(data["name"])
150100 if name == "" {
@@ -272,31 +222,6 @@ func optionalTagIDs(raw string) ([]entity.TagID, error) {
272222 return ids, nil
273223 }
274224
275-// optionalFolderID returns nil for empty or "root" input, meaning no folder constraint;
276-// otherwise it parses a positive folder id into a non-nil *entity.FolderID.
277-func optionalFolderID(raw string) (*entity.FolderID, error) {
278- raw = strings.TrimSpace(raw)
279- if raw == "" || raw == "root" {
280- return nil, nil
281- }
282- id, err := parsePositiveInt64(raw, "folder id")
283- if err != nil {
284- return nil, err
285- }
286- folderID := entity.FolderID(id)
287- return &folderID, nil
288-}
289-
290-// filterFolderID returns *entity.FolderID(0) for "root" to explicitly filter
291-// favorites in the root folder; other inputs delegate to optionalFolderID.
292-func filterFolderID(raw string) (*entity.FolderID, error) {
293- if strings.TrimSpace(raw) == "root" {
294- root := entity.FolderID(0)
295- return &root, nil
296- }
297- return optionalFolderID(raw)
298-}
299-
300225 // optionalShortcut accepts favorite keyboard shortcuts 1–9; empty input clears the shortcut.
301226 func optionalShortcut(raw string) (*int, error) {
302227 raw = strings.TrimSpace(raw)
@@ -12,20 +12,17 @@ import (
12 )12 )
13 13
14 const (14 const (
15- favoriteActionCreate = "favorite.create"15+ favoriteActionCreate = "favorite.create"
16- favoriteActionUpdate = "favorite.update"16+ favoriteActionUpdate = "favorite.update"
17- favoriteActionDelete = "favorite.delete"17+ favoriteActionDelete = "favorite.delete"
18- favoriteActionFilterFolder = "favorite.filterFolder"18+ favoriteActionFilterTag = "favorite.filterTag"
19- favoriteActionFilterTag = "favorite.filterTag"19+ favoriteActionFilterUntagged = "favorite.filterUntagged"
20- favoriteActionClearFilters = "favorite.clearFilters"20+ favoriteActionClearFilters = "favorite.clearFilters"
21- folderActionCreate = "folder.create"21+ tagActionCreate = "tag.create"
22- folderActionUpdate = "folder.update"22+ tagActionUpdate = "tag.update"
23- folderActionDelete = "folder.delete"23+ tagActionDelete = "tag.delete"
24- tagActionCreate = "tag.create"24+ tagActionAssign = "tag.assign"
25- tagActionUpdate = "tag.update"25+ tagActionRemove = "tag.remove"
26- tagActionDelete = "tag.delete"
27- tagActionAssign = "tag.assign"
28- tagActionRemove = "tag.remove"
29 )26 )
30 27
31 //nolint:gocyclo,funlen // Mechanical dispatcher keeps action routing in one place.28 //nolint:gocyclo,funlen // Mechanical dispatcher keeps action routing in one place.
@@ -36,10 +33,6 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
36 data := event.Data33 data := event.Data
37 switch event.Action {34 switch event.Action {
38 case favoriteActionCreate:35 case favoriteActionCreate:
39- folderID, err := optionalFolderID(data["folder_id"])
40- if err != nil {
41- return err
42- }
43 favoriteURL, err := validateFavoriteURL(data["url"])36 favoriteURL, err := validateFavoriteURL(data["url"])
44 if err != nil {37 if err != nil {
45 return err38 return err
@@ -49,10 +42,9 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
49 return err42 return err
50 }43 }
51 favorite, err := a.deps.Favorites.CreateFavorite(ctx, dto.FavoriteCreateInput{44 favorite, err := a.deps.Favorites.CreateFavorite(ctx, dto.FavoriteCreateInput{
52- URL: favoriteURL,45+ URL: favoriteURL,
53- Title: strings.TrimSpace(data["title"]),46+ Title: strings.TrimSpace(data["title"]),
54- FolderID: folderID,47+ Tags: tags,
55- Tags: tags,
56 })48 })
57 if err != nil {49 if err != nil {
58 return err50 return err
@@ -63,10 +55,6 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
63 if err != nil {55 if err != nil {
64 return err56 return err
65 }57 }
66- folderID, err := optionalFolderID(data["folder_id"])
67- if err != nil {
68- return err
69- }
70 shortcut, err := optionalShortcut(data["shortcut_key"])58 shortcut, err := optionalShortcut(data["shortcut_key"])
71 if err != nil {59 if err != nil {
72 return err60 return err
@@ -75,7 +63,6 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
75 ID: entity.FavoriteID(id),63 ID: entity.FavoriteID(id),
76 Title: strings.TrimSpace(data["title"]),64 Title: strings.TrimSpace(data["title"]),
77 FaviconURL: strings.TrimSpace(data["favicon_url"]),65 FaviconURL: strings.TrimSpace(data["favicon_url"]),
78- FolderID: folderID,
79 ShortcutKey: shortcut,66 ShortcutKey: shortcut,
80 })67 })
81 if err != nil {68 if err != nil {
@@ -91,14 +78,6 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
91 return err78 return err
92 }79 }
93 a.favoritesNotice = "Deleted favorite"80 a.favoritesNotice = "Deleted favorite"
94- case favoriteActionFilterFolder:
95- folderID, err := filterFolderID(firstActionValue(data, "folderId", "folder_id"))
96- if err != nil {
97- return err
98- }
99- a.favoriteFolderFilter = folderID
100- a.favoriteTagFilter = nil
101- a.favoritesNotice = ""
102 case favoriteActionFilterTag:81 case favoriteActionFilterTag:
103 tagID, err := parsePositiveInt64(firstActionValue(data, "tagId", "tag_id"), "tag id")82 tagID, err := parsePositiveInt64(firstActionValue(data, "tagId", "tag_id"), "tag id")
104 if err != nil {83 if err != nil {
@@ -106,45 +85,16 @@ func (a *App) handleFavoriteAction(ctx context.Context, event DOMAction) error {
106 }85 }
107 id := entity.TagID(tagID)86 id := entity.TagID(tagID)
108 a.favoriteTagFilter = &id87 a.favoriteTagFilter = &id
109- a.favoriteFolderFilter = nil88+ a.favoriteUntaggedFilter = false
89+ a.favoritesNotice = ""
90+ case favoriteActionFilterUntagged:
91+ a.favoriteTagFilter = nil
92+ a.favoriteUntaggedFilter = true
110 a.favoritesNotice = ""93 a.favoritesNotice = ""
111 case favoriteActionClearFilters:94 case favoriteActionClearFilters:
112- a.favoriteFolderFilter = nil
113 a.favoriteTagFilter = nil95 a.favoriteTagFilter = nil
96+ a.favoriteUntaggedFilter = false
114 a.favoritesNotice = ""97 a.favoritesNotice = ""
115- case folderActionCreate:
116- name := strings.TrimSpace(data["name"])
117- if name == "" {
118- return fmt.Errorf("folder name is required")
119- }
120- folder, err := a.deps.Favorites.CreateFolder(ctx, name, strings.TrimSpace(data["icon"]), nil)
121- if err != nil {
122- return err
123- }
124- a.favoritesNotice = "Created folder " + folderDisplayName(folder)
125- case folderActionUpdate:
126- id, err := parsePositiveInt64(data["id"], "folder id")
127- if err != nil {
128- return err
129- }
130- name := strings.TrimSpace(data["name"])
131- if name == "" {
132- return fmt.Errorf("folder name is required")
133- }
134- if err := a.deps.Favorites.UpdateFolder(ctx, id, name, strings.TrimSpace(data["icon"])); err != nil {
135- return err
136- }
137- a.favoritesNotice = "Saved folder " + name
138- case folderActionDelete:
139- id, err := parsePositiveInt64(data["id"], "folder id")
140- if err != nil {
141- return err
142- }
143- if err := a.deps.Favorites.DeleteFolder(ctx, id); err != nil {
144- return err
145- }
146- a.favoriteFolderFilter = nil
147- a.favoritesNotice = "Deleted folder"
148 case tagActionCreate:98 case tagActionCreate:
149 name := strings.TrimSpace(data["name"])99 name := strings.TrimSpace(data["name"])
150 if name == "" {100 if name == "" {
@@ -272,31 +222,6 @@ func optionalTagIDs(raw string) ([]entity.TagID, error) {
272 return ids, nil222 return ids, nil
273 }223 }
274 224
275-// optionalFolderID returns nil for empty or "root" input, meaning no folder constraint;
276-// otherwise it parses a positive folder id into a non-nil *entity.FolderID.
277-func optionalFolderID(raw string) (*entity.FolderID, error) {
278- raw = strings.TrimSpace(raw)
279- if raw == "" || raw == "root" {
280- return nil, nil
281- }
282- id, err := parsePositiveInt64(raw, "folder id")
283- if err != nil {
284- return nil, err
285- }
286- folderID := entity.FolderID(id)
287- return &folderID, nil
288-}
289-
290-// filterFolderID returns *entity.FolderID(0) for "root" to explicitly filter
291-// favorites in the root folder; other inputs delegate to optionalFolderID.
292-func filterFolderID(raw string) (*entity.FolderID, error) {
293- if strings.TrimSpace(raw) == "root" {
294- root := entity.FolderID(0)
295- return &root, nil
296- }
297- return optionalFolderID(raw)
298-}
299-
300 // optionalShortcut accepts favorite keyboard shortcuts 1–9; empty input clears the shortcut.225 // optionalShortcut accepts favorite keyboard shortcuts 1–9; empty input clears the shortcut.
301 func optionalShortcut(raw string) (*int, error) {226 func optionalShortcut(raw string) (*int, error) {
302 raw = strings.TrimSpace(raw)227 raw = strings.TrimSpace(raw)
M internal/ui/systemviews/favorites.templ +8 -64
@@ -19,22 +19,7 @@ templ FavoritesView(data favoritesRenderData) {
1919 <ul class="sv-list">
2020 for _, favorite := range data.Favorites {
2121 if favorite != nil {
22- @FavoriteItem(favorite, data.Folders, data.Tags)
23- }
24- }
25- </ul>
26- }
27- }
28- @Section("sv-folders-list", "Folders") {
29- @Meta(foldersSummary(data))
30- @FolderCreateForm()
31- if len(data.Folders) == 0 {
32- @EmptyState("No folders")
33- } else {
34- <ul class="sv-list">
35- for _, folder := range data.Folders {
36- if folder != nil {
37- @FolderItem(folder)
22+ @FavoriteItem(favorite, data.Tags)
3823 }
3924 }
4025 </ul>
@@ -61,23 +46,14 @@ templ FavoritesControls(data favoritesRenderData) {
6146 <form class="sv-card-form" data-sv-action="favorite.create">
6247 <label><span>URL</span><input name="url" type="url" placeholder="https://example.com" required/></label>
6348 <label><span>Title</span><input name="title" type="text" placeholder="Optional title"/></label>
64- @FolderSelect("folder_id", data.Folders, nil, "Folder")
6549 @FavoriteCreateTagPicker(data.Tags)
6650 <button class="sv-button" type="submit">@ButtonIconLabel(iconPlus, "Add favorite")</button>
6751 </form>
6852 <div class="sv-filter-panel">
69- <div class="sv-button-row">
70- <span class="sv-meta">Folders</span>
71- <button type="button" class={ favoriteFilterClass(favoriteAllFilterActive(data)) } aria-pressed={ fmt.Sprintf("%t", favoriteAllFilterActive(data)) } data-sv-action="favorite.clearFilters">All</button>
72- <button type="button" class={ favoriteFilterClass(favoriteFolderFilterActive(data, entity.FolderID(0))) } aria-pressed={ fmt.Sprintf("%t", favoriteFolderFilterActive(data, entity.FolderID(0))) } data-sv-action="favorite.filterFolder" data-folder-id="root">Unfiled</button>
73- for _, folder := range data.Folders {
74- if folder != nil {
75- <button type="button" class={ favoriteFilterClass(favoriteFolderFilterActive(data, folder.ID)) } aria-pressed={ fmt.Sprintf("%t", favoriteFolderFilterActive(data, folder.ID)) } data-sv-action="favorite.filterFolder" data-folder-id={ fmt.Sprintf("%d", folder.ID) }>{ folderDisplayName(folder) }</button>
76- }
77- }
78- </div>
7953 <div class="sv-button-row">
8054 <span class="sv-meta">Tags</span>
55+ <button type="button" class={ favoriteFilterClass(favoriteAllFilterActive(data)) } aria-pressed={ fmt.Sprintf("%t", favoriteAllFilterActive(data)) } data-sv-action="favorite.clearFilters">All</button>
56+ <button type="button" class={ favoriteFilterClass(favoriteUntaggedFilterActive(data)) } aria-pressed={ fmt.Sprintf("%t", favoriteUntaggedFilterActive(data)) } data-sv-action="favorite.filterUntagged">Untagged</button>
8157 for _, tag := range data.Tags {
8258 if tag != nil {
8359 <button type="button" class={ favoriteFilterClass(favoriteTagFilterActive(data, tag.ID)) } aria-pressed={ fmt.Sprintf("%t", favoriteTagFilterActive(data, tag.ID)) } data-sv-action="favorite.filterTag" data-tag-id={ fmt.Sprintf("%d", tag.ID) }>
@@ -108,15 +84,15 @@ templ FavoriteCreateTagPicker(tags []*entity.Tag) {
10884 }
10985 }
11086
111-templ FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*entity.Tag) {
87+templ FavoriteItem(favorite *entity.Favorite, tags []*entity.Tag) {
11288 <li class="sv-list-row">
11389 <article class="sv-favorite-item" data-sv-favorite-row data-favorite-id={ fmt.Sprintf("%d", favorite.ID) }>
11490 <div class="sv-favorite-header">
11591 <div>
11692 <a class="sv-link sv-favorite-open" href={ templ.SafeURL(sanitizeHref(favorite.URL)) }>{ favoriteItemLabel(favorite) }</a>
11793 <p class="sv-history-url">{ favorite.URL }</p>
118- if favoriteMetaText(favorite, folders) != "" {
119- <p class="sv-meta">{ favoriteMetaText(favorite, folders) }</p>
94+ if favoriteMetaText(favorite) != "" {
95+ <p class="sv-meta">{ favoriteMetaText(favorite) }</p>
12096 }
12197 </div>
12298 <div class="sv-history-row-actions">
@@ -124,18 +100,17 @@ templ FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*
124100 <button type="button" class="sv-button sv-button-secondary sv-button-danger sv-button-icon-only" data-sv-action="favorite.delete" data-id={ fmt.Sprintf("%d", favorite.ID) } data-sv-confirm="Delete this favorite?" aria-label="Delete this favorite">@IconOnly(iconTrash, "Delete this favorite")</button>
125101 </div>
126102 </div>
127- @FavoriteEditForm(favorite, folders)
103+ @FavoriteEditForm(favorite)
128104 @FavoriteTagControls(favorite, tags)
129105 </article>
130106 </li>
131107 }
132108
133-templ FavoriteEditForm(favorite *entity.Favorite, folders []*entity.Folder) {
109+templ FavoriteEditForm(favorite *entity.Favorite) {
134110 <form class="sv-inline-form" data-sv-action="favorite.update">
135111 <input type="hidden" name="id" value={ fmt.Sprintf("%d", favorite.ID) }/>
136112 <label><span>Title</span><input name="title" type="text" value={ favorite.Title }/></label>
137113 <label><span>Favicon</span><input name="favicon_url" type="url" value={ favorite.FaviconURL }/></label>
138- @FolderSelect("folder_id", folders, favorite.FolderID, "Folder")
139114 @ShortcutSelect(favorite.ShortcutKey)
140115 <button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconSave, "Save")</button>
141116 </form>
@@ -156,26 +131,6 @@ templ FavoriteTagControls(favorite *entity.Favorite, tags []*entity.Tag) {
156131 }
157132 }
158133
159-templ FolderCreateForm() {
160- <form class="sv-inline-form" data-sv-action="folder.create">
161- <label><span>Name</span><input name="name" type="text" required/></label>
162- <label><span>Icon</span><input name="icon" type="text" placeholder="Optional"/></label>
163- <button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconFolder, "Create folder")</button>
164- </form>
165-}
166-
167-templ FolderItem(folder *entity.Folder) {
168- <li class="sv-list-row">
169- <form class="sv-inline-form" data-sv-action="folder.update">
170- <input type="hidden" name="id" value={ fmt.Sprintf("%d", folder.ID) }/>
171- <label><span>Name</span><input name="name" type="text" value={ folder.Name } required/></label>
172- <label><span>Icon</span><input name="icon" type="text" value={ folder.Icon }/></label>
173- <button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconSave, "Save")</button>
174- <button type="button" class="sv-button sv-button-secondary sv-button-danger sv-button-icon-only" data-sv-action="folder.delete" data-id={ fmt.Sprintf("%d", folder.ID) } data-sv-confirm="Delete this folder? Favorites will move out of it." aria-label="Delete this folder">@IconOnly(iconTrash, "Delete this folder")</button>
175- </form>
176- </li>
177-}
178-
179134 templ TagCreateForm() {
180135 <form class="sv-inline-form" data-sv-action="tag.create">
181136 <label><span>Name</span><input name="name" type="text" required/></label>
@@ -197,17 +152,6 @@ templ TagItem(tag *entity.Tag) {
197152 </li>
198153 }
199154
200-templ FolderSelect(name string, folders []*entity.Folder, selected *entity.FolderID, label string) {
201- <label><span>{ label }</span><select name={ name }>
202- <option value="">Unfiled</option>
203- for _, folder := range folders {
204- if folder != nil {
205- <option value={ fmt.Sprintf("%d", folder.ID) } selected?={ folderSelected(selected, folder.ID) }>{ folderDisplayName(folder) }</option>
206- }
207- }
208- </select></label>
209-}
210-
211155 templ ShortcutSelect(selected *int) {
212156 <label><span>Shortcut</span><select name="shortcut_key">
213157 <option value="">None</option>
@@ -19,22 +19,7 @@ templ FavoritesView(data favoritesRenderData) {
19 <ul class="sv-list">19 <ul class="sv-list">
20 for _, favorite := range data.Favorites {20 for _, favorite := range data.Favorites {
21 if favorite != nil {21 if favorite != nil {
22- @FavoriteItem(favorite, data.Folders, data.Tags)22+ @FavoriteItem(favorite, data.Tags)
23- }
24- }
25- </ul>
26- }
27- }
28- @Section("sv-folders-list", "Folders") {
29- @Meta(foldersSummary(data))
30- @FolderCreateForm()
31- if len(data.Folders) == 0 {
32- @EmptyState("No folders")
33- } else {
34- <ul class="sv-list">
35- for _, folder := range data.Folders {
36- if folder != nil {
37- @FolderItem(folder)
38 }23 }
39 }24 }
40 </ul>25 </ul>
@@ -61,23 +46,14 @@ templ FavoritesControls(data favoritesRenderData) {
61 <form class="sv-card-form" data-sv-action="favorite.create">46 <form class="sv-card-form" data-sv-action="favorite.create">
62 <label><span>URL</span><input name="url" type="url" placeholder="https://example.com" required/></label>47 <label><span>URL</span><input name="url" type="url" placeholder="https://example.com" required/></label>
63 <label><span>Title</span><input name="title" type="text" placeholder="Optional title"/></label>48 <label><span>Title</span><input name="title" type="text" placeholder="Optional title"/></label>
64- @FolderSelect("folder_id", data.Folders, nil, "Folder")
65 @FavoriteCreateTagPicker(data.Tags)49 @FavoriteCreateTagPicker(data.Tags)
66 <button class="sv-button" type="submit">@ButtonIconLabel(iconPlus, "Add favorite")</button>50 <button class="sv-button" type="submit">@ButtonIconLabel(iconPlus, "Add favorite")</button>
67 </form>51 </form>
68 <div class="sv-filter-panel">52 <div class="sv-filter-panel">
69- <div class="sv-button-row">
70- <span class="sv-meta">Folders</span>
71- <button type="button" class={ favoriteFilterClass(favoriteAllFilterActive(data)) } aria-pressed={ fmt.Sprintf("%t", favoriteAllFilterActive(data)) } data-sv-action="favorite.clearFilters">All</button>
72- <button type="button" class={ favoriteFilterClass(favoriteFolderFilterActive(data, entity.FolderID(0))) } aria-pressed={ fmt.Sprintf("%t", favoriteFolderFilterActive(data, entity.FolderID(0))) } data-sv-action="favorite.filterFolder" data-folder-id="root">Unfiled</button>
73- for _, folder := range data.Folders {
74- if folder != nil {
75- <button type="button" class={ favoriteFilterClass(favoriteFolderFilterActive(data, folder.ID)) } aria-pressed={ fmt.Sprintf("%t", favoriteFolderFilterActive(data, folder.ID)) } data-sv-action="favorite.filterFolder" data-folder-id={ fmt.Sprintf("%d", folder.ID) }>{ folderDisplayName(folder) }</button>
76- }
77- }
78- </div>
79 <div class="sv-button-row">53 <div class="sv-button-row">
80 <span class="sv-meta">Tags</span>54 <span class="sv-meta">Tags</span>
55+ <button type="button" class={ favoriteFilterClass(favoriteAllFilterActive(data)) } aria-pressed={ fmt.Sprintf("%t", favoriteAllFilterActive(data)) } data-sv-action="favorite.clearFilters">All</button>
56+ <button type="button" class={ favoriteFilterClass(favoriteUntaggedFilterActive(data)) } aria-pressed={ fmt.Sprintf("%t", favoriteUntaggedFilterActive(data)) } data-sv-action="favorite.filterUntagged">Untagged</button>
81 for _, tag := range data.Tags {57 for _, tag := range data.Tags {
82 if tag != nil {58 if tag != nil {
83 <button type="button" class={ favoriteFilterClass(favoriteTagFilterActive(data, tag.ID)) } aria-pressed={ fmt.Sprintf("%t", favoriteTagFilterActive(data, tag.ID)) } data-sv-action="favorite.filterTag" data-tag-id={ fmt.Sprintf("%d", tag.ID) }>59 <button type="button" class={ favoriteFilterClass(favoriteTagFilterActive(data, tag.ID)) } aria-pressed={ fmt.Sprintf("%t", favoriteTagFilterActive(data, tag.ID)) } data-sv-action="favorite.filterTag" data-tag-id={ fmt.Sprintf("%d", tag.ID) }>
@@ -108,15 +84,15 @@ templ FavoriteCreateTagPicker(tags []*entity.Tag) {
108 }84 }
109 }85 }
110 86
111-templ FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*entity.Tag) {87+templ FavoriteItem(favorite *entity.Favorite, tags []*entity.Tag) {
112 <li class="sv-list-row">88 <li class="sv-list-row">
113 <article class="sv-favorite-item" data-sv-favorite-row data-favorite-id={ fmt.Sprintf("%d", favorite.ID) }>89 <article class="sv-favorite-item" data-sv-favorite-row data-favorite-id={ fmt.Sprintf("%d", favorite.ID) }>
114 <div class="sv-favorite-header">90 <div class="sv-favorite-header">
115 <div>91 <div>
116 <a class="sv-link sv-favorite-open" href={ templ.SafeURL(sanitizeHref(favorite.URL)) }>{ favoriteItemLabel(favorite) }</a>92 <a class="sv-link sv-favorite-open" href={ templ.SafeURL(sanitizeHref(favorite.URL)) }>{ favoriteItemLabel(favorite) }</a>
117 <p class="sv-history-url">{ favorite.URL }</p>93 <p class="sv-history-url">{ favorite.URL }</p>
118- if favoriteMetaText(favorite, folders) != "" {94+ if favoriteMetaText(favorite) != "" {
119- <p class="sv-meta">{ favoriteMetaText(favorite, folders) }</p>95+ <p class="sv-meta">{ favoriteMetaText(favorite) }</p>
120 }96 }
121 </div>97 </div>
122 <div class="sv-history-row-actions">98 <div class="sv-history-row-actions">
@@ -124,18 +100,17 @@ templ FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*
124 <button type="button" class="sv-button sv-button-secondary sv-button-danger sv-button-icon-only" data-sv-action="favorite.delete" data-id={ fmt.Sprintf("%d", favorite.ID) } data-sv-confirm="Delete this favorite?" aria-label="Delete this favorite">@IconOnly(iconTrash, "Delete this favorite")</button>100 <button type="button" class="sv-button sv-button-secondary sv-button-danger sv-button-icon-only" data-sv-action="favorite.delete" data-id={ fmt.Sprintf("%d", favorite.ID) } data-sv-confirm="Delete this favorite?" aria-label="Delete this favorite">@IconOnly(iconTrash, "Delete this favorite")</button>
125 </div>101 </div>
126 </div>102 </div>
127- @FavoriteEditForm(favorite, folders)103+ @FavoriteEditForm(favorite)
128 @FavoriteTagControls(favorite, tags)104 @FavoriteTagControls(favorite, tags)
129 </article>105 </article>
130 </li>106 </li>
131 }107 }
132 108
133-templ FavoriteEditForm(favorite *entity.Favorite, folders []*entity.Folder) {109+templ FavoriteEditForm(favorite *entity.Favorite) {
134 <form class="sv-inline-form" data-sv-action="favorite.update">110 <form class="sv-inline-form" data-sv-action="favorite.update">
135 <input type="hidden" name="id" value={ fmt.Sprintf("%d", favorite.ID) }/>111 <input type="hidden" name="id" value={ fmt.Sprintf("%d", favorite.ID) }/>
136 <label><span>Title</span><input name="title" type="text" value={ favorite.Title }/></label>112 <label><span>Title</span><input name="title" type="text" value={ favorite.Title }/></label>
137 <label><span>Favicon</span><input name="favicon_url" type="url" value={ favorite.FaviconURL }/></label>113 <label><span>Favicon</span><input name="favicon_url" type="url" value={ favorite.FaviconURL }/></label>
138- @FolderSelect("folder_id", folders, favorite.FolderID, "Folder")
139 @ShortcutSelect(favorite.ShortcutKey)114 @ShortcutSelect(favorite.ShortcutKey)
140 <button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconSave, "Save")</button>115 <button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconSave, "Save")</button>
141 </form>116 </form>
@@ -156,26 +131,6 @@ templ FavoriteTagControls(favorite *entity.Favorite, tags []*entity.Tag) {
156 }131 }
157 }132 }
158 133
159-templ FolderCreateForm() {
160- <form class="sv-inline-form" data-sv-action="folder.create">
161- <label><span>Name</span><input name="name" type="text" required/></label>
162- <label><span>Icon</span><input name="icon" type="text" placeholder="Optional"/></label>
163- <button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconFolder, "Create folder")</button>
164- </form>
165-}
166-
167-templ FolderItem(folder *entity.Folder) {
168- <li class="sv-list-row">
169- <form class="sv-inline-form" data-sv-action="folder.update">
170- <input type="hidden" name="id" value={ fmt.Sprintf("%d", folder.ID) }/>
171- <label><span>Name</span><input name="name" type="text" value={ folder.Name } required/></label>
172- <label><span>Icon</span><input name="icon" type="text" value={ folder.Icon }/></label>
173- <button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconSave, "Save")</button>
174- <button type="button" class="sv-button sv-button-secondary sv-button-danger sv-button-icon-only" data-sv-action="folder.delete" data-id={ fmt.Sprintf("%d", folder.ID) } data-sv-confirm="Delete this folder? Favorites will move out of it." aria-label="Delete this folder">@IconOnly(iconTrash, "Delete this folder")</button>
175- </form>
176- </li>
177-}
178-
179 templ TagCreateForm() {134 templ TagCreateForm() {
180 <form class="sv-inline-form" data-sv-action="tag.create">135 <form class="sv-inline-form" data-sv-action="tag.create">
181 <label><span>Name</span><input name="name" type="text" required/></label>136 <label><span>Name</span><input name="name" type="text" required/></label>
@@ -197,17 +152,6 @@ templ TagItem(tag *entity.Tag) {
197 </li>152 </li>
198 }153 }
199 154
200-templ FolderSelect(name string, folders []*entity.Folder, selected *entity.FolderID, label string) {
201- <label><span>{ label }</span><select name={ name }>
202- <option value="">Unfiled</option>
203- for _, folder := range folders {
204- if folder != nil {
205- <option value={ fmt.Sprintf("%d", folder.ID) } selected?={ folderSelected(selected, folder.ID) }>{ folderDisplayName(folder) }</option>
206- }
207- }
208- </select></label>
209-}
210-
211 templ ShortcutSelect(selected *int) {155 templ ShortcutSelect(selected *int) {
212 <label><span>Shortcut</span><select name="shortcut_key">156 <label><span>Shortcut</span><select name="shortcut_key">
213 <option value="">None</option>157 <option value="">None</option>
M internal/ui/systemviews/favorites_templ.go +245 -616
@@ -95,7 +95,7 @@ func FavoritesView(data favoritesRenderData) templ.Component {
9595 }
9696 for _, favorite := range data.Favorites {
9797 if favorite != nil {
98- templ_7745c5c3_Err = FavoriteItem(favorite, data.Folders, data.Tags).Render(ctx, templ_7745c5c3_Buffer)
98+ templ_7745c5c3_Err = FavoriteItem(favorite, data.Tags).Render(ctx, templ_7745c5c3_Buffer)
9999 if templ_7745c5c3_Err != nil {
100100 return templ_7745c5c3_Err
101101 }
@@ -113,63 +113,6 @@ func FavoritesView(data favoritesRenderData) templ.Component {
113113 return templ_7745c5c3_Err
114114 }
115115 templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
116- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
117- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
118- if !templ_7745c5c3_IsBuffer {
119- defer func() {
120- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
121- if templ_7745c5c3_Err == nil {
122- templ_7745c5c3_Err = templ_7745c5c3_BufErr
123- }
124- }()
125- }
126- ctx = templ.InitializeContext(ctx)
127- templ_7745c5c3_Err = Meta(foldersSummary(data)).Render(ctx, templ_7745c5c3_Buffer)
128- if templ_7745c5c3_Err != nil {
129- return templ_7745c5c3_Err
130- }
131- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
132- if templ_7745c5c3_Err != nil {
133- return templ_7745c5c3_Err
134- }
135- templ_7745c5c3_Err = FolderCreateForm().Render(ctx, templ_7745c5c3_Buffer)
136- if templ_7745c5c3_Err != nil {
137- return templ_7745c5c3_Err
138- }
139- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " ")
140- if templ_7745c5c3_Err != nil {
141- return templ_7745c5c3_Err
142- }
143- if len(data.Folders) == 0 {
144- templ_7745c5c3_Err = EmptyState("No folders").Render(ctx, templ_7745c5c3_Buffer)
145- if templ_7745c5c3_Err != nil {
146- return templ_7745c5c3_Err
147- }
148- } else {
149- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<ul class=\"sv-list\">")
150- if templ_7745c5c3_Err != nil {
151- return templ_7745c5c3_Err
152- }
153- for _, folder := range data.Folders {
154- if folder != nil {
155- templ_7745c5c3_Err = FolderItem(folder).Render(ctx, templ_7745c5c3_Buffer)
156- if templ_7745c5c3_Err != nil {
157- return templ_7745c5c3_Err
158- }
159- }
160- }
161- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</ul>")
162- if templ_7745c5c3_Err != nil {
163- return templ_7745c5c3_Err
164- }
165- }
166- return nil
167- })
168- templ_7745c5c3_Err = Section("sv-folders-list", "Folders").Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
169- if templ_7745c5c3_Err != nil {
170- return templ_7745c5c3_Err
171- }
172- templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
173116 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
174117 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
175118 if !templ_7745c5c3_IsBuffer {
@@ -185,7 +128,7 @@ func FavoritesView(data favoritesRenderData) templ.Component {
185128 if templ_7745c5c3_Err != nil {
186129 return templ_7745c5c3_Err
187130 }
188- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " ")
131+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
189132 if templ_7745c5c3_Err != nil {
190133 return templ_7745c5c3_Err
191134 }
@@ -193,7 +136,7 @@ func FavoritesView(data favoritesRenderData) templ.Component {
193136 if templ_7745c5c3_Err != nil {
194137 return templ_7745c5c3_Err
195138 }
196- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " ")
139+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " ")
197140 if templ_7745c5c3_Err != nil {
198141 return templ_7745c5c3_Err
199142 }
@@ -203,7 +146,7 @@ func FavoritesView(data favoritesRenderData) templ.Component {
203146 return templ_7745c5c3_Err
204147 }
205148 } else {
206- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<ul class=\"sv-list\">")
149+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<ul class=\"sv-list\">")
207150 if templ_7745c5c3_Err != nil {
208151 return templ_7745c5c3_Err
209152 }
@@ -215,14 +158,14 @@ func FavoritesView(data favoritesRenderData) templ.Component {
215158 }
216159 }
217160 }
218- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</ul>")
161+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</ul>")
219162 if templ_7745c5c3_Err != nil {
220163 return templ_7745c5c3_Err
221164 }
222165 }
223166 return nil
224167 })
225- templ_7745c5c3_Err = Section("sv-tags-list", "Tags").Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
168+ templ_7745c5c3_Err = Section("sv-tags-list", "Tags").Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
226169 if templ_7745c5c3_Err != nil {
227170 return templ_7745c5c3_Err
228171 }
@@ -246,16 +189,12 @@ func FavoritesControls(data favoritesRenderData) templ.Component {
246189 }()
247190 }
248191 ctx = templ.InitializeContext(ctx)
249- templ_7745c5c3_Var6 := templ.GetChildren(ctx)
250- if templ_7745c5c3_Var6 == nil {
251- templ_7745c5c3_Var6 = templ.NopComponent
192+ templ_7745c5c3_Var5 := templ.GetChildren(ctx)
193+ if templ_7745c5c3_Var5 == nil {
194+ templ_7745c5c3_Var5 = templ.NopComponent
252195 }
253196 ctx = templ.ClearChildren(ctx)
254- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<form class=\"sv-card-form\" data-sv-action=\"favorite.create\"><label><span>URL</span><input name=\"url\" type=\"url\" placeholder=\"https://example.com\" required></label> <label><span>Title</span><input name=\"title\" type=\"text\" placeholder=\"Optional title\"></label>")
255- if templ_7745c5c3_Err != nil {
256- return templ_7745c5c3_Err
257- }
258- templ_7745c5c3_Err = FolderSelect("folder_id", data.Folders, nil, "Folder").Render(ctx, templ_7745c5c3_Buffer)
197+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<form class=\"sv-card-form\" data-sv-action=\"favorite.create\"><label><span>URL</span><input name=\"url\" type=\"url\" placeholder=\"https://example.com\" required></label> <label><span>Title</span><input name=\"title\" type=\"text\" placeholder=\"Optional title\"></label>")
259198 if templ_7745c5c3_Err != nil {
260199 return templ_7745c5c3_Err
261200 }
@@ -263,7 +202,7 @@ func FavoritesControls(data favoritesRenderData) templ.Component {
263202 if templ_7745c5c3_Err != nil {
264203 return templ_7745c5c3_Err
265204 }
266- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<button class=\"sv-button\" type=\"submit\">")
205+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<button class=\"sv-button\" type=\"submit\">")
267206 if templ_7745c5c3_Err != nil {
268207 return templ_7745c5c3_Err
269208 }
@@ -271,228 +210,159 @@ func FavoritesControls(data favoritesRenderData) templ.Component {
271210 if templ_7745c5c3_Err != nil {
272211 return templ_7745c5c3_Err
273212 }
274- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</button></form><div class=\"sv-filter-panel\"><div class=\"sv-button-row\"><span class=\"sv-meta\">Folders</span> ")
213+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</button></form><div class=\"sv-filter-panel\"><div class=\"sv-button-row\"><span class=\"sv-meta\">Tags</span> ")
275214 if templ_7745c5c3_Err != nil {
276215 return templ_7745c5c3_Err
277216 }
278- var templ_7745c5c3_Var7 = []any{favoriteFilterClass(favoriteAllFilterActive(data))}
279- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var7...)
217+ var templ_7745c5c3_Var6 = []any{favoriteFilterClass(favoriteAllFilterActive(data))}
218+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...)
280219 if templ_7745c5c3_Err != nil {
281220 return templ_7745c5c3_Err
282221 }
283- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<button type=\"button\" class=\"")
222+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<button type=\"button\" class=\"")
284223 if templ_7745c5c3_Err != nil {
285224 return templ_7745c5c3_Err
286225 }
287- var templ_7745c5c3_Var8 string
288- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var7).String())
226+ var templ_7745c5c3_Var7 string
227+ templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var6).String())
289228 if templ_7745c5c3_Err != nil {
290229 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}
291230 }
292- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
231+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
293232 if templ_7745c5c3_Err != nil {
294233 return templ_7745c5c3_Err
295234 }
296- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" aria-pressed=\"")
235+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" aria-pressed=\"")
297236 if templ_7745c5c3_Err != nil {
298237 return templ_7745c5c3_Err
299238 }
300- var templ_7745c5c3_Var9 string
301- templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteAllFilterActive(data)))
239+ var templ_7745c5c3_Var8 string
240+ templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteAllFilterActive(data)))
302241 if templ_7745c5c3_Err != nil {
303- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 71, Col: 149}
242+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 55, Col: 149}
304243 }
305- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
244+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
306245 if templ_7745c5c3_Err != nil {
307246 return templ_7745c5c3_Err
308247 }
309- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" data-sv-action=\"favorite.clearFilters\">All</button> ")
248+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" data-sv-action=\"favorite.clearFilters\">All</button> ")
310249 if templ_7745c5c3_Err != nil {
311250 return templ_7745c5c3_Err
312251 }
313- var templ_7745c5c3_Var10 = []any{favoriteFilterClass(favoriteFolderFilterActive(data, entity.FolderID(0)))}
314- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...)
252+ var templ_7745c5c3_Var9 = []any{favoriteFilterClass(favoriteUntaggedFilterActive(data))}
253+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...)
315254 if templ_7745c5c3_Err != nil {
316255 return templ_7745c5c3_Err
317256 }
318- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<button type=\"button\" class=\"")
257+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<button type=\"button\" class=\"")
319258 if templ_7745c5c3_Err != nil {
320259 return templ_7745c5c3_Err
321260 }
322- var templ_7745c5c3_Var11 string
323- templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var10).String())
261+ var templ_7745c5c3_Var10 string
262+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var9).String())
324263 if templ_7745c5c3_Err != nil {
325264 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}
326265 }
327- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
266+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
328267 if templ_7745c5c3_Err != nil {
329268 return templ_7745c5c3_Err
330269 }
331- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" aria-pressed=\"")
270+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\" aria-pressed=\"")
332271 if templ_7745c5c3_Err != nil {
333272 return templ_7745c5c3_Err
334273 }
335- var templ_7745c5c3_Var12 string
336- templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteFolderFilterActive(data, entity.FolderID(0))))
274+ var templ_7745c5c3_Var11 string
275+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteUntaggedFilterActive(data)))
337276 if templ_7745c5c3_Err != nil {
338- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 72, Col: 195}
277+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 56, Col: 159}
339278 }
340- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12)
279+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
341280 if templ_7745c5c3_Err != nil {
342281 return templ_7745c5c3_Err
343282 }
344- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\" data-sv-action=\"favorite.filterFolder\" data-folder-id=\"root\">Unfiled</button> ")
345- if templ_7745c5c3_Err != nil {
346- return templ_7745c5c3_Err
347- }
348- for _, folder := range data.Folders {
349- if folder != nil {
350- var templ_7745c5c3_Var13 = []any{favoriteFilterClass(favoriteFolderFilterActive(data, folder.ID))}
351- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var13...)
352- if templ_7745c5c3_Err != nil {
353- return templ_7745c5c3_Err
354- }
355- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<button type=\"button\" class=\"")
356- if templ_7745c5c3_Err != nil {
357- return templ_7745c5c3_Err
358- }
359- var templ_7745c5c3_Var14 string
360- templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var13).String())
361- if templ_7745c5c3_Err != nil {
362- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}
363- }
364- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
365- if templ_7745c5c3_Err != nil {
366- return templ_7745c5c3_Err
367- }
368- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" aria-pressed=\"")
369- if templ_7745c5c3_Err != nil {
370- return templ_7745c5c3_Err
371- }
372- var templ_7745c5c3_Var15 string
373- templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteFolderFilterActive(data, folder.ID)))
374- if templ_7745c5c3_Err != nil {
375- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 75, Col: 179}
376- }
377- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
378- if templ_7745c5c3_Err != nil {
379- return templ_7745c5c3_Err
380- }
381- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" data-sv-action=\"favorite.filterFolder\" data-folder-id=\"")
382- if templ_7745c5c3_Err != nil {
383- return templ_7745c5c3_Err
384- }
385- var templ_7745c5c3_Var16 string
386- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", folder.ID))
387- if templ_7745c5c3_Err != nil {
388- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 75, Col: 266}
389- }
390- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
391- if templ_7745c5c3_Err != nil {
392- return templ_7745c5c3_Err
393- }
394- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\">")
395- if templ_7745c5c3_Err != nil {
396- return templ_7745c5c3_Err
397- }
398- var templ_7745c5c3_Var17 string
399- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(folderDisplayName(folder))
400- if templ_7745c5c3_Err != nil {
401- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 75, Col: 296}
402- }
403- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
404- if templ_7745c5c3_Err != nil {
405- return templ_7745c5c3_Err
406- }
407- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</button>")
408- if templ_7745c5c3_Err != nil {
409- return templ_7745c5c3_Err
410- }
411- }
412- }
413- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><div class=\"sv-button-row\"><span class=\"sv-meta\">Tags</span> ")
283+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" data-sv-action=\"favorite.filterUntagged\">Untagged</button> ")
414284 if templ_7745c5c3_Err != nil {
415285 return templ_7745c5c3_Err
416286 }
417287 for _, tag := range data.Tags {
418288 if tag != nil {
419- var templ_7745c5c3_Var18 = []any{favoriteFilterClass(favoriteTagFilterActive(data, tag.ID))}
420- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...)
289+ var templ_7745c5c3_Var12 = []any{favoriteFilterClass(favoriteTagFilterActive(data, tag.ID))}
290+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
421291 if templ_7745c5c3_Err != nil {
422292 return templ_7745c5c3_Err
423293 }
424- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<button type=\"button\" class=\"")
294+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<button type=\"button\" class=\"")
425295 if templ_7745c5c3_Err != nil {
426296 return templ_7745c5c3_Err
427297 }
428- var templ_7745c5c3_Var19 string
429- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var18).String())
298+ var templ_7745c5c3_Var13 string
299+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var12).String())
430300 if templ_7745c5c3_Err != nil {
431301 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}
432302 }
433- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)
303+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
434304 if templ_7745c5c3_Err != nil {
435305 return templ_7745c5c3_Err
436306 }
437- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\" aria-pressed=\"")
307+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\" aria-pressed=\"")
438308 if templ_7745c5c3_Err != nil {
439309 return templ_7745c5c3_Err
440310 }
441- var templ_7745c5c3_Var20 string
442- templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteTagFilterActive(data, tag.ID)))
311+ var templ_7745c5c3_Var14 string
312+ templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteTagFilterActive(data, tag.ID)))
443313 if templ_7745c5c3_Err != nil {
444- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 83, Col: 167}
314+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 59, Col: 167}
445315 }
446- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20)
316+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
447317 if templ_7745c5c3_Err != nil {
448318 return templ_7745c5c3_Err
449319 }
450- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" data-sv-action=\"favorite.filterTag\" data-tag-id=\"")
320+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" data-sv-action=\"favorite.filterTag\" data-tag-id=\"")
451321 if templ_7745c5c3_Err != nil {
452322 return templ_7745c5c3_Err
453323 }
454- var templ_7745c5c3_Var21 string
455- templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
324+ var templ_7745c5c3_Var15 string
325+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
456326 if templ_7745c5c3_Err != nil {
457- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 83, Col: 245}
327+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 59, Col: 245}
458328 }
459- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21)
329+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
460330 if templ_7745c5c3_Err != nil {
461331 return templ_7745c5c3_Err
462332 }
463- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\"><span class=\"sv-tag-dot\" style=\"")
333+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\"><span class=\"sv-tag-dot\" style=\"")
464334 if templ_7745c5c3_Err != nil {
465335 return templ_7745c5c3_Err
466336 }
467- var templ_7745c5c3_Var22 string
468- templ_7745c5c3_Var22, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
337+ var templ_7745c5c3_Var16 string
338+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
469339 if templ_7745c5c3_Err != nil {
470- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 84, Col: 78}
340+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 60, Col: 78}
471341 }
472- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
342+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
473343 if templ_7745c5c3_Err != nil {
474344 return templ_7745c5c3_Err
475345 }
476- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\"></span>")
346+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\"></span>")
477347 if templ_7745c5c3_Err != nil {
478348 return templ_7745c5c3_Err
479349 }
480- var templ_7745c5c3_Var23 string
481- templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(tag.Name)
350+ var templ_7745c5c3_Var17 string
351+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(tag.Name)
482352 if templ_7745c5c3_Err != nil {
483- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 84, Col: 98}
353+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 60, Col: 98}
484354 }
485- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
355+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
486356 if templ_7745c5c3_Err != nil {
487357 return templ_7745c5c3_Err
488358 }
489- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</button>")
359+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</button>")
490360 if templ_7745c5c3_Err != nil {
491361 return templ_7745c5c3_Err
492362 }
493363 }
494364 }
495- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</div></div><p class=\"sv-meta sv-key-hints\">Keys: <kbd>j</kbd>/<kbd>k</kbd> move, <kbd>Enter</kbd> open, <kbd>e</kbd> edit title, <kbd>d</kbd> delete focused favorite.</p>")
365+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</div></div><p class=\"sv-meta sv-key-hints\">Keys: <kbd>j</kbd>/<kbd>k</kbd> move, <kbd>Enter</kbd> open, <kbd>e</kbd> edit title, <kbd>d</kbd> delete focused favorite.</p>")
496366 if templ_7745c5c3_Err != nil {
497367 return templ_7745c5c3_Err
498368 }
@@ -516,64 +386,64 @@ func FavoriteCreateTagPicker(tags []*entity.Tag) templ.Component {
516386 }()
517387 }
518388 ctx = templ.InitializeContext(ctx)
519- templ_7745c5c3_Var24 := templ.GetChildren(ctx)
520- if templ_7745c5c3_Var24 == nil {
521- templ_7745c5c3_Var24 = templ.NopComponent
389+ templ_7745c5c3_Var18 := templ.GetChildren(ctx)
390+ if templ_7745c5c3_Var18 == nil {
391+ templ_7745c5c3_Var18 = templ.NopComponent
522392 }
523393 ctx = templ.ClearChildren(ctx)
524394 if len(tags) > 0 {
525- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<fieldset class=\"sv-tag-picker\"><legend>Tags</legend><div class=\"sv-tag-controls\">")
395+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<fieldset class=\"sv-tag-picker\"><legend>Tags</legend><div class=\"sv-tag-controls\">")
526396 if templ_7745c5c3_Err != nil {
527397 return templ_7745c5c3_Err
528398 }
529399 for _, tag := range tags {
530400 if tag != nil {
531- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<label class=\"sv-tag-toggle\"><input type=\"checkbox\" name=\"tags\" value=\"")
401+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<label class=\"sv-tag-toggle\"><input type=\"checkbox\" name=\"tags\" value=\"")
532402 if templ_7745c5c3_Err != nil {
533403 return templ_7745c5c3_Err
534404 }
535- var templ_7745c5c3_Var25 string
536- templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
405+ var templ_7745c5c3_Var19 string
406+ templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
537407 if templ_7745c5c3_Err != nil {
538- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 101, Col: 75}
408+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 77, Col: 75}
539409 }
540- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)
410+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)
541411 if templ_7745c5c3_Err != nil {
542412 return templ_7745c5c3_Err
543413 }
544- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\"> <span><span class=\"sv-tag-dot\" style=\"")
414+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\"> <span><span class=\"sv-tag-dot\" style=\"")
545415 if templ_7745c5c3_Err != nil {
546416 return templ_7745c5c3_Err
547417 }
548- var templ_7745c5c3_Var26 string
549- templ_7745c5c3_Var26, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
418+ var templ_7745c5c3_Var20 string
419+ templ_7745c5c3_Var20, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
550420 if templ_7745c5c3_Err != nil {
551- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 102, Col: 85}
421+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 78, Col: 85}
552422 }
553- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
423+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
554424 if templ_7745c5c3_Err != nil {
555425 return templ_7745c5c3_Err
556426 }
557- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\"></span>")
427+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\"></span>")
558428 if templ_7745c5c3_Err != nil {
559429 return templ_7745c5c3_Err
560430 }
561- var templ_7745c5c3_Var27 string
562- templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(tag.Name)
431+ var templ_7745c5c3_Var21 string
432+ templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(tag.Name)
563433 if templ_7745c5c3_Err != nil {
564- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 102, Col: 105}
434+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 78, Col: 105}
565435 }
566- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
436+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
567437 if templ_7745c5c3_Err != nil {
568438 return templ_7745c5c3_Err
569439 }
570- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</span></label>")
440+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</span></label>")
571441 if templ_7745c5c3_Err != nil {
572442 return templ_7745c5c3_Err
573443 }
574444 }
575445 }
576- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</div></fieldset>")
446+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</div></fieldset>")
577447 if templ_7745c5c3_Err != nil {
578448 return templ_7745c5c3_Err
579449 }
@@ -582,7 +452,7 @@ func FavoriteCreateTagPicker(tags []*entity.Tag) templ.Component {
582452 })
583453 }
584454
585-func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*entity.Tag) templ.Component {
455+func FavoriteItem(favorite *entity.Favorite, tags []*entity.Tag) templ.Component {
586456 return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
587457 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
588458 if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -598,100 +468,100 @@ func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*e
598468 }()
599469 }
600470 ctx = templ.InitializeContext(ctx)
601- templ_7745c5c3_Var28 := templ.GetChildren(ctx)
602- if templ_7745c5c3_Var28 == nil {
603- templ_7745c5c3_Var28 = templ.NopComponent
471+ templ_7745c5c3_Var22 := templ.GetChildren(ctx)
472+ if templ_7745c5c3_Var22 == nil {
473+ templ_7745c5c3_Var22 = templ.NopComponent
604474 }
605475 ctx = templ.ClearChildren(ctx)
606- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<li class=\"sv-list-row\"><article class=\"sv-favorite-item\" data-sv-favorite-row data-favorite-id=\"")
476+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<li class=\"sv-list-row\"><article class=\"sv-favorite-item\" data-sv-favorite-row data-favorite-id=\"")
607477 if templ_7745c5c3_Err != nil {
608478 return templ_7745c5c3_Err
609479 }
610- var templ_7745c5c3_Var29 string
611- templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
480+ var templ_7745c5c3_Var23 string
481+ templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
612482 if templ_7745c5c3_Err != nil {
613- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 113, Col: 106}
483+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 89, Col: 106}
614484 }
615- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)
485+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
616486 if templ_7745c5c3_Err != nil {
617487 return templ_7745c5c3_Err
618488 }
619- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\"><div class=\"sv-favorite-header\"><div><a class=\"sv-link sv-favorite-open\" href=\"")
489+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\"><div class=\"sv-favorite-header\"><div><a class=\"sv-link sv-favorite-open\" href=\"")
620490 if templ_7745c5c3_Err != nil {
621491 return templ_7745c5c3_Err
622492 }
623- var templ_7745c5c3_Var30 templ.SafeURL
624- templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(sanitizeHref(favorite.URL)))
493+ var templ_7745c5c3_Var24 templ.SafeURL
494+ templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(sanitizeHref(favorite.URL)))
625495 if templ_7745c5c3_Err != nil {
626- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 116, Col: 89}
496+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 92, Col: 89}
627497 }
628- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
498+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
629499 if templ_7745c5c3_Err != nil {
630500 return templ_7745c5c3_Err
631501 }
632- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\">")
502+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\">")
633503 if templ_7745c5c3_Err != nil {
634504 return templ_7745c5c3_Err
635505 }
636- var templ_7745c5c3_Var31 string
637- templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteItemLabel(favorite))
506+ var templ_7745c5c3_Var25 string
507+ templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteItemLabel(favorite))
638508 if templ_7745c5c3_Err != nil {
639- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 116, Col: 121}
509+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 92, Col: 121}
640510 }
641- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
511+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
642512 if templ_7745c5c3_Err != nil {
643513 return templ_7745c5c3_Err
644514 }
645- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</a><p class=\"sv-history-url\">")
515+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</a><p class=\"sv-history-url\">")
646516 if templ_7745c5c3_Err != nil {
647517 return templ_7745c5c3_Err
648518 }
649- var templ_7745c5c3_Var32 string
650- templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(favorite.URL)
519+ var templ_7745c5c3_Var26 string
520+ templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(favorite.URL)
651521 if templ_7745c5c3_Err != nil {
652- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 117, Col: 45}
522+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 93, Col: 45}
653523 }
654- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
524+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
655525 if templ_7745c5c3_Err != nil {
656526 return templ_7745c5c3_Err
657527 }
658- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</p>")
528+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</p>")
659529 if templ_7745c5c3_Err != nil {
660530 return templ_7745c5c3_Err
661531 }
662- if favoriteMetaText(favorite, folders) != "" {
663- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<p class=\"sv-meta\">")
532+ if favoriteMetaText(favorite) != "" {
533+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<p class=\"sv-meta\">")
664534 if templ_7745c5c3_Err != nil {
665535 return templ_7745c5c3_Err
666536 }
667- var templ_7745c5c3_Var33 string
668- templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteMetaText(favorite, folders))
537+ var templ_7745c5c3_Var27 string
538+ templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteMetaText(favorite))
669539 if templ_7745c5c3_Err != nil {
670- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 119, Col: 62}
540+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 95, Col: 53}
671541 }
672- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
542+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
673543 if templ_7745c5c3_Err != nil {
674544 return templ_7745c5c3_Err
675545 }
676- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</p>")
546+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</p>")
677547 if templ_7745c5c3_Err != nil {
678548 return templ_7745c5c3_Err
679549 }
680550 }
681- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</div><div class=\"sv-history-row-actions\"><a class=\"sv-button sv-button-secondary\" href=\"")
551+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</div><div class=\"sv-history-row-actions\"><a class=\"sv-button sv-button-secondary\" href=\"")
682552 if templ_7745c5c3_Err != nil {
683553 return templ_7745c5c3_Err
684554 }
685- var templ_7745c5c3_Var34 templ.SafeURL
686- templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(sanitizeHref(favorite.URL)))
555+ var templ_7745c5c3_Var28 templ.SafeURL
556+ templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(sanitizeHref(favorite.URL)))
687557 if templ_7745c5c3_Err != nil {
688- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 123, Col: 94}
558+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 99, Col: 94}
689559 }
690- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
560+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
691561 if templ_7745c5c3_Err != nil {
692562 return templ_7745c5c3_Err
693563 }
694- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "\">")
564+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "\">")
695565 if templ_7745c5c3_Err != nil {
696566 return templ_7745c5c3_Err
697567 }
@@ -699,20 +569,20 @@ func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*e
699569 if templ_7745c5c3_Err != nil {
700570 return templ_7745c5c3_Err
701571 }
702- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</a> <button type=\"button\" class=\"sv-button sv-button-secondary sv-button-danger sv-button-icon-only\" data-sv-action=\"favorite.delete\" data-id=\"")
572+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</a> <button type=\"button\" class=\"sv-button sv-button-secondary sv-button-danger sv-button-icon-only\" data-sv-action=\"favorite.delete\" data-id=\"")
703573 if templ_7745c5c3_Err != nil {
704574 return templ_7745c5c3_Err
705575 }
706- var templ_7745c5c3_Var35 string
707- templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
576+ var templ_7745c5c3_Var29 string
577+ templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
708578 if templ_7745c5c3_Err != nil {
709- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 124, Col: 175}
579+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 100, Col: 175}
710580 }
711- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
581+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)
712582 if templ_7745c5c3_Err != nil {
713583 return templ_7745c5c3_Err
714584 }
715- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "\" data-sv-confirm=\"Delete this favorite?\" aria-label=\"Delete this favorite\">")
585+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\" data-sv-confirm=\"Delete this favorite?\" aria-label=\"Delete this favorite\">")
716586 if templ_7745c5c3_Err != nil {
717587 return templ_7745c5c3_Err
718588 }
@@ -720,11 +590,11 @@ func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*e
720590 if templ_7745c5c3_Err != nil {
721591 return templ_7745c5c3_Err
722592 }
723- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</button></div></div>")
593+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</button></div></div>")
724594 if templ_7745c5c3_Err != nil {
725595 return templ_7745c5c3_Err
726596 }
727- templ_7745c5c3_Err = FavoriteEditForm(favorite, folders).Render(ctx, templ_7745c5c3_Buffer)
597+ templ_7745c5c3_Err = FavoriteEditForm(favorite).Render(ctx, templ_7745c5c3_Buffer)
728598 if templ_7745c5c3_Err != nil {
729599 return templ_7745c5c3_Err
730600 }
@@ -732,7 +602,7 @@ func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*e
732602 if templ_7745c5c3_Err != nil {
733603 return templ_7745c5c3_Err
734604 }
735- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</article></li>")
605+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</article></li>")
736606 if templ_7745c5c3_Err != nil {
737607 return templ_7745c5c3_Err
738608 }
@@ -740,7 +610,7 @@ func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*e
740610 })
741611 }
742612
743-func FavoriteEditForm(favorite *entity.Favorite, folders []*entity.Folder) templ.Component {
613+func FavoriteEditForm(favorite *entity.Favorite) templ.Component {
744614 return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
745615 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
746616 if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -756,55 +626,51 @@ func FavoriteEditForm(favorite *entity.Favorite, folders []*entity.Folder) templ
756626 }()
757627 }
758628 ctx = templ.InitializeContext(ctx)
759- templ_7745c5c3_Var36 := templ.GetChildren(ctx)
760- if templ_7745c5c3_Var36 == nil {
761- templ_7745c5c3_Var36 = templ.NopComponent
629+ templ_7745c5c3_Var30 := templ.GetChildren(ctx)
630+ if templ_7745c5c3_Var30 == nil {
631+ templ_7745c5c3_Var30 = templ.NopComponent
762632 }
763633 ctx = templ.ClearChildren(ctx)
764- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<form class=\"sv-inline-form\" data-sv-action=\"favorite.update\"><input type=\"hidden\" name=\"id\" value=\"")
634+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<form class=\"sv-inline-form\" data-sv-action=\"favorite.update\"><input type=\"hidden\" name=\"id\" value=\"")
765635 if templ_7745c5c3_Err != nil {
766636 return templ_7745c5c3_Err
767637 }
768- var templ_7745c5c3_Var37 string
769- templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
638+ var templ_7745c5c3_Var31 string
639+ templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
770640 if templ_7745c5c3_Err != nil {
771- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 135, Col: 71}
641+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 111, Col: 71}
772642 }
773- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37)
643+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var31)
774644 if templ_7745c5c3_Err != nil {
775645 return templ_7745c5c3_Err
776646 }
777- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\"> <label><span>Title</span><input name=\"title\" type=\"text\" value=\"")
647+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\"> <label><span>Title</span><input name=\"title\" type=\"text\" value=\"")
778648 if templ_7745c5c3_Err != nil {
779649 return templ_7745c5c3_Err
780650 }
781- var templ_7745c5c3_Var38 string
782- templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(favorite.Title)
651+ var templ_7745c5c3_Var32 string
652+ templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.ResolveAttributeValue(favorite.Title)
783653 if templ_7745c5c3_Err != nil {
784- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 136, Col: 81}
654+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 112, Col: 81}
785655 }
786- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38)
656+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var32)
787657 if templ_7745c5c3_Err != nil {
788658 return templ_7745c5c3_Err
789659 }
790- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\"></label> <label><span>Favicon</span><input name=\"favicon_url\" type=\"url\" value=\"")
660+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\"></label> <label><span>Favicon</span><input name=\"favicon_url\" type=\"url\" value=\"")
791661 if templ_7745c5c3_Err != nil {
792662 return templ_7745c5c3_Err
793663 }
794- var templ_7745c5c3_Var39 string
795- templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.ResolveAttributeValue(favorite.FaviconURL)
664+ var templ_7745c5c3_Var33 string
665+ templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.ResolveAttributeValue(favorite.FaviconURL)
796666 if templ_7745c5c3_Err != nil {
797- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 137, Col: 93}
667+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 113, Col: 93}
798668 }
799- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var39)
669+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33)
800670 if templ_7745c5c3_Err != nil {
801671 return templ_7745c5c3_Err
802672 }
803- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\"></label>")
804- if templ_7745c5c3_Err != nil {
805- return templ_7745c5c3_Err
806- }
807- templ_7745c5c3_Err = FolderSelect("folder_id", folders, favorite.FolderID, "Folder").Render(ctx, templ_7745c5c3_Buffer)
673+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\"></label>")
808674 if templ_7745c5c3_Err != nil {
809675 return templ_7745c5c3_Err
810676 }
@@ -812,7 +678,7 @@ func FavoriteEditForm(favorite *entity.Favorite, folders []*entity.Folder) templ
812678 if templ_7745c5c3_Err != nil {
813679 return templ_7745c5c3_Err
814680 }
815- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<button class=\"sv-button sv-button-secondary\" type=\"submit\">")
681+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<button class=\"sv-button sv-button-secondary\" type=\"submit\">")
816682 if templ_7745c5c3_Err != nil {
817683 return templ_7745c5c3_Err
818684 }
@@ -820,7 +686,7 @@ func FavoriteEditForm(favorite *entity.Favorite, folders []*entity.Folder) templ
820686 if templ_7745c5c3_Err != nil {
821687 return templ_7745c5c3_Err
822688 }
823- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</button></form>")
689+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</button></form>")
824690 if templ_7745c5c3_Err != nil {
825691 return templ_7745c5c3_Err
826692 }
@@ -844,108 +710,108 @@ func FavoriteTagControls(favorite *entity.Favorite, tags []*entity.Tag) templ.Co
844710 }()
845711 }
846712 ctx = templ.InitializeContext(ctx)
847- templ_7745c5c3_Var40 := templ.GetChildren(ctx)
848- if templ_7745c5c3_Var40 == nil {
849- templ_7745c5c3_Var40 = templ.NopComponent
713+ templ_7745c5c3_Var34 := templ.GetChildren(ctx)
714+ if templ_7745c5c3_Var34 == nil {
715+ templ_7745c5c3_Var34 = templ.NopComponent
850716 }
851717 ctx = templ.ClearChildren(ctx)
852718 if len(tags) > 0 {
853- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<div class=\"sv-tag-controls\"><span class=\"sv-meta\">Tags</span> ")
719+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<div class=\"sv-tag-controls\"><span class=\"sv-meta\">Tags</span> ")
854720 if templ_7745c5c3_Err != nil {
855721 return templ_7745c5c3_Err
856722 }
857723 for _, tag := range tags {
858724 if tag != nil {
859- var templ_7745c5c3_Var41 = []any{favoriteTagButtonClass(favorite, tag)}
860- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var41...)
725+ var templ_7745c5c3_Var35 = []any{favoriteTagButtonClass(favorite, tag)}
726+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var35...)
861727 if templ_7745c5c3_Err != nil {
862728 return templ_7745c5c3_Err
863729 }
864- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "<button type=\"button\" class=\"")
730+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<button type=\"button\" class=\"")
865731 if templ_7745c5c3_Err != nil {
866732 return templ_7745c5c3_Err
867733 }
868- var templ_7745c5c3_Var42 string
869- templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var41).String())
734+ var templ_7745c5c3_Var36 string
735+ templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var35).String())
870736 if templ_7745c5c3_Err != nil {
871737 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}
872738 }
873- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var42)
739+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var36)
874740 if templ_7745c5c3_Err != nil {
875741 return templ_7745c5c3_Err
876742 }
877- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\" data-sv-action=\"")
743+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "\" data-sv-action=\"")
878744 if templ_7745c5c3_Err != nil {
879745 return templ_7745c5c3_Err
880746 }
881- var templ_7745c5c3_Var43 string
882- templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.ResolveAttributeValue(favoriteTagButtonAction(favorite, tag))
747+ var templ_7745c5c3_Var37 string
748+ templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(favoriteTagButtonAction(favorite, tag))
883749 if templ_7745c5c3_Err != nil {
884- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 150, Col: 130}
750+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 125, Col: 130}
885751 }
886- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var43)
752+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37)
887753 if templ_7745c5c3_Err != nil {
888754 return templ_7745c5c3_Err
889755 }
890- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\" data-favorite-id=\"")
756+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\" data-favorite-id=\"")
891757 if templ_7745c5c3_Err != nil {
892758 return templ_7745c5c3_Err
893759 }
894- var templ_7745c5c3_Var44 string
895- templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
760+ var templ_7745c5c3_Var38 string
761+ templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
896762 if templ_7745c5c3_Err != nil {
897- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 150, Col: 182}
763+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 125, Col: 182}
898764 }
899- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var44)
765+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38)
900766 if templ_7745c5c3_Err != nil {
901767 return templ_7745c5c3_Err
902768 }
903- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" data-tag-id=\"")
769+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\" data-tag-id=\"")
904770 if templ_7745c5c3_Err != nil {
905771 return templ_7745c5c3_Err
906772 }
907- var templ_7745c5c3_Var45 string
908- templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
773+ var templ_7745c5c3_Var39 string
774+ templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
909775 if templ_7745c5c3_Err != nil {
910- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 150, Col: 224}
776+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 125, Col: 224}
911777 }
912- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var45)
778+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var39)
913779 if templ_7745c5c3_Err != nil {
914780 return templ_7745c5c3_Err
915781 }
916- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "\"><span class=\"sv-tag-dot\" style=\"")
782+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\"><span class=\"sv-tag-dot\" style=\"")
917783 if templ_7745c5c3_Err != nil {
918784 return templ_7745c5c3_Err
919785 }
920- var templ_7745c5c3_Var46 string
921- templ_7745c5c3_Var46, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
786+ var templ_7745c5c3_Var40 string
787+ templ_7745c5c3_Var40, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
922788 if templ_7745c5c3_Err != nil {
923- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 151, Col: 78}
789+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 126, Col: 78}
924790 }
925- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
791+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
926792 if templ_7745c5c3_Err != nil {
927793 return templ_7745c5c3_Err
928794 }
929- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "\"></span>")
795+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\"></span>")
930796 if templ_7745c5c3_Err != nil {
931797 return templ_7745c5c3_Err
932798 }
933- var templ_7745c5c3_Var47 string
934- templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteTagButtonLabel(favorite, tag))
799+ var templ_7745c5c3_Var41 string
800+ templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteTagButtonLabel(favorite, tag))
935801 if templ_7745c5c3_Err != nil {
936- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 151, Col: 127}
802+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 126, Col: 127}
937803 }
938- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
804+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
939805 if templ_7745c5c3_Err != nil {
940806 return templ_7745c5c3_Err
941807 }
942- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "</button>")
808+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "</button>")
943809 if templ_7745c5c3_Err != nil {
944810 return templ_7745c5c3_Err
945811 }
946812 }
947813 }
948- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "</div>")
814+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</div>")
949815 if templ_7745c5c3_Err != nil {
950816 return templ_7745c5c3_Err
951817 }
@@ -954,140 +820,6 @@ func FavoriteTagControls(favorite *entity.Favorite, tags []*entity.Tag) templ.Co
954820 })
955821 }
956822
957-func FolderCreateForm() templ.Component {
958- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
959- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
960- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
961- return templ_7745c5c3_CtxErr
962- }
963- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
964- if !templ_7745c5c3_IsBuffer {
965- defer func() {
966- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
967- if templ_7745c5c3_Err == nil {
968- templ_7745c5c3_Err = templ_7745c5c3_BufErr
969- }
970- }()
971- }
972- ctx = templ.InitializeContext(ctx)
973- templ_7745c5c3_Var48 := templ.GetChildren(ctx)
974- if templ_7745c5c3_Var48 == nil {
975- templ_7745c5c3_Var48 = templ.NopComponent
976- }
977- ctx = templ.ClearChildren(ctx)
978- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<form class=\"sv-inline-form\" data-sv-action=\"folder.create\"><label><span>Name</span><input name=\"name\" type=\"text\" required></label> <label><span>Icon</span><input name=\"icon\" type=\"text\" placeholder=\"Optional\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")
979- if templ_7745c5c3_Err != nil {
980- return templ_7745c5c3_Err
981- }
982- templ_7745c5c3_Err = ButtonIconLabel(iconFolder, "Create folder").Render(ctx, templ_7745c5c3_Buffer)
983- if templ_7745c5c3_Err != nil {
984- return templ_7745c5c3_Err
985- }
986- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "</button></form>")
987- if templ_7745c5c3_Err != nil {
988- return templ_7745c5c3_Err
989- }
990- return nil
991- })
992-}
993-
994-func FolderItem(folder *entity.Folder) templ.Component {
995- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
996- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
997- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
998- return templ_7745c5c3_CtxErr
999- }
1000- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
1001- if !templ_7745c5c3_IsBuffer {
1002- defer func() {
1003- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
1004- if templ_7745c5c3_Err == nil {
1005- templ_7745c5c3_Err = templ_7745c5c3_BufErr
1006- }
1007- }()
1008- }
1009- ctx = templ.InitializeContext(ctx)
1010- templ_7745c5c3_Var49 := templ.GetChildren(ctx)
1011- if templ_7745c5c3_Var49 == nil {
1012- templ_7745c5c3_Var49 = templ.NopComponent
1013- }
1014- ctx = templ.ClearChildren(ctx)
1015- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "<li class=\"sv-list-row\"><form class=\"sv-inline-form\" data-sv-action=\"folder.update\"><input type=\"hidden\" name=\"id\" value=\"")
1016- if templ_7745c5c3_Err != nil {
1017- return templ_7745c5c3_Err
1018- }
1019- var templ_7745c5c3_Var50 string
1020- templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", folder.ID))
1021- if templ_7745c5c3_Err != nil {
1022- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 170, Col: 70}
1023- }
1024- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)
1025- if templ_7745c5c3_Err != nil {
1026- return templ_7745c5c3_Err
1027- }
1028- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "\"> <label><span>Name</span><input name=\"name\" type=\"text\" value=\"")
1029- if templ_7745c5c3_Err != nil {
1030- return templ_7745c5c3_Err
1031- }
1032- var templ_7745c5c3_Var51 string
1033- templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.ResolveAttributeValue(folder.Name)
1034- if templ_7745c5c3_Err != nil {
1035- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 171, Col: 77}
1036- }
1037- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var51)
1038- if templ_7745c5c3_Err != nil {
1039- return templ_7745c5c3_Err
1040- }
1041- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "\" required></label> <label><span>Icon</span><input name=\"icon\" type=\"text\" value=\"")
1042- if templ_7745c5c3_Err != nil {
1043- return templ_7745c5c3_Err
1044- }
1045- var templ_7745c5c3_Var52 string
1046- templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.ResolveAttributeValue(folder.Icon)
1047- if templ_7745c5c3_Err != nil {
1048- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 172, Col: 77}
1049- }
1050- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var52)
1051- if templ_7745c5c3_Err != nil {
1052- return templ_7745c5c3_Err
1053- }
1054- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")
1055- if templ_7745c5c3_Err != nil {
1056- return templ_7745c5c3_Err
1057- }
1058- templ_7745c5c3_Err = ButtonIconLabel(iconSave, "Save").Render(ctx, templ_7745c5c3_Buffer)
1059- if templ_7745c5c3_Err != nil {
1060- return templ_7745c5c3_Err
1061- }
1062- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</button> <button type=\"button\" class=\"sv-button sv-button-secondary sv-button-danger sv-button-icon-only\" data-sv-action=\"folder.delete\" data-id=\"")
1063- if templ_7745c5c3_Err != nil {
1064- return templ_7745c5c3_Err
1065- }
1066- var templ_7745c5c3_Var53 string
1067- templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", folder.ID))
1068- if templ_7745c5c3_Err != nil {
1069- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 174, Col: 169}
1070- }
1071- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var53)
1072- if templ_7745c5c3_Err != nil {
1073- return templ_7745c5c3_Err
1074- }
1075- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\" data-sv-confirm=\"Delete this folder? Favorites will move out of it.\" aria-label=\"Delete this folder\">")
1076- if templ_7745c5c3_Err != nil {
1077- return templ_7745c5c3_Err
1078- }
1079- templ_7745c5c3_Err = IconOnly(iconTrash, "Delete this folder").Render(ctx, templ_7745c5c3_Buffer)
1080- if templ_7745c5c3_Err != nil {
1081- return templ_7745c5c3_Err
1082- }
1083- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "</button></form></li>")
1084- if templ_7745c5c3_Err != nil {
1085- return templ_7745c5c3_Err
1086- }
1087- return nil
1088- })
1089-}
1090-
1091823 func TagCreateForm() templ.Component {
1092824 return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
1093825 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
@@ -1104,12 +836,12 @@ func TagCreateForm() templ.Component {
1104836 }()
1105837 }
1106838 ctx = templ.InitializeContext(ctx)
1107- templ_7745c5c3_Var54 := templ.GetChildren(ctx)
1108- if templ_7745c5c3_Var54 == nil {
1109- templ_7745c5c3_Var54 = templ.NopComponent
839+ templ_7745c5c3_Var42 := templ.GetChildren(ctx)
840+ if templ_7745c5c3_Var42 == nil {
841+ templ_7745c5c3_Var42 = templ.NopComponent
1110842 }
1111843 ctx = templ.ClearChildren(ctx)
1112- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "<form class=\"sv-inline-form\" data-sv-action=\"tag.create\"><label><span>Name</span><input name=\"name\" type=\"text\" required></label> <label><span>Color</span><input name=\"color\" type=\"text\" placeholder=\"#808080\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")
844+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "<form class=\"sv-inline-form\" data-sv-action=\"tag.create\"><label><span>Name</span><input name=\"name\" type=\"text\" required></label> <label><span>Color</span><input name=\"color\" type=\"text\" placeholder=\"#808080\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")
1113845 if templ_7745c5c3_Err != nil {
1114846 return templ_7745c5c3_Err
1115847 }
@@ -1117,7 +849,7 @@ func TagCreateForm() templ.Component {
1117849 if templ_7745c5c3_Err != nil {
1118850 return templ_7745c5c3_Err
1119851 }
1120- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "</button></form>")
852+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</button></form>")
1121853 if templ_7745c5c3_Err != nil {
1122854 return templ_7745c5c3_Err
1123855 }
@@ -1141,64 +873,64 @@ func TagItem(tag *entity.Tag) templ.Component {
1141873 }()
1142874 }
1143875 ctx = templ.InitializeContext(ctx)
1144- templ_7745c5c3_Var55 := templ.GetChildren(ctx)
1145- if templ_7745c5c3_Var55 == nil {
1146- templ_7745c5c3_Var55 = templ.NopComponent
876+ templ_7745c5c3_Var43 := templ.GetChildren(ctx)
877+ if templ_7745c5c3_Var43 == nil {
878+ templ_7745c5c3_Var43 = templ.NopComponent
1147879 }
1148880 ctx = templ.ClearChildren(ctx)
1149- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<li class=\"sv-list-row\"><form class=\"sv-inline-form\" data-sv-action=\"tag.update\"><input type=\"hidden\" name=\"id\" value=\"")
881+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "<li class=\"sv-list-row\"><form class=\"sv-inline-form\" data-sv-action=\"tag.update\"><input type=\"hidden\" name=\"id\" value=\"")
1150882 if templ_7745c5c3_Err != nil {
1151883 return templ_7745c5c3_Err
1152884 }
1153- var templ_7745c5c3_Var56 string
1154- templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
885+ var templ_7745c5c3_Var44 string
886+ templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
1155887 if templ_7745c5c3_Err != nil {
1156- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 190, Col: 67}
888+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 145, Col: 67}
1157889 }
1158- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var56)
890+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var44)
1159891 if templ_7745c5c3_Err != nil {
1160892 return templ_7745c5c3_Err
1161893 }
1162- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\"> <span class=\"sv-tag-dot\" style=\"")
894+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\"> <span class=\"sv-tag-dot\" style=\"")
1163895 if templ_7745c5c3_Err != nil {
1164896 return templ_7745c5c3_Err
1165897 }
1166- var templ_7745c5c3_Var57 string
1167- templ_7745c5c3_Var57, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
898+ var templ_7745c5c3_Var45 string
899+ templ_7745c5c3_Var45, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
1168900 if templ_7745c5c3_Err != nil {
1169- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 191, Col: 75}
901+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 146, Col: 75}
1170902 }
1171- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57))
903+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
1172904 if templ_7745c5c3_Err != nil {
1173905 return templ_7745c5c3_Err
1174906 }
1175- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "\"></span> <label><span>Name</span><input name=\"name\" type=\"text\" value=\"")
907+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\"></span> <label><span>Name</span><input name=\"name\" type=\"text\" value=\"")
1176908 if templ_7745c5c3_Err != nil {
1177909 return templ_7745c5c3_Err
1178910 }
1179- var templ_7745c5c3_Var58 string
1180- templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.ResolveAttributeValue(tag.Name)
911+ var templ_7745c5c3_Var46 string
912+ templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.ResolveAttributeValue(tag.Name)
1181913 if templ_7745c5c3_Err != nil {
1182- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 192, Col: 74}
914+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 147, Col: 74}
1183915 }
1184- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var58)
916+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var46)
1185917 if templ_7745c5c3_Err != nil {
1186918 return templ_7745c5c3_Err
1187919 }
1188- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "\" required></label> <label><span>Color</span><input name=\"color\" type=\"text\" value=\"")
920+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" required></label> <label><span>Color</span><input name=\"color\" type=\"text\" value=\"")
1189921 if templ_7745c5c3_Err != nil {
1190922 return templ_7745c5c3_Err
1191923 }
1192- var templ_7745c5c3_Var59 string
1193- templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.ResolveAttributeValue(tag.Color)
924+ var templ_7745c5c3_Var47 string
925+ templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.ResolveAttributeValue(tag.Color)
1194926 if templ_7745c5c3_Err != nil {
1195- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 193, Col: 77}
927+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 148, Col: 77}
1196928 }
1197- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var59)
929+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var47)
1198930 if templ_7745c5c3_Err != nil {
1199931 return templ_7745c5c3_Err
1200932 }
1201- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")
933+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")
1202934 if templ_7745c5c3_Err != nil {
1203935 return templ_7745c5c3_Err
1204936 }
@@ -1206,20 +938,20 @@ func TagItem(tag *entity.Tag) templ.Component {
1206938 if templ_7745c5c3_Err != nil {
1207939 return templ_7745c5c3_Err
1208940 }
1209- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</button> <button type=\"button\" class=\"sv-button sv-button-secondary sv-button-danger sv-button-icon-only\" data-sv-action=\"tag.delete\" data-id=\"")
941+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</button> <button type=\"button\" class=\"sv-button sv-button-secondary sv-button-danger sv-button-icon-only\" data-sv-action=\"tag.delete\" data-id=\"")
1210942 if templ_7745c5c3_Err != nil {
1211943 return templ_7745c5c3_Err
1212944 }
1213- var templ_7745c5c3_Var60 string
1214- templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
945+ var templ_7745c5c3_Var48 string
946+ templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
1215947 if templ_7745c5c3_Err != nil {
1216- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 195, Col: 163}
948+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 150, Col: 163}
1217949 }
1218- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var60)
950+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var48)
1219951 if templ_7745c5c3_Err != nil {
1220952 return templ_7745c5c3_Err
1221953 }
1222- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "\" data-sv-confirm=\"Delete this tag?\" aria-label=\"Delete this tag\">")
954+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "\" data-sv-confirm=\"Delete this tag?\" aria-label=\"Delete this tag\">")
1223955 if templ_7745c5c3_Err != nil {
1224956 return templ_7745c5c3_Err
1225957 }
@@ -1227,110 +959,7 @@ func TagItem(tag *entity.Tag) templ.Component {
1227959 if templ_7745c5c3_Err != nil {
1228960 return templ_7745c5c3_Err
1229961 }
1230- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "</button></form></li>")
1231- if templ_7745c5c3_Err != nil {
1232- return templ_7745c5c3_Err
1233- }
1234- return nil
1235- })
1236-}
1237-
1238-func FolderSelect(name string, folders []*entity.Folder, selected *entity.FolderID, label string) templ.Component {
1239- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
1240- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
1241- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
1242- return templ_7745c5c3_CtxErr
1243- }
1244- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
1245- if !templ_7745c5c3_IsBuffer {
1246- defer func() {
1247- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
1248- if templ_7745c5c3_Err == nil {
1249- templ_7745c5c3_Err = templ_7745c5c3_BufErr
1250- }
1251- }()
1252- }
1253- ctx = templ.InitializeContext(ctx)
1254- templ_7745c5c3_Var61 := templ.GetChildren(ctx)
1255- if templ_7745c5c3_Var61 == nil {
1256- templ_7745c5c3_Var61 = templ.NopComponent
1257- }
1258- ctx = templ.ClearChildren(ctx)
1259- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "<label><span>")
1260- if templ_7745c5c3_Err != nil {
1261- return templ_7745c5c3_Err
1262- }
1263- var templ_7745c5c3_Var62 string
1264- templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.JoinStringErrs(label)
1265- if templ_7745c5c3_Err != nil {
1266- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 201, Col: 21}
1267- }
1268- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var62))
1269- if templ_7745c5c3_Err != nil {
1270- return templ_7745c5c3_Err
1271- }
1272- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "</span><select name=\"")
1273- if templ_7745c5c3_Err != nil {
1274- return templ_7745c5c3_Err
1275- }
1276- var templ_7745c5c3_Var63 string
1277- templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.ResolveAttributeValue(name)
1278- if templ_7745c5c3_Err != nil {
1279- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 201, Col: 49}
1280- }
1281- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var63)
1282- if templ_7745c5c3_Err != nil {
1283- return templ_7745c5c3_Err
1284- }
1285- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "\"><option value=\"\">Unfiled</option> ")
1286- if templ_7745c5c3_Err != nil {
1287- return templ_7745c5c3_Err
1288- }
1289- for _, folder := range folders {
1290- if folder != nil {
1291- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<option value=\"")
1292- if templ_7745c5c3_Err != nil {
1293- return templ_7745c5c3_Err
1294- }
1295- var templ_7745c5c3_Var64 string
1296- templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", folder.ID))
1297- if templ_7745c5c3_Err != nil {
1298- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 205, Col: 48}
1299- }
1300- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var64)
1301- if templ_7745c5c3_Err != nil {
1302- return templ_7745c5c3_Err
1303- }
1304- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "\"")
1305- if templ_7745c5c3_Err != nil {
1306- return templ_7745c5c3_Err
1307- }
1308- if folderSelected(selected, folder.ID) {
1309- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, " selected")
1310- if templ_7745c5c3_Err != nil {
1311- return templ_7745c5c3_Err
1312- }
1313- }
1314- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, ">")
1315- if templ_7745c5c3_Err != nil {
1316- return templ_7745c5c3_Err
1317- }
1318- var templ_7745c5c3_Var65 string
1319- templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(folderDisplayName(folder))
1320- if templ_7745c5c3_Err != nil {
1321- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 205, Col: 128}
1322- }
1323- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65))
1324- if templ_7745c5c3_Err != nil {
1325- return templ_7745c5c3_Err
1326- }
1327- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "</option>")
1328- if templ_7745c5c3_Err != nil {
1329- return templ_7745c5c3_Err
1330- }
1331- }
1332- }
1333- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "</select></label>")
962+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "</button></form></li>")
1334963 if templ_7745c5c3_Err != nil {
1335964 return templ_7745c5c3_Err
1336965 }
@@ -1354,58 +983,58 @@ func ShortcutSelect(selected *int) templ.Component {
1354983 }()
1355984 }
1356985 ctx = templ.InitializeContext(ctx)
1357- templ_7745c5c3_Var66 := templ.GetChildren(ctx)
1358- if templ_7745c5c3_Var66 == nil {
1359- templ_7745c5c3_Var66 = templ.NopComponent
986+ templ_7745c5c3_Var49 := templ.GetChildren(ctx)
987+ if templ_7745c5c3_Var49 == nil {
988+ templ_7745c5c3_Var49 = templ.NopComponent
1360989 }
1361990 ctx = templ.ClearChildren(ctx)
1362- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "<label><span>Shortcut</span><select name=\"shortcut_key\"><option value=\"\">None</option> ")
991+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<label><span>Shortcut</span><select name=\"shortcut_key\"><option value=\"\">None</option> ")
1363992 if templ_7745c5c3_Err != nil {
1364993 return templ_7745c5c3_Err
1365994 }
1366995 for i := 1; i <= 9; i++ {
1367- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "<option value=\"")
996+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<option value=\"")
1368997 if templ_7745c5c3_Err != nil {
1369998 return templ_7745c5c3_Err
1370999 }
1371- var templ_7745c5c3_Var67 string
1372- templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", i))
1000+ var templ_7745c5c3_Var50 string
1001+ templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", i))
13731002 if templ_7745c5c3_Err != nil {
1374- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 215, Col: 39}
1003+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 159, Col: 39}
13751004 }
1376- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var67)
1005+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)
13771006 if templ_7745c5c3_Err != nil {
13781007 return templ_7745c5c3_Err
13791008 }
1380- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "\"")
1009+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\"")
13811010 if templ_7745c5c3_Err != nil {
13821011 return templ_7745c5c3_Err
13831012 }
13841013 if shortcutSelected(selected, i) {
1385- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, " selected")
1014+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, " selected")
13861015 if templ_7745c5c3_Err != nil {
13871016 return templ_7745c5c3_Err
13881017 }
13891018 }
1390- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, ">")
1019+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, ">")
13911020 if templ_7745c5c3_Err != nil {
13921021 return templ_7745c5c3_Err
13931022 }
1394- var templ_7745c5c3_Var68 string
1395- templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", i))
1023+ var templ_7745c5c3_Var51 string
1024+ templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", i))
13961025 if templ_7745c5c3_Err != nil {
1397- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 215, Col: 108}
1026+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 159, Col: 108}
13981027 }
1399- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68))
1028+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
14001029 if templ_7745c5c3_Err != nil {
14011030 return templ_7745c5c3_Err
14021031 }
1403- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, "</option>")
1032+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "</option>")
14041033 if templ_7745c5c3_Err != nil {
14051034 return templ_7745c5c3_Err
14061035 }
14071036 }
1408- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "</select></label>")
1037+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</select></label>")
14091038 if templ_7745c5c3_Err != nil {
14101039 return templ_7745c5c3_Err
14111040 }
@@ -95,7 +95,7 @@ func FavoritesView(data favoritesRenderData) templ.Component {
95 }95 }
96 for _, favorite := range data.Favorites {96 for _, favorite := range data.Favorites {
97 if favorite != nil {97 if favorite != nil {
98- templ_7745c5c3_Err = FavoriteItem(favorite, data.Folders, data.Tags).Render(ctx, templ_7745c5c3_Buffer)98+ templ_7745c5c3_Err = FavoriteItem(favorite, data.Tags).Render(ctx, templ_7745c5c3_Buffer)
99 if templ_7745c5c3_Err != nil {99 if templ_7745c5c3_Err != nil {
100 return templ_7745c5c3_Err100 return templ_7745c5c3_Err
101 }101 }
@@ -113,63 +113,6 @@ func FavoritesView(data favoritesRenderData) templ.Component {
113 return templ_7745c5c3_Err113 return templ_7745c5c3_Err
114 }114 }
115 templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {115 templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
116- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
117- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
118- if !templ_7745c5c3_IsBuffer {
119- defer func() {
120- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
121- if templ_7745c5c3_Err == nil {
122- templ_7745c5c3_Err = templ_7745c5c3_BufErr
123- }
124- }()
125- }
126- ctx = templ.InitializeContext(ctx)
127- templ_7745c5c3_Err = Meta(foldersSummary(data)).Render(ctx, templ_7745c5c3_Buffer)
128- if templ_7745c5c3_Err != nil {
129- return templ_7745c5c3_Err
130- }
131- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
132- if templ_7745c5c3_Err != nil {
133- return templ_7745c5c3_Err
134- }
135- templ_7745c5c3_Err = FolderCreateForm().Render(ctx, templ_7745c5c3_Buffer)
136- if templ_7745c5c3_Err != nil {
137- return templ_7745c5c3_Err
138- }
139- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " ")
140- if templ_7745c5c3_Err != nil {
141- return templ_7745c5c3_Err
142- }
143- if len(data.Folders) == 0 {
144- templ_7745c5c3_Err = EmptyState("No folders").Render(ctx, templ_7745c5c3_Buffer)
145- if templ_7745c5c3_Err != nil {
146- return templ_7745c5c3_Err
147- }
148- } else {
149- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<ul class=\"sv-list\">")
150- if templ_7745c5c3_Err != nil {
151- return templ_7745c5c3_Err
152- }
153- for _, folder := range data.Folders {
154- if folder != nil {
155- templ_7745c5c3_Err = FolderItem(folder).Render(ctx, templ_7745c5c3_Buffer)
156- if templ_7745c5c3_Err != nil {
157- return templ_7745c5c3_Err
158- }
159- }
160- }
161- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</ul>")
162- if templ_7745c5c3_Err != nil {
163- return templ_7745c5c3_Err
164- }
165- }
166- return nil
167- })
168- templ_7745c5c3_Err = Section("sv-folders-list", "Folders").Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
169- if templ_7745c5c3_Err != nil {
170- return templ_7745c5c3_Err
171- }
172- templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
173 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context116 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
174 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)117 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
175 if !templ_7745c5c3_IsBuffer {118 if !templ_7745c5c3_IsBuffer {
@@ -185,7 +128,7 @@ func FavoritesView(data favoritesRenderData) templ.Component {
185 if templ_7745c5c3_Err != nil {128 if templ_7745c5c3_Err != nil {
186 return templ_7745c5c3_Err129 return templ_7745c5c3_Err
187 }130 }
188- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, " ")131+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
189 if templ_7745c5c3_Err != nil {132 if templ_7745c5c3_Err != nil {
190 return templ_7745c5c3_Err133 return templ_7745c5c3_Err
191 }134 }
@@ -193,7 +136,7 @@ func FavoritesView(data favoritesRenderData) templ.Component {
193 if templ_7745c5c3_Err != nil {136 if templ_7745c5c3_Err != nil {
194 return templ_7745c5c3_Err137 return templ_7745c5c3_Err
195 }138 }
196- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " ")139+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " ")
197 if templ_7745c5c3_Err != nil {140 if templ_7745c5c3_Err != nil {
198 return templ_7745c5c3_Err141 return templ_7745c5c3_Err
199 }142 }
@@ -203,7 +146,7 @@ func FavoritesView(data favoritesRenderData) templ.Component {
203 return templ_7745c5c3_Err146 return templ_7745c5c3_Err
204 }147 }
205 } else {148 } else {
206- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<ul class=\"sv-list\">")149+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<ul class=\"sv-list\">")
207 if templ_7745c5c3_Err != nil {150 if templ_7745c5c3_Err != nil {
208 return templ_7745c5c3_Err151 return templ_7745c5c3_Err
209 }152 }
@@ -215,14 +158,14 @@ func FavoritesView(data favoritesRenderData) templ.Component {
215 }158 }
216 }159 }
217 }160 }
218- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</ul>")161+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</ul>")
219 if templ_7745c5c3_Err != nil {162 if templ_7745c5c3_Err != nil {
220 return templ_7745c5c3_Err163 return templ_7745c5c3_Err
221 }164 }
222 }165 }
223 return nil166 return nil
224 })167 })
225- templ_7745c5c3_Err = Section("sv-tags-list", "Tags").Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)168+ templ_7745c5c3_Err = Section("sv-tags-list", "Tags").Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
226 if templ_7745c5c3_Err != nil {169 if templ_7745c5c3_Err != nil {
227 return templ_7745c5c3_Err170 return templ_7745c5c3_Err
228 }171 }
@@ -246,16 +189,12 @@ func FavoritesControls(data favoritesRenderData) templ.Component {
246 }()189 }()
247 }190 }
248 ctx = templ.InitializeContext(ctx)191 ctx = templ.InitializeContext(ctx)
249- templ_7745c5c3_Var6 := templ.GetChildren(ctx)192+ templ_7745c5c3_Var5 := templ.GetChildren(ctx)
250- if templ_7745c5c3_Var6 == nil {193+ if templ_7745c5c3_Var5 == nil {
251- templ_7745c5c3_Var6 = templ.NopComponent194+ templ_7745c5c3_Var5 = templ.NopComponent
252 }195 }
253 ctx = templ.ClearChildren(ctx)196 ctx = templ.ClearChildren(ctx)
254- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<form class=\"sv-card-form\" data-sv-action=\"favorite.create\"><label><span>URL</span><input name=\"url\" type=\"url\" placeholder=\"https://example.com\" required></label> <label><span>Title</span><input name=\"title\" type=\"text\" placeholder=\"Optional title\"></label>")197+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<form class=\"sv-card-form\" data-sv-action=\"favorite.create\"><label><span>URL</span><input name=\"url\" type=\"url\" placeholder=\"https://example.com\" required></label> <label><span>Title</span><input name=\"title\" type=\"text\" placeholder=\"Optional title\"></label>")
255- if templ_7745c5c3_Err != nil {
256- return templ_7745c5c3_Err
257- }
258- templ_7745c5c3_Err = FolderSelect("folder_id", data.Folders, nil, "Folder").Render(ctx, templ_7745c5c3_Buffer)
259 if templ_7745c5c3_Err != nil {198 if templ_7745c5c3_Err != nil {
260 return templ_7745c5c3_Err199 return templ_7745c5c3_Err
261 }200 }
@@ -263,7 +202,7 @@ func FavoritesControls(data favoritesRenderData) templ.Component {
263 if templ_7745c5c3_Err != nil {202 if templ_7745c5c3_Err != nil {
264 return templ_7745c5c3_Err203 return templ_7745c5c3_Err
265 }204 }
266- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<button class=\"sv-button\" type=\"submit\">")205+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<button class=\"sv-button\" type=\"submit\">")
267 if templ_7745c5c3_Err != nil {206 if templ_7745c5c3_Err != nil {
268 return templ_7745c5c3_Err207 return templ_7745c5c3_Err
269 }208 }
@@ -271,228 +210,159 @@ func FavoritesControls(data favoritesRenderData) templ.Component {
271 if templ_7745c5c3_Err != nil {210 if templ_7745c5c3_Err != nil {
272 return templ_7745c5c3_Err211 return templ_7745c5c3_Err
273 }212 }
274- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</button></form><div class=\"sv-filter-panel\"><div class=\"sv-button-row\"><span class=\"sv-meta\">Folders</span> ")213+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</button></form><div class=\"sv-filter-panel\"><div class=\"sv-button-row\"><span class=\"sv-meta\">Tags</span> ")
275 if templ_7745c5c3_Err != nil {214 if templ_7745c5c3_Err != nil {
276 return templ_7745c5c3_Err215 return templ_7745c5c3_Err
277 }216 }
278- var templ_7745c5c3_Var7 = []any{favoriteFilterClass(favoriteAllFilterActive(data))}217+ var templ_7745c5c3_Var6 = []any{favoriteFilterClass(favoriteAllFilterActive(data))}
279- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var7...)218+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...)
280 if templ_7745c5c3_Err != nil {219 if templ_7745c5c3_Err != nil {
281 return templ_7745c5c3_Err220 return templ_7745c5c3_Err
282 }221 }
283- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<button type=\"button\" class=\"")222+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<button type=\"button\" class=\"")
284 if templ_7745c5c3_Err != nil {223 if templ_7745c5c3_Err != nil {
285 return templ_7745c5c3_Err224 return templ_7745c5c3_Err
286 }225 }
287- var templ_7745c5c3_Var8 string226+ var templ_7745c5c3_Var7 string
288- templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var7).String())227+ templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var6).String())
289 if templ_7745c5c3_Err != nil {228 if templ_7745c5c3_Err != nil {
290 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}229 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}
291 }230 }
292- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)231+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
293 if templ_7745c5c3_Err != nil {232 if templ_7745c5c3_Err != nil {
294 return templ_7745c5c3_Err233 return templ_7745c5c3_Err
295 }234 }
296- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" aria-pressed=\"")235+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\" aria-pressed=\"")
297 if templ_7745c5c3_Err != nil {236 if templ_7745c5c3_Err != nil {
298 return templ_7745c5c3_Err237 return templ_7745c5c3_Err
299 }238 }
300- var templ_7745c5c3_Var9 string239+ var templ_7745c5c3_Var8 string
301- templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteAllFilterActive(data)))240+ templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteAllFilterActive(data)))
302 if templ_7745c5c3_Err != nil {241 if templ_7745c5c3_Err != nil {
303- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 71, Col: 149}242+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 55, Col: 149}
304 }243 }
305- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)244+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
306 if templ_7745c5c3_Err != nil {245 if templ_7745c5c3_Err != nil {
307 return templ_7745c5c3_Err246 return templ_7745c5c3_Err
308 }247 }
309- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" data-sv-action=\"favorite.clearFilters\">All</button> ")248+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" data-sv-action=\"favorite.clearFilters\">All</button> ")
310 if templ_7745c5c3_Err != nil {249 if templ_7745c5c3_Err != nil {
311 return templ_7745c5c3_Err250 return templ_7745c5c3_Err
312 }251 }
313- var templ_7745c5c3_Var10 = []any{favoriteFilterClass(favoriteFolderFilterActive(data, entity.FolderID(0)))}252+ var templ_7745c5c3_Var9 = []any{favoriteFilterClass(favoriteUntaggedFilterActive(data))}
314- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...)253+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...)
315 if templ_7745c5c3_Err != nil {254 if templ_7745c5c3_Err != nil {
316 return templ_7745c5c3_Err255 return templ_7745c5c3_Err
317 }256 }
318- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<button type=\"button\" class=\"")257+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<button type=\"button\" class=\"")
319 if templ_7745c5c3_Err != nil {258 if templ_7745c5c3_Err != nil {
320 return templ_7745c5c3_Err259 return templ_7745c5c3_Err
321 }260 }
322- var templ_7745c5c3_Var11 string261+ var templ_7745c5c3_Var10 string
323- templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var10).String())262+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var9).String())
324 if templ_7745c5c3_Err != nil {263 if templ_7745c5c3_Err != nil {
325 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}264 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}
326 }265 }
327- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)266+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
328 if templ_7745c5c3_Err != nil {267 if templ_7745c5c3_Err != nil {
329 return templ_7745c5c3_Err268 return templ_7745c5c3_Err
330 }269 }
331- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" aria-pressed=\"")270+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\" aria-pressed=\"")
332 if templ_7745c5c3_Err != nil {271 if templ_7745c5c3_Err != nil {
333 return templ_7745c5c3_Err272 return templ_7745c5c3_Err
334 }273 }
335- var templ_7745c5c3_Var12 string274+ var templ_7745c5c3_Var11 string
336- templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteFolderFilterActive(data, entity.FolderID(0))))275+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteUntaggedFilterActive(data)))
337 if templ_7745c5c3_Err != nil {276 if templ_7745c5c3_Err != nil {
338- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 72, Col: 195}277+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 56, Col: 159}
339 }278 }
340- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12)279+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
341 if templ_7745c5c3_Err != nil {280 if templ_7745c5c3_Err != nil {
342 return templ_7745c5c3_Err281 return templ_7745c5c3_Err
343 }282 }
344- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\" data-sv-action=\"favorite.filterFolder\" data-folder-id=\"root\">Unfiled</button> ")283+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" data-sv-action=\"favorite.filterUntagged\">Untagged</button> ")
345- if templ_7745c5c3_Err != nil {
346- return templ_7745c5c3_Err
347- }
348- for _, folder := range data.Folders {
349- if folder != nil {
350- var templ_7745c5c3_Var13 = []any{favoriteFilterClass(favoriteFolderFilterActive(data, folder.ID))}
351- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var13...)
352- if templ_7745c5c3_Err != nil {
353- return templ_7745c5c3_Err
354- }
355- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<button type=\"button\" class=\"")
356- if templ_7745c5c3_Err != nil {
357- return templ_7745c5c3_Err
358- }
359- var templ_7745c5c3_Var14 string
360- templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var13).String())
361- if templ_7745c5c3_Err != nil {
362- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}
363- }
364- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
365- if templ_7745c5c3_Err != nil {
366- return templ_7745c5c3_Err
367- }
368- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" aria-pressed=\"")
369- if templ_7745c5c3_Err != nil {
370- return templ_7745c5c3_Err
371- }
372- var templ_7745c5c3_Var15 string
373- templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteFolderFilterActive(data, folder.ID)))
374- if templ_7745c5c3_Err != nil {
375- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 75, Col: 179}
376- }
377- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
378- if templ_7745c5c3_Err != nil {
379- return templ_7745c5c3_Err
380- }
381- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" data-sv-action=\"favorite.filterFolder\" data-folder-id=\"")
382- if templ_7745c5c3_Err != nil {
383- return templ_7745c5c3_Err
384- }
385- var templ_7745c5c3_Var16 string
386- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", folder.ID))
387- if templ_7745c5c3_Err != nil {
388- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 75, Col: 266}
389- }
390- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
391- if templ_7745c5c3_Err != nil {
392- return templ_7745c5c3_Err
393- }
394- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\">")
395- if templ_7745c5c3_Err != nil {
396- return templ_7745c5c3_Err
397- }
398- var templ_7745c5c3_Var17 string
399- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(folderDisplayName(folder))
400- if templ_7745c5c3_Err != nil {
401- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 75, Col: 296}
402- }
403- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
404- if templ_7745c5c3_Err != nil {
405- return templ_7745c5c3_Err
406- }
407- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</button>")
408- if templ_7745c5c3_Err != nil {
409- return templ_7745c5c3_Err
410- }
411- }
412- }
413- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><div class=\"sv-button-row\"><span class=\"sv-meta\">Tags</span> ")
414 if templ_7745c5c3_Err != nil {284 if templ_7745c5c3_Err != nil {
415 return templ_7745c5c3_Err285 return templ_7745c5c3_Err
416 }286 }
417 for _, tag := range data.Tags {287 for _, tag := range data.Tags {
418 if tag != nil {288 if tag != nil {
419- var templ_7745c5c3_Var18 = []any{favoriteFilterClass(favoriteTagFilterActive(data, tag.ID))}289+ var templ_7745c5c3_Var12 = []any{favoriteFilterClass(favoriteTagFilterActive(data, tag.ID))}
420- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...)290+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
421 if templ_7745c5c3_Err != nil {291 if templ_7745c5c3_Err != nil {
422 return templ_7745c5c3_Err292 return templ_7745c5c3_Err
423 }293 }
424- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<button type=\"button\" class=\"")294+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<button type=\"button\" class=\"")
425 if templ_7745c5c3_Err != nil {295 if templ_7745c5c3_Err != nil {
426 return templ_7745c5c3_Err296 return templ_7745c5c3_Err
427 }297 }
428- var templ_7745c5c3_Var19 string298+ var templ_7745c5c3_Var13 string
429- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var18).String())299+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var12).String())
430 if templ_7745c5c3_Err != nil {300 if templ_7745c5c3_Err != nil {
431 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}301 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}
432 }302 }
433- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)303+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
434 if templ_7745c5c3_Err != nil {304 if templ_7745c5c3_Err != nil {
435 return templ_7745c5c3_Err305 return templ_7745c5c3_Err
436 }306 }
437- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\" aria-pressed=\"")307+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\" aria-pressed=\"")
438 if templ_7745c5c3_Err != nil {308 if templ_7745c5c3_Err != nil {
439 return templ_7745c5c3_Err309 return templ_7745c5c3_Err
440 }310 }
441- var templ_7745c5c3_Var20 string311+ var templ_7745c5c3_Var14 string
442- templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteTagFilterActive(data, tag.ID)))312+ templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%t", favoriteTagFilterActive(data, tag.ID)))
443 if templ_7745c5c3_Err != nil {313 if templ_7745c5c3_Err != nil {
444- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 83, Col: 167}314+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 59, Col: 167}
445 }315 }
446- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20)316+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
447 if templ_7745c5c3_Err != nil {317 if templ_7745c5c3_Err != nil {
448 return templ_7745c5c3_Err318 return templ_7745c5c3_Err
449 }319 }
450- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" data-sv-action=\"favorite.filterTag\" data-tag-id=\"")320+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" data-sv-action=\"favorite.filterTag\" data-tag-id=\"")
451 if templ_7745c5c3_Err != nil {321 if templ_7745c5c3_Err != nil {
452 return templ_7745c5c3_Err322 return templ_7745c5c3_Err
453 }323 }
454- var templ_7745c5c3_Var21 string324+ var templ_7745c5c3_Var15 string
455- templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))325+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
456 if templ_7745c5c3_Err != nil {326 if templ_7745c5c3_Err != nil {
457- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 83, Col: 245}327+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 59, Col: 245}
458 }328 }
459- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21)329+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
460 if templ_7745c5c3_Err != nil {330 if templ_7745c5c3_Err != nil {
461 return templ_7745c5c3_Err331 return templ_7745c5c3_Err
462 }332 }
463- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\"><span class=\"sv-tag-dot\" style=\"")333+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\"><span class=\"sv-tag-dot\" style=\"")
464 if templ_7745c5c3_Err != nil {334 if templ_7745c5c3_Err != nil {
465 return templ_7745c5c3_Err335 return templ_7745c5c3_Err
466 }336 }
467- var templ_7745c5c3_Var22 string337+ var templ_7745c5c3_Var16 string
468- templ_7745c5c3_Var22, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))338+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
469 if templ_7745c5c3_Err != nil {339 if templ_7745c5c3_Err != nil {
470- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 84, Col: 78}340+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 60, Col: 78}
471 }341 }
472- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))342+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
473 if templ_7745c5c3_Err != nil {343 if templ_7745c5c3_Err != nil {
474 return templ_7745c5c3_Err344 return templ_7745c5c3_Err
475 }345 }
476- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\"></span>")346+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\"></span>")
477 if templ_7745c5c3_Err != nil {347 if templ_7745c5c3_Err != nil {
478 return templ_7745c5c3_Err348 return templ_7745c5c3_Err
479 }349 }
480- var templ_7745c5c3_Var23 string350+ var templ_7745c5c3_Var17 string
481- templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(tag.Name)351+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(tag.Name)
482 if templ_7745c5c3_Err != nil {352 if templ_7745c5c3_Err != nil {
483- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 84, Col: 98}353+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 60, Col: 98}
484 }354 }
485- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))355+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
486 if templ_7745c5c3_Err != nil {356 if templ_7745c5c3_Err != nil {
487 return templ_7745c5c3_Err357 return templ_7745c5c3_Err
488 }358 }
489- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</button>")359+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</button>")
490 if templ_7745c5c3_Err != nil {360 if templ_7745c5c3_Err != nil {
491 return templ_7745c5c3_Err361 return templ_7745c5c3_Err
492 }362 }
493 }363 }
494 }364 }
495- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</div></div><p class=\"sv-meta sv-key-hints\">Keys: <kbd>j</kbd>/<kbd>k</kbd> move, <kbd>Enter</kbd> open, <kbd>e</kbd> edit title, <kbd>d</kbd> delete focused favorite.</p>")365+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</div></div><p class=\"sv-meta sv-key-hints\">Keys: <kbd>j</kbd>/<kbd>k</kbd> move, <kbd>Enter</kbd> open, <kbd>e</kbd> edit title, <kbd>d</kbd> delete focused favorite.</p>")
496 if templ_7745c5c3_Err != nil {366 if templ_7745c5c3_Err != nil {
497 return templ_7745c5c3_Err367 return templ_7745c5c3_Err
498 }368 }
@@ -516,64 +386,64 @@ func FavoriteCreateTagPicker(tags []*entity.Tag) templ.Component {
516 }()386 }()
517 }387 }
518 ctx = templ.InitializeContext(ctx)388 ctx = templ.InitializeContext(ctx)
519- templ_7745c5c3_Var24 := templ.GetChildren(ctx)389+ templ_7745c5c3_Var18 := templ.GetChildren(ctx)
520- if templ_7745c5c3_Var24 == nil {390+ if templ_7745c5c3_Var18 == nil {
521- templ_7745c5c3_Var24 = templ.NopComponent391+ templ_7745c5c3_Var18 = templ.NopComponent
522 }392 }
523 ctx = templ.ClearChildren(ctx)393 ctx = templ.ClearChildren(ctx)
524 if len(tags) > 0 {394 if len(tags) > 0 {
525- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<fieldset class=\"sv-tag-picker\"><legend>Tags</legend><div class=\"sv-tag-controls\">")395+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<fieldset class=\"sv-tag-picker\"><legend>Tags</legend><div class=\"sv-tag-controls\">")
526 if templ_7745c5c3_Err != nil {396 if templ_7745c5c3_Err != nil {
527 return templ_7745c5c3_Err397 return templ_7745c5c3_Err
528 }398 }
529 for _, tag := range tags {399 for _, tag := range tags {
530 if tag != nil {400 if tag != nil {
531- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<label class=\"sv-tag-toggle\"><input type=\"checkbox\" name=\"tags\" value=\"")401+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<label class=\"sv-tag-toggle\"><input type=\"checkbox\" name=\"tags\" value=\"")
532 if templ_7745c5c3_Err != nil {402 if templ_7745c5c3_Err != nil {
533 return templ_7745c5c3_Err403 return templ_7745c5c3_Err
534 }404 }
535- var templ_7745c5c3_Var25 string405+ var templ_7745c5c3_Var19 string
536- templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))406+ templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
537 if templ_7745c5c3_Err != nil {407 if templ_7745c5c3_Err != nil {
538- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 101, Col: 75}408+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 77, Col: 75}
539 }409 }
540- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)410+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)
541 if templ_7745c5c3_Err != nil {411 if templ_7745c5c3_Err != nil {
542 return templ_7745c5c3_Err412 return templ_7745c5c3_Err
543 }413 }
544- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\"> <span><span class=\"sv-tag-dot\" style=\"")414+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\"> <span><span class=\"sv-tag-dot\" style=\"")
545 if templ_7745c5c3_Err != nil {415 if templ_7745c5c3_Err != nil {
546 return templ_7745c5c3_Err416 return templ_7745c5c3_Err
547 }417 }
548- var templ_7745c5c3_Var26 string418+ var templ_7745c5c3_Var20 string
549- templ_7745c5c3_Var26, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))419+ templ_7745c5c3_Var20, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
550 if templ_7745c5c3_Err != nil {420 if templ_7745c5c3_Err != nil {
551- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 102, Col: 85}421+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 78, Col: 85}
552 }422 }
553- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))423+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
554 if templ_7745c5c3_Err != nil {424 if templ_7745c5c3_Err != nil {
555 return templ_7745c5c3_Err425 return templ_7745c5c3_Err
556 }426 }
557- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\"></span>")427+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\"></span>")
558 if templ_7745c5c3_Err != nil {428 if templ_7745c5c3_Err != nil {
559 return templ_7745c5c3_Err429 return templ_7745c5c3_Err
560 }430 }
561- var templ_7745c5c3_Var27 string431+ var templ_7745c5c3_Var21 string
562- templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(tag.Name)432+ templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(tag.Name)
563 if templ_7745c5c3_Err != nil {433 if templ_7745c5c3_Err != nil {
564- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 102, Col: 105}434+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 78, Col: 105}
565 }435 }
566- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))436+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
567 if templ_7745c5c3_Err != nil {437 if templ_7745c5c3_Err != nil {
568 return templ_7745c5c3_Err438 return templ_7745c5c3_Err
569 }439 }
570- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</span></label>")440+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</span></label>")
571 if templ_7745c5c3_Err != nil {441 if templ_7745c5c3_Err != nil {
572 return templ_7745c5c3_Err442 return templ_7745c5c3_Err
573 }443 }
574 }444 }
575 }445 }
576- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</div></fieldset>")446+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</div></fieldset>")
577 if templ_7745c5c3_Err != nil {447 if templ_7745c5c3_Err != nil {
578 return templ_7745c5c3_Err448 return templ_7745c5c3_Err
579 }449 }
@@ -582,7 +452,7 @@ func FavoriteCreateTagPicker(tags []*entity.Tag) templ.Component {
582 })452 })
583 }453 }
584 454
585-func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*entity.Tag) templ.Component {455+func FavoriteItem(favorite *entity.Favorite, tags []*entity.Tag) templ.Component {
586 return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {456 return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
587 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context457 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
588 if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {458 if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -598,100 +468,100 @@ func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*e
598 }()468 }()
599 }469 }
600 ctx = templ.InitializeContext(ctx)470 ctx = templ.InitializeContext(ctx)
601- templ_7745c5c3_Var28 := templ.GetChildren(ctx)471+ templ_7745c5c3_Var22 := templ.GetChildren(ctx)
602- if templ_7745c5c3_Var28 == nil {472+ if templ_7745c5c3_Var22 == nil {
603- templ_7745c5c3_Var28 = templ.NopComponent473+ templ_7745c5c3_Var22 = templ.NopComponent
604 }474 }
605 ctx = templ.ClearChildren(ctx)475 ctx = templ.ClearChildren(ctx)
606- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<li class=\"sv-list-row\"><article class=\"sv-favorite-item\" data-sv-favorite-row data-favorite-id=\"")476+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<li class=\"sv-list-row\"><article class=\"sv-favorite-item\" data-sv-favorite-row data-favorite-id=\"")
607 if templ_7745c5c3_Err != nil {477 if templ_7745c5c3_Err != nil {
608 return templ_7745c5c3_Err478 return templ_7745c5c3_Err
609 }479 }
610- var templ_7745c5c3_Var29 string480+ var templ_7745c5c3_Var23 string
611- templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))481+ templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
612 if templ_7745c5c3_Err != nil {482 if templ_7745c5c3_Err != nil {
613- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 113, Col: 106}483+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 89, Col: 106}
614 }484 }
615- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)485+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
616 if templ_7745c5c3_Err != nil {486 if templ_7745c5c3_Err != nil {
617 return templ_7745c5c3_Err487 return templ_7745c5c3_Err
618 }488 }
619- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\"><div class=\"sv-favorite-header\"><div><a class=\"sv-link sv-favorite-open\" href=\"")489+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\"><div class=\"sv-favorite-header\"><div><a class=\"sv-link sv-favorite-open\" href=\"")
620 if templ_7745c5c3_Err != nil {490 if templ_7745c5c3_Err != nil {
621 return templ_7745c5c3_Err491 return templ_7745c5c3_Err
622 }492 }
623- var templ_7745c5c3_Var30 templ.SafeURL493+ var templ_7745c5c3_Var24 templ.SafeURL
624- templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(sanitizeHref(favorite.URL)))494+ templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(sanitizeHref(favorite.URL)))
625 if templ_7745c5c3_Err != nil {495 if templ_7745c5c3_Err != nil {
626- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 116, Col: 89}496+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 92, Col: 89}
627 }497 }
628- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))498+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
629 if templ_7745c5c3_Err != nil {499 if templ_7745c5c3_Err != nil {
630 return templ_7745c5c3_Err500 return templ_7745c5c3_Err
631 }501 }
632- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\">")502+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\">")
633 if templ_7745c5c3_Err != nil {503 if templ_7745c5c3_Err != nil {
634 return templ_7745c5c3_Err504 return templ_7745c5c3_Err
635 }505 }
636- var templ_7745c5c3_Var31 string506+ var templ_7745c5c3_Var25 string
637- templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteItemLabel(favorite))507+ templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteItemLabel(favorite))
638 if templ_7745c5c3_Err != nil {508 if templ_7745c5c3_Err != nil {
639- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 116, Col: 121}509+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 92, Col: 121}
640 }510 }
641- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))511+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
642 if templ_7745c5c3_Err != nil {512 if templ_7745c5c3_Err != nil {
643 return templ_7745c5c3_Err513 return templ_7745c5c3_Err
644 }514 }
645- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</a><p class=\"sv-history-url\">")515+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</a><p class=\"sv-history-url\">")
646 if templ_7745c5c3_Err != nil {516 if templ_7745c5c3_Err != nil {
647 return templ_7745c5c3_Err517 return templ_7745c5c3_Err
648 }518 }
649- var templ_7745c5c3_Var32 string519+ var templ_7745c5c3_Var26 string
650- templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(favorite.URL)520+ templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(favorite.URL)
651 if templ_7745c5c3_Err != nil {521 if templ_7745c5c3_Err != nil {
652- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 117, Col: 45}522+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 93, Col: 45}
653 }523 }
654- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))524+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
655 if templ_7745c5c3_Err != nil {525 if templ_7745c5c3_Err != nil {
656 return templ_7745c5c3_Err526 return templ_7745c5c3_Err
657 }527 }
658- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</p>")528+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</p>")
659 if templ_7745c5c3_Err != nil {529 if templ_7745c5c3_Err != nil {
660 return templ_7745c5c3_Err530 return templ_7745c5c3_Err
661 }531 }
662- if favoriteMetaText(favorite, folders) != "" {532+ if favoriteMetaText(favorite) != "" {
663- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<p class=\"sv-meta\">")533+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<p class=\"sv-meta\">")
664 if templ_7745c5c3_Err != nil {534 if templ_7745c5c3_Err != nil {
665 return templ_7745c5c3_Err535 return templ_7745c5c3_Err
666 }536 }
667- var templ_7745c5c3_Var33 string537+ var templ_7745c5c3_Var27 string
668- templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteMetaText(favorite, folders))538+ templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteMetaText(favorite))
669 if templ_7745c5c3_Err != nil {539 if templ_7745c5c3_Err != nil {
670- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 119, Col: 62}540+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 95, Col: 53}
671 }541 }
672- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))542+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
673 if templ_7745c5c3_Err != nil {543 if templ_7745c5c3_Err != nil {
674 return templ_7745c5c3_Err544 return templ_7745c5c3_Err
675 }545 }
676- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</p>")546+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</p>")
677 if templ_7745c5c3_Err != nil {547 if templ_7745c5c3_Err != nil {
678 return templ_7745c5c3_Err548 return templ_7745c5c3_Err
679 }549 }
680 }550 }
681- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</div><div class=\"sv-history-row-actions\"><a class=\"sv-button sv-button-secondary\" href=\"")551+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</div><div class=\"sv-history-row-actions\"><a class=\"sv-button sv-button-secondary\" href=\"")
682 if templ_7745c5c3_Err != nil {552 if templ_7745c5c3_Err != nil {
683 return templ_7745c5c3_Err553 return templ_7745c5c3_Err
684 }554 }
685- var templ_7745c5c3_Var34 templ.SafeURL555+ var templ_7745c5c3_Var28 templ.SafeURL
686- templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(sanitizeHref(favorite.URL)))556+ templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(sanitizeHref(favorite.URL)))
687 if templ_7745c5c3_Err != nil {557 if templ_7745c5c3_Err != nil {
688- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 123, Col: 94}558+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 99, Col: 94}
689 }559 }
690- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))560+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
691 if templ_7745c5c3_Err != nil {561 if templ_7745c5c3_Err != nil {
692 return templ_7745c5c3_Err562 return templ_7745c5c3_Err
693 }563 }
694- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "\">")564+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "\">")
695 if templ_7745c5c3_Err != nil {565 if templ_7745c5c3_Err != nil {
696 return templ_7745c5c3_Err566 return templ_7745c5c3_Err
697 }567 }
@@ -699,20 +569,20 @@ func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*e
699 if templ_7745c5c3_Err != nil {569 if templ_7745c5c3_Err != nil {
700 return templ_7745c5c3_Err570 return templ_7745c5c3_Err
701 }571 }
702- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</a> <button type=\"button\" class=\"sv-button sv-button-secondary sv-button-danger sv-button-icon-only\" data-sv-action=\"favorite.delete\" data-id=\"")572+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</a> <button type=\"button\" class=\"sv-button sv-button-secondary sv-button-danger sv-button-icon-only\" data-sv-action=\"favorite.delete\" data-id=\"")
703 if templ_7745c5c3_Err != nil {573 if templ_7745c5c3_Err != nil {
704 return templ_7745c5c3_Err574 return templ_7745c5c3_Err
705 }575 }
706- var templ_7745c5c3_Var35 string576+ var templ_7745c5c3_Var29 string
707- templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))577+ templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
708 if templ_7745c5c3_Err != nil {578 if templ_7745c5c3_Err != nil {
709- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 124, Col: 175}579+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 100, Col: 175}
710 }580 }
711- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)581+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)
712 if templ_7745c5c3_Err != nil {582 if templ_7745c5c3_Err != nil {
713 return templ_7745c5c3_Err583 return templ_7745c5c3_Err
714 }584 }
715- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "\" data-sv-confirm=\"Delete this favorite?\" aria-label=\"Delete this favorite\">")585+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\" data-sv-confirm=\"Delete this favorite?\" aria-label=\"Delete this favorite\">")
716 if templ_7745c5c3_Err != nil {586 if templ_7745c5c3_Err != nil {
717 return templ_7745c5c3_Err587 return templ_7745c5c3_Err
718 }588 }
@@ -720,11 +590,11 @@ func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*e
720 if templ_7745c5c3_Err != nil {590 if templ_7745c5c3_Err != nil {
721 return templ_7745c5c3_Err591 return templ_7745c5c3_Err
722 }592 }
723- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</button></div></div>")593+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</button></div></div>")
724 if templ_7745c5c3_Err != nil {594 if templ_7745c5c3_Err != nil {
725 return templ_7745c5c3_Err595 return templ_7745c5c3_Err
726 }596 }
727- templ_7745c5c3_Err = FavoriteEditForm(favorite, folders).Render(ctx, templ_7745c5c3_Buffer)597+ templ_7745c5c3_Err = FavoriteEditForm(favorite).Render(ctx, templ_7745c5c3_Buffer)
728 if templ_7745c5c3_Err != nil {598 if templ_7745c5c3_Err != nil {
729 return templ_7745c5c3_Err599 return templ_7745c5c3_Err
730 }600 }
@@ -732,7 +602,7 @@ func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*e
732 if templ_7745c5c3_Err != nil {602 if templ_7745c5c3_Err != nil {
733 return templ_7745c5c3_Err603 return templ_7745c5c3_Err
734 }604 }
735- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</article></li>")605+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</article></li>")
736 if templ_7745c5c3_Err != nil {606 if templ_7745c5c3_Err != nil {
737 return templ_7745c5c3_Err607 return templ_7745c5c3_Err
738 }608 }
@@ -740,7 +610,7 @@ func FavoriteItem(favorite *entity.Favorite, folders []*entity.Folder, tags []*e
740 })610 })
741 }611 }
742 612
743-func FavoriteEditForm(favorite *entity.Favorite, folders []*entity.Folder) templ.Component {613+func FavoriteEditForm(favorite *entity.Favorite) templ.Component {
744 return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {614 return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
745 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context615 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
746 if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {616 if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -756,55 +626,51 @@ func FavoriteEditForm(favorite *entity.Favorite, folders []*entity.Folder) templ
756 }()626 }()
757 }627 }
758 ctx = templ.InitializeContext(ctx)628 ctx = templ.InitializeContext(ctx)
759- templ_7745c5c3_Var36 := templ.GetChildren(ctx)629+ templ_7745c5c3_Var30 := templ.GetChildren(ctx)
760- if templ_7745c5c3_Var36 == nil {630+ if templ_7745c5c3_Var30 == nil {
761- templ_7745c5c3_Var36 = templ.NopComponent631+ templ_7745c5c3_Var30 = templ.NopComponent
762 }632 }
763 ctx = templ.ClearChildren(ctx)633 ctx = templ.ClearChildren(ctx)
764- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<form class=\"sv-inline-form\" data-sv-action=\"favorite.update\"><input type=\"hidden\" name=\"id\" value=\"")634+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<form class=\"sv-inline-form\" data-sv-action=\"favorite.update\"><input type=\"hidden\" name=\"id\" value=\"")
765 if templ_7745c5c3_Err != nil {635 if templ_7745c5c3_Err != nil {
766 return templ_7745c5c3_Err636 return templ_7745c5c3_Err
767 }637 }
768- var templ_7745c5c3_Var37 string638+ var templ_7745c5c3_Var31 string
769- templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))639+ templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
770 if templ_7745c5c3_Err != nil {640 if templ_7745c5c3_Err != nil {
771- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 135, Col: 71}641+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 111, Col: 71}
772 }642 }
773- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37)643+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var31)
774 if templ_7745c5c3_Err != nil {644 if templ_7745c5c3_Err != nil {
775 return templ_7745c5c3_Err645 return templ_7745c5c3_Err
776 }646 }
777- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\"> <label><span>Title</span><input name=\"title\" type=\"text\" value=\"")647+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\"> <label><span>Title</span><input name=\"title\" type=\"text\" value=\"")
778 if templ_7745c5c3_Err != nil {648 if templ_7745c5c3_Err != nil {
779 return templ_7745c5c3_Err649 return templ_7745c5c3_Err
780 }650 }
781- var templ_7745c5c3_Var38 string651+ var templ_7745c5c3_Var32 string
782- templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(favorite.Title)652+ templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.ResolveAttributeValue(favorite.Title)
783 if templ_7745c5c3_Err != nil {653 if templ_7745c5c3_Err != nil {
784- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 136, Col: 81}654+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 112, Col: 81}
785 }655 }
786- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38)656+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var32)
787 if templ_7745c5c3_Err != nil {657 if templ_7745c5c3_Err != nil {
788 return templ_7745c5c3_Err658 return templ_7745c5c3_Err
789 }659 }
790- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\"></label> <label><span>Favicon</span><input name=\"favicon_url\" type=\"url\" value=\"")660+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\"></label> <label><span>Favicon</span><input name=\"favicon_url\" type=\"url\" value=\"")
791 if templ_7745c5c3_Err != nil {661 if templ_7745c5c3_Err != nil {
792 return templ_7745c5c3_Err662 return templ_7745c5c3_Err
793 }663 }
794- var templ_7745c5c3_Var39 string664+ var templ_7745c5c3_Var33 string
795- templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.ResolveAttributeValue(favorite.FaviconURL)665+ templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.ResolveAttributeValue(favorite.FaviconURL)
796 if templ_7745c5c3_Err != nil {666 if templ_7745c5c3_Err != nil {
797- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 137, Col: 93}667+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 113, Col: 93}
798 }668 }
799- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var39)669+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33)
800 if templ_7745c5c3_Err != nil {670 if templ_7745c5c3_Err != nil {
801 return templ_7745c5c3_Err671 return templ_7745c5c3_Err
802 }672 }
803- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\"></label>")673+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\"></label>")
804- if templ_7745c5c3_Err != nil {
805- return templ_7745c5c3_Err
806- }
807- templ_7745c5c3_Err = FolderSelect("folder_id", folders, favorite.FolderID, "Folder").Render(ctx, templ_7745c5c3_Buffer)
808 if templ_7745c5c3_Err != nil {674 if templ_7745c5c3_Err != nil {
809 return templ_7745c5c3_Err675 return templ_7745c5c3_Err
810 }676 }
@@ -812,7 +678,7 @@ func FavoriteEditForm(favorite *entity.Favorite, folders []*entity.Folder) templ
812 if templ_7745c5c3_Err != nil {678 if templ_7745c5c3_Err != nil {
813 return templ_7745c5c3_Err679 return templ_7745c5c3_Err
814 }680 }
815- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<button class=\"sv-button sv-button-secondary\" type=\"submit\">")681+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<button class=\"sv-button sv-button-secondary\" type=\"submit\">")
816 if templ_7745c5c3_Err != nil {682 if templ_7745c5c3_Err != nil {
817 return templ_7745c5c3_Err683 return templ_7745c5c3_Err
818 }684 }
@@ -820,7 +686,7 @@ func FavoriteEditForm(favorite *entity.Favorite, folders []*entity.Folder) templ
820 if templ_7745c5c3_Err != nil {686 if templ_7745c5c3_Err != nil {
821 return templ_7745c5c3_Err687 return templ_7745c5c3_Err
822 }688 }
823- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</button></form>")689+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</button></form>")
824 if templ_7745c5c3_Err != nil {690 if templ_7745c5c3_Err != nil {
825 return templ_7745c5c3_Err691 return templ_7745c5c3_Err
826 }692 }
@@ -844,108 +710,108 @@ func FavoriteTagControls(favorite *entity.Favorite, tags []*entity.Tag) templ.Co
844 }()710 }()
845 }711 }
846 ctx = templ.InitializeContext(ctx)712 ctx = templ.InitializeContext(ctx)
847- templ_7745c5c3_Var40 := templ.GetChildren(ctx)713+ templ_7745c5c3_Var34 := templ.GetChildren(ctx)
848- if templ_7745c5c3_Var40 == nil {714+ if templ_7745c5c3_Var34 == nil {
849- templ_7745c5c3_Var40 = templ.NopComponent715+ templ_7745c5c3_Var34 = templ.NopComponent
850 }716 }
851 ctx = templ.ClearChildren(ctx)717 ctx = templ.ClearChildren(ctx)
852 if len(tags) > 0 {718 if len(tags) > 0 {
853- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<div class=\"sv-tag-controls\"><span class=\"sv-meta\">Tags</span> ")719+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<div class=\"sv-tag-controls\"><span class=\"sv-meta\">Tags</span> ")
854 if templ_7745c5c3_Err != nil {720 if templ_7745c5c3_Err != nil {
855 return templ_7745c5c3_Err721 return templ_7745c5c3_Err
856 }722 }
857 for _, tag := range tags {723 for _, tag := range tags {
858 if tag != nil {724 if tag != nil {
859- var templ_7745c5c3_Var41 = []any{favoriteTagButtonClass(favorite, tag)}725+ var templ_7745c5c3_Var35 = []any{favoriteTagButtonClass(favorite, tag)}
860- templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var41...)726+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var35...)
861 if templ_7745c5c3_Err != nil {727 if templ_7745c5c3_Err != nil {
862 return templ_7745c5c3_Err728 return templ_7745c5c3_Err
863 }729 }
864- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "<button type=\"button\" class=\"")730+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<button type=\"button\" class=\"")
865 if templ_7745c5c3_Err != nil {731 if templ_7745c5c3_Err != nil {
866 return templ_7745c5c3_Err732 return templ_7745c5c3_Err
867 }733 }
868- var templ_7745c5c3_Var42 string734+ var templ_7745c5c3_Var36 string
869- templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var41).String())735+ templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var35).String())
870 if templ_7745c5c3_Err != nil {736 if templ_7745c5c3_Err != nil {
871 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}737 return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 1, Col: 0}
872 }738 }
873- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var42)739+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var36)
874 if templ_7745c5c3_Err != nil {740 if templ_7745c5c3_Err != nil {
875 return templ_7745c5c3_Err741 return templ_7745c5c3_Err
876 }742 }
877- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\" data-sv-action=\"")743+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "\" data-sv-action=\"")
878 if templ_7745c5c3_Err != nil {744 if templ_7745c5c3_Err != nil {
879 return templ_7745c5c3_Err745 return templ_7745c5c3_Err
880 }746 }
881- var templ_7745c5c3_Var43 string747+ var templ_7745c5c3_Var37 string
882- templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.ResolveAttributeValue(favoriteTagButtonAction(favorite, tag))748+ templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(favoriteTagButtonAction(favorite, tag))
883 if templ_7745c5c3_Err != nil {749 if templ_7745c5c3_Err != nil {
884- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 150, Col: 130}750+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 125, Col: 130}
885 }751 }
886- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var43)752+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37)
887 if templ_7745c5c3_Err != nil {753 if templ_7745c5c3_Err != nil {
888 return templ_7745c5c3_Err754 return templ_7745c5c3_Err
889 }755 }
890- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\" data-favorite-id=\"")756+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\" data-favorite-id=\"")
891 if templ_7745c5c3_Err != nil {757 if templ_7745c5c3_Err != nil {
892 return templ_7745c5c3_Err758 return templ_7745c5c3_Err
893 }759 }
894- var templ_7745c5c3_Var44 string760+ var templ_7745c5c3_Var38 string
895- templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))761+ templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", favorite.ID))
896 if templ_7745c5c3_Err != nil {762 if templ_7745c5c3_Err != nil {
897- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 150, Col: 182}763+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 125, Col: 182}
898 }764 }
899- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var44)765+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38)
900 if templ_7745c5c3_Err != nil {766 if templ_7745c5c3_Err != nil {
901 return templ_7745c5c3_Err767 return templ_7745c5c3_Err
902 }768 }
903- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" data-tag-id=\"")769+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\" data-tag-id=\"")
904 if templ_7745c5c3_Err != nil {770 if templ_7745c5c3_Err != nil {
905 return templ_7745c5c3_Err771 return templ_7745c5c3_Err
906 }772 }
907- var templ_7745c5c3_Var45 string773+ var templ_7745c5c3_Var39 string
908- templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))774+ templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
909 if templ_7745c5c3_Err != nil {775 if templ_7745c5c3_Err != nil {
910- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 150, Col: 224}776+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 125, Col: 224}
911 }777 }
912- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var45)778+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var39)
913 if templ_7745c5c3_Err != nil {779 if templ_7745c5c3_Err != nil {
914 return templ_7745c5c3_Err780 return templ_7745c5c3_Err
915 }781 }
916- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "\"><span class=\"sv-tag-dot\" style=\"")782+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\"><span class=\"sv-tag-dot\" style=\"")
917 if templ_7745c5c3_Err != nil {783 if templ_7745c5c3_Err != nil {
918 return templ_7745c5c3_Err784 return templ_7745c5c3_Err
919 }785 }
920- var templ_7745c5c3_Var46 string786+ var templ_7745c5c3_Var40 string
921- templ_7745c5c3_Var46, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))787+ templ_7745c5c3_Var40, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
922 if templ_7745c5c3_Err != nil {788 if templ_7745c5c3_Err != nil {
923- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 151, Col: 78}789+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 126, Col: 78}
924 }790 }
925- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))791+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
926 if templ_7745c5c3_Err != nil {792 if templ_7745c5c3_Err != nil {
927 return templ_7745c5c3_Err793 return templ_7745c5c3_Err
928 }794 }
929- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "\"></span>")795+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\"></span>")
930 if templ_7745c5c3_Err != nil {796 if templ_7745c5c3_Err != nil {
931 return templ_7745c5c3_Err797 return templ_7745c5c3_Err
932 }798 }
933- var templ_7745c5c3_Var47 string799+ var templ_7745c5c3_Var41 string
934- templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteTagButtonLabel(favorite, tag))800+ templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(favoriteTagButtonLabel(favorite, tag))
935 if templ_7745c5c3_Err != nil {801 if templ_7745c5c3_Err != nil {
936- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 151, Col: 127}802+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 126, Col: 127}
937 }803 }
938- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))804+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
939 if templ_7745c5c3_Err != nil {805 if templ_7745c5c3_Err != nil {
940 return templ_7745c5c3_Err806 return templ_7745c5c3_Err
941 }807 }
942- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "</button>")808+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "</button>")
943 if templ_7745c5c3_Err != nil {809 if templ_7745c5c3_Err != nil {
944 return templ_7745c5c3_Err810 return templ_7745c5c3_Err
945 }811 }
946 }812 }
947 }813 }
948- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "</div>")814+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</div>")
949 if templ_7745c5c3_Err != nil {815 if templ_7745c5c3_Err != nil {
950 return templ_7745c5c3_Err816 return templ_7745c5c3_Err
951 }817 }
@@ -954,140 +820,6 @@ func FavoriteTagControls(favorite *entity.Favorite, tags []*entity.Tag) templ.Co
954 })820 })
955 }821 }
956 822
957-func FolderCreateForm() templ.Component {
958- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
959- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
960- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
961- return templ_7745c5c3_CtxErr
962- }
963- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
964- if !templ_7745c5c3_IsBuffer {
965- defer func() {
966- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
967- if templ_7745c5c3_Err == nil {
968- templ_7745c5c3_Err = templ_7745c5c3_BufErr
969- }
970- }()
971- }
972- ctx = templ.InitializeContext(ctx)
973- templ_7745c5c3_Var48 := templ.GetChildren(ctx)
974- if templ_7745c5c3_Var48 == nil {
975- templ_7745c5c3_Var48 = templ.NopComponent
976- }
977- ctx = templ.ClearChildren(ctx)
978- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<form class=\"sv-inline-form\" data-sv-action=\"folder.create\"><label><span>Name</span><input name=\"name\" type=\"text\" required></label> <label><span>Icon</span><input name=\"icon\" type=\"text\" placeholder=\"Optional\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")
979- if templ_7745c5c3_Err != nil {
980- return templ_7745c5c3_Err
981- }
982- templ_7745c5c3_Err = ButtonIconLabel(iconFolder, "Create folder").Render(ctx, templ_7745c5c3_Buffer)
983- if templ_7745c5c3_Err != nil {
984- return templ_7745c5c3_Err
985- }
986- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "</button></form>")
987- if templ_7745c5c3_Err != nil {
988- return templ_7745c5c3_Err
989- }
990- return nil
991- })
992-}
993-
994-func FolderItem(folder *entity.Folder) templ.Component {
995- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
996- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
997- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
998- return templ_7745c5c3_CtxErr
999- }
1000- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
1001- if !templ_7745c5c3_IsBuffer {
1002- defer func() {
1003- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
1004- if templ_7745c5c3_Err == nil {
1005- templ_7745c5c3_Err = templ_7745c5c3_BufErr
1006- }
1007- }()
1008- }
1009- ctx = templ.InitializeContext(ctx)
1010- templ_7745c5c3_Var49 := templ.GetChildren(ctx)
1011- if templ_7745c5c3_Var49 == nil {
1012- templ_7745c5c3_Var49 = templ.NopComponent
1013- }
1014- ctx = templ.ClearChildren(ctx)
1015- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "<li class=\"sv-list-row\"><form class=\"sv-inline-form\" data-sv-action=\"folder.update\"><input type=\"hidden\" name=\"id\" value=\"")
1016- if templ_7745c5c3_Err != nil {
1017- return templ_7745c5c3_Err
1018- }
1019- var templ_7745c5c3_Var50 string
1020- templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", folder.ID))
1021- if templ_7745c5c3_Err != nil {
1022- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 170, Col: 70}
1023- }
1024- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)
1025- if templ_7745c5c3_Err != nil {
1026- return templ_7745c5c3_Err
1027- }
1028- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "\"> <label><span>Name</span><input name=\"name\" type=\"text\" value=\"")
1029- if templ_7745c5c3_Err != nil {
1030- return templ_7745c5c3_Err
1031- }
1032- var templ_7745c5c3_Var51 string
1033- templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.ResolveAttributeValue(folder.Name)
1034- if templ_7745c5c3_Err != nil {
1035- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 171, Col: 77}
1036- }
1037- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var51)
1038- if templ_7745c5c3_Err != nil {
1039- return templ_7745c5c3_Err
1040- }
1041- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "\" required></label> <label><span>Icon</span><input name=\"icon\" type=\"text\" value=\"")
1042- if templ_7745c5c3_Err != nil {
1043- return templ_7745c5c3_Err
1044- }
1045- var templ_7745c5c3_Var52 string
1046- templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.ResolveAttributeValue(folder.Icon)
1047- if templ_7745c5c3_Err != nil {
1048- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 172, Col: 77}
1049- }
1050- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var52)
1051- if templ_7745c5c3_Err != nil {
1052- return templ_7745c5c3_Err
1053- }
1054- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")
1055- if templ_7745c5c3_Err != nil {
1056- return templ_7745c5c3_Err
1057- }
1058- templ_7745c5c3_Err = ButtonIconLabel(iconSave, "Save").Render(ctx, templ_7745c5c3_Buffer)
1059- if templ_7745c5c3_Err != nil {
1060- return templ_7745c5c3_Err
1061- }
1062- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</button> <button type=\"button\" class=\"sv-button sv-button-secondary sv-button-danger sv-button-icon-only\" data-sv-action=\"folder.delete\" data-id=\"")
1063- if templ_7745c5c3_Err != nil {
1064- return templ_7745c5c3_Err
1065- }
1066- var templ_7745c5c3_Var53 string
1067- templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", folder.ID))
1068- if templ_7745c5c3_Err != nil {
1069- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 174, Col: 169}
1070- }
1071- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var53)
1072- if templ_7745c5c3_Err != nil {
1073- return templ_7745c5c3_Err
1074- }
1075- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\" data-sv-confirm=\"Delete this folder? Favorites will move out of it.\" aria-label=\"Delete this folder\">")
1076- if templ_7745c5c3_Err != nil {
1077- return templ_7745c5c3_Err
1078- }
1079- templ_7745c5c3_Err = IconOnly(iconTrash, "Delete this folder").Render(ctx, templ_7745c5c3_Buffer)
1080- if templ_7745c5c3_Err != nil {
1081- return templ_7745c5c3_Err
1082- }
1083- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "</button></form></li>")
1084- if templ_7745c5c3_Err != nil {
1085- return templ_7745c5c3_Err
1086- }
1087- return nil
1088- })
1089-}
1090-
1091 func TagCreateForm() templ.Component {823 func TagCreateForm() templ.Component {
1092 return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {824 return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
1093 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context825 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
@@ -1104,12 +836,12 @@ func TagCreateForm() templ.Component {
1104 }()836 }()
1105 }837 }
1106 ctx = templ.InitializeContext(ctx)838 ctx = templ.InitializeContext(ctx)
1107- templ_7745c5c3_Var54 := templ.GetChildren(ctx)839+ templ_7745c5c3_Var42 := templ.GetChildren(ctx)
1108- if templ_7745c5c3_Var54 == nil {840+ if templ_7745c5c3_Var42 == nil {
1109- templ_7745c5c3_Var54 = templ.NopComponent841+ templ_7745c5c3_Var42 = templ.NopComponent
1110 }842 }
1111 ctx = templ.ClearChildren(ctx)843 ctx = templ.ClearChildren(ctx)
1112- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "<form class=\"sv-inline-form\" data-sv-action=\"tag.create\"><label><span>Name</span><input name=\"name\" type=\"text\" required></label> <label><span>Color</span><input name=\"color\" type=\"text\" placeholder=\"#808080\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")844+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "<form class=\"sv-inline-form\" data-sv-action=\"tag.create\"><label><span>Name</span><input name=\"name\" type=\"text\" required></label> <label><span>Color</span><input name=\"color\" type=\"text\" placeholder=\"#808080\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")
1113 if templ_7745c5c3_Err != nil {845 if templ_7745c5c3_Err != nil {
1114 return templ_7745c5c3_Err846 return templ_7745c5c3_Err
1115 }847 }
@@ -1117,7 +849,7 @@ func TagCreateForm() templ.Component {
1117 if templ_7745c5c3_Err != nil {849 if templ_7745c5c3_Err != nil {
1118 return templ_7745c5c3_Err850 return templ_7745c5c3_Err
1119 }851 }
1120- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "</button></form>")852+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</button></form>")
1121 if templ_7745c5c3_Err != nil {853 if templ_7745c5c3_Err != nil {
1122 return templ_7745c5c3_Err854 return templ_7745c5c3_Err
1123 }855 }
@@ -1141,64 +873,64 @@ func TagItem(tag *entity.Tag) templ.Component {
1141 }()873 }()
1142 }874 }
1143 ctx = templ.InitializeContext(ctx)875 ctx = templ.InitializeContext(ctx)
1144- templ_7745c5c3_Var55 := templ.GetChildren(ctx)876+ templ_7745c5c3_Var43 := templ.GetChildren(ctx)
1145- if templ_7745c5c3_Var55 == nil {877+ if templ_7745c5c3_Var43 == nil {
1146- templ_7745c5c3_Var55 = templ.NopComponent878+ templ_7745c5c3_Var43 = templ.NopComponent
1147 }879 }
1148 ctx = templ.ClearChildren(ctx)880 ctx = templ.ClearChildren(ctx)
1149- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<li class=\"sv-list-row\"><form class=\"sv-inline-form\" data-sv-action=\"tag.update\"><input type=\"hidden\" name=\"id\" value=\"")881+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "<li class=\"sv-list-row\"><form class=\"sv-inline-form\" data-sv-action=\"tag.update\"><input type=\"hidden\" name=\"id\" value=\"")
1150 if templ_7745c5c3_Err != nil {882 if templ_7745c5c3_Err != nil {
1151 return templ_7745c5c3_Err883 return templ_7745c5c3_Err
1152 }884 }
1153- var templ_7745c5c3_Var56 string885+ var templ_7745c5c3_Var44 string
1154- templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))886+ templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
1155 if templ_7745c5c3_Err != nil {887 if templ_7745c5c3_Err != nil {
1156- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 190, Col: 67}888+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 145, Col: 67}
1157 }889 }
1158- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var56)890+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var44)
1159 if templ_7745c5c3_Err != nil {891 if templ_7745c5c3_Err != nil {
1160 return templ_7745c5c3_Err892 return templ_7745c5c3_Err
1161 }893 }
1162- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\"> <span class=\"sv-tag-dot\" style=\"")894+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\"> <span class=\"sv-tag-dot\" style=\"")
1163 if templ_7745c5c3_Err != nil {895 if templ_7745c5c3_Err != nil {
1164 return templ_7745c5c3_Err896 return templ_7745c5c3_Err
1165 }897 }
1166- var templ_7745c5c3_Var57 string898+ var templ_7745c5c3_Var45 string
1167- templ_7745c5c3_Var57, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))899+ templ_7745c5c3_Var45, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("background:" + safeTagColor(tag.Color))
1168 if templ_7745c5c3_Err != nil {900 if templ_7745c5c3_Err != nil {
1169- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 191, Col: 75}901+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 146, Col: 75}
1170 }902 }
1171- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57))903+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
1172 if templ_7745c5c3_Err != nil {904 if templ_7745c5c3_Err != nil {
1173 return templ_7745c5c3_Err905 return templ_7745c5c3_Err
1174 }906 }
1175- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "\"></span> <label><span>Name</span><input name=\"name\" type=\"text\" value=\"")907+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\"></span> <label><span>Name</span><input name=\"name\" type=\"text\" value=\"")
1176 if templ_7745c5c3_Err != nil {908 if templ_7745c5c3_Err != nil {
1177 return templ_7745c5c3_Err909 return templ_7745c5c3_Err
1178 }910 }
1179- var templ_7745c5c3_Var58 string911+ var templ_7745c5c3_Var46 string
1180- templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.ResolveAttributeValue(tag.Name)912+ templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.ResolveAttributeValue(tag.Name)
1181 if templ_7745c5c3_Err != nil {913 if templ_7745c5c3_Err != nil {
1182- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 192, Col: 74}914+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 147, Col: 74}
1183 }915 }
1184- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var58)916+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var46)
1185 if templ_7745c5c3_Err != nil {917 if templ_7745c5c3_Err != nil {
1186 return templ_7745c5c3_Err918 return templ_7745c5c3_Err
1187 }919 }
1188- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "\" required></label> <label><span>Color</span><input name=\"color\" type=\"text\" value=\"")920+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" required></label> <label><span>Color</span><input name=\"color\" type=\"text\" value=\"")
1189 if templ_7745c5c3_Err != nil {921 if templ_7745c5c3_Err != nil {
1190 return templ_7745c5c3_Err922 return templ_7745c5c3_Err
1191 }923 }
1192- var templ_7745c5c3_Var59 string924+ var templ_7745c5c3_Var47 string
1193- templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.ResolveAttributeValue(tag.Color)925+ templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.ResolveAttributeValue(tag.Color)
1194 if templ_7745c5c3_Err != nil {926 if templ_7745c5c3_Err != nil {
1195- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 193, Col: 77}927+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 148, Col: 77}
1196 }928 }
1197- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var59)929+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var47)
1198 if templ_7745c5c3_Err != nil {930 if templ_7745c5c3_Err != nil {
1199 return templ_7745c5c3_Err931 return templ_7745c5c3_Err
1200 }932 }
1201- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")933+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "\"></label> <button class=\"sv-button sv-button-secondary\" type=\"submit\">")
1202 if templ_7745c5c3_Err != nil {934 if templ_7745c5c3_Err != nil {
1203 return templ_7745c5c3_Err935 return templ_7745c5c3_Err
1204 }936 }
@@ -1206,20 +938,20 @@ func TagItem(tag *entity.Tag) templ.Component {
1206 if templ_7745c5c3_Err != nil {938 if templ_7745c5c3_Err != nil {
1207 return templ_7745c5c3_Err939 return templ_7745c5c3_Err
1208 }940 }
1209- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</button> <button type=\"button\" class=\"sv-button sv-button-secondary sv-button-danger sv-button-icon-only\" data-sv-action=\"tag.delete\" data-id=\"")941+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</button> <button type=\"button\" class=\"sv-button sv-button-secondary sv-button-danger sv-button-icon-only\" data-sv-action=\"tag.delete\" data-id=\"")
1210 if templ_7745c5c3_Err != nil {942 if templ_7745c5c3_Err != nil {
1211 return templ_7745c5c3_Err943 return templ_7745c5c3_Err
1212 }944 }
1213- var templ_7745c5c3_Var60 string945+ var templ_7745c5c3_Var48 string
1214- templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))946+ templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", tag.ID))
1215 if templ_7745c5c3_Err != nil {947 if templ_7745c5c3_Err != nil {
1216- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 195, Col: 163}948+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 150, Col: 163}
1217 }949 }
1218- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var60)950+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var48)
1219 if templ_7745c5c3_Err != nil {951 if templ_7745c5c3_Err != nil {
1220 return templ_7745c5c3_Err952 return templ_7745c5c3_Err
1221 }953 }
1222- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "\" data-sv-confirm=\"Delete this tag?\" aria-label=\"Delete this tag\">")954+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "\" data-sv-confirm=\"Delete this tag?\" aria-label=\"Delete this tag\">")
1223 if templ_7745c5c3_Err != nil {955 if templ_7745c5c3_Err != nil {
1224 return templ_7745c5c3_Err956 return templ_7745c5c3_Err
1225 }957 }
@@ -1227,110 +959,7 @@ func TagItem(tag *entity.Tag) templ.Component {
1227 if templ_7745c5c3_Err != nil {959 if templ_7745c5c3_Err != nil {
1228 return templ_7745c5c3_Err960 return templ_7745c5c3_Err
1229 }961 }
1230- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "</button></form></li>")962+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "</button></form></li>")
1231- if templ_7745c5c3_Err != nil {
1232- return templ_7745c5c3_Err
1233- }
1234- return nil
1235- })
1236-}
1237-
1238-func FolderSelect(name string, folders []*entity.Folder, selected *entity.FolderID, label string) templ.Component {
1239- return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
1240- templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
1241- if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
1242- return templ_7745c5c3_CtxErr
1243- }
1244- templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
1245- if !templ_7745c5c3_IsBuffer {
1246- defer func() {
1247- templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
1248- if templ_7745c5c3_Err == nil {
1249- templ_7745c5c3_Err = templ_7745c5c3_BufErr
1250- }
1251- }()
1252- }
1253- ctx = templ.InitializeContext(ctx)
1254- templ_7745c5c3_Var61 := templ.GetChildren(ctx)
1255- if templ_7745c5c3_Var61 == nil {
1256- templ_7745c5c3_Var61 = templ.NopComponent
1257- }
1258- ctx = templ.ClearChildren(ctx)
1259- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "<label><span>")
1260- if templ_7745c5c3_Err != nil {
1261- return templ_7745c5c3_Err
1262- }
1263- var templ_7745c5c3_Var62 string
1264- templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.JoinStringErrs(label)
1265- if templ_7745c5c3_Err != nil {
1266- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 201, Col: 21}
1267- }
1268- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var62))
1269- if templ_7745c5c3_Err != nil {
1270- return templ_7745c5c3_Err
1271- }
1272- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "</span><select name=\"")
1273- if templ_7745c5c3_Err != nil {
1274- return templ_7745c5c3_Err
1275- }
1276- var templ_7745c5c3_Var63 string
1277- templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.ResolveAttributeValue(name)
1278- if templ_7745c5c3_Err != nil {
1279- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 201, Col: 49}
1280- }
1281- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var63)
1282- if templ_7745c5c3_Err != nil {
1283- return templ_7745c5c3_Err
1284- }
1285- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "\"><option value=\"\">Unfiled</option> ")
1286- if templ_7745c5c3_Err != nil {
1287- return templ_7745c5c3_Err
1288- }
1289- for _, folder := range folders {
1290- if folder != nil {
1291- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<option value=\"")
1292- if templ_7745c5c3_Err != nil {
1293- return templ_7745c5c3_Err
1294- }
1295- var templ_7745c5c3_Var64 string
1296- templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", folder.ID))
1297- if templ_7745c5c3_Err != nil {
1298- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 205, Col: 48}
1299- }
1300- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var64)
1301- if templ_7745c5c3_Err != nil {
1302- return templ_7745c5c3_Err
1303- }
1304- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "\"")
1305- if templ_7745c5c3_Err != nil {
1306- return templ_7745c5c3_Err
1307- }
1308- if folderSelected(selected, folder.ID) {
1309- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, " selected")
1310- if templ_7745c5c3_Err != nil {
1311- return templ_7745c5c3_Err
1312- }
1313- }
1314- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, ">")
1315- if templ_7745c5c3_Err != nil {
1316- return templ_7745c5c3_Err
1317- }
1318- var templ_7745c5c3_Var65 string
1319- templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(folderDisplayName(folder))
1320- if templ_7745c5c3_Err != nil {
1321- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 205, Col: 128}
1322- }
1323- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65))
1324- if templ_7745c5c3_Err != nil {
1325- return templ_7745c5c3_Err
1326- }
1327- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "</option>")
1328- if templ_7745c5c3_Err != nil {
1329- return templ_7745c5c3_Err
1330- }
1331- }
1332- }
1333- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "</select></label>")
1334 if templ_7745c5c3_Err != nil {963 if templ_7745c5c3_Err != nil {
1335 return templ_7745c5c3_Err964 return templ_7745c5c3_Err
1336 }965 }
@@ -1354,58 +983,58 @@ func ShortcutSelect(selected *int) templ.Component {
1354 }()983 }()
1355 }984 }
1356 ctx = templ.InitializeContext(ctx)985 ctx = templ.InitializeContext(ctx)
1357- templ_7745c5c3_Var66 := templ.GetChildren(ctx)986+ templ_7745c5c3_Var49 := templ.GetChildren(ctx)
1358- if templ_7745c5c3_Var66 == nil {987+ if templ_7745c5c3_Var49 == nil {
1359- templ_7745c5c3_Var66 = templ.NopComponent988+ templ_7745c5c3_Var49 = templ.NopComponent
1360 }989 }
1361 ctx = templ.ClearChildren(ctx)990 ctx = templ.ClearChildren(ctx)
1362- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "<label><span>Shortcut</span><select name=\"shortcut_key\"><option value=\"\">None</option> ")991+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<label><span>Shortcut</span><select name=\"shortcut_key\"><option value=\"\">None</option> ")
1363 if templ_7745c5c3_Err != nil {992 if templ_7745c5c3_Err != nil {
1364 return templ_7745c5c3_Err993 return templ_7745c5c3_Err
1365 }994 }
1366 for i := 1; i <= 9; i++ {995 for i := 1; i <= 9; i++ {
1367- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "<option value=\"")996+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<option value=\"")
1368 if templ_7745c5c3_Err != nil {997 if templ_7745c5c3_Err != nil {
1369 return templ_7745c5c3_Err998 return templ_7745c5c3_Err
1370 }999 }
1371- var templ_7745c5c3_Var67 string1000+ var templ_7745c5c3_Var50 string
1372- templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", i))1001+ templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", i))
1373 if templ_7745c5c3_Err != nil {1002 if templ_7745c5c3_Err != nil {
1374- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 215, Col: 39}1003+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 159, Col: 39}
1375 }1004 }
1376- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var67)1005+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)
1377 if templ_7745c5c3_Err != nil {1006 if templ_7745c5c3_Err != nil {
1378 return templ_7745c5c3_Err1007 return templ_7745c5c3_Err
1379 }1008 }
1380- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "\"")1009+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\"")
1381 if templ_7745c5c3_Err != nil {1010 if templ_7745c5c3_Err != nil {
1382 return templ_7745c5c3_Err1011 return templ_7745c5c3_Err
1383 }1012 }
1384 if shortcutSelected(selected, i) {1013 if shortcutSelected(selected, i) {
1385- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, " selected")1014+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, " selected")
1386 if templ_7745c5c3_Err != nil {1015 if templ_7745c5c3_Err != nil {
1387 return templ_7745c5c3_Err1016 return templ_7745c5c3_Err
1388 }1017 }
1389 }1018 }
1390- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, ">")1019+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, ">")
1391 if templ_7745c5c3_Err != nil {1020 if templ_7745c5c3_Err != nil {
1392 return templ_7745c5c3_Err1021 return templ_7745c5c3_Err
1393 }1022 }
1394- var templ_7745c5c3_Var68 string1023+ var templ_7745c5c3_Var51 string
1395- templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", i))1024+ templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", i))
1396 if templ_7745c5c3_Err != nil {1025 if templ_7745c5c3_Err != nil {
1397- return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 215, Col: 108}1026+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `favorites.templ`, Line: 159, Col: 108}
1398 }1027 }
1399- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68))1028+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
1400 if templ_7745c5c3_Err != nil {1029 if templ_7745c5c3_Err != nil {
1401 return templ_7745c5c3_Err1030 return templ_7745c5c3_Err
1402 }1031 }
1403- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, "</option>")1032+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "</option>")
1404 if templ_7745c5c3_Err != nil {1033 if templ_7745c5c3_Err != nil {
1405 return templ_7745c5c3_Err1034 return templ_7745c5c3_Err
1406 }1035 }
1407 }1036 }
1408- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "</select></label>")1037+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</select></label>")
1409 if templ_7745c5c3_Err != nil {1038 if templ_7745c5c3_Err != nil {
1410 return templ_7745c5c3_Err1039 return templ_7745c5c3_Err
1411 }1040 }
M internal/ui/systemviews/favorites_view.go +19 -62
@@ -10,13 +10,12 @@ import (
1010 const defaultTagColor = "#808080"
1111
1212 type favoritesRenderData struct {
13- Favorites []*entity.Favorite
14- Folders []*entity.Folder
15- Tags []*entity.Tag
16- FolderFilter *entity.FolderID
17- TagFilter *entity.TagID
18- Notice string
19- Error string
13+ Favorites []*entity.Favorite
14+ Tags []*entity.Tag
15+ TagFilter *entity.TagID
16+ UntaggedFilter bool
17+ Notice string
18+ Error string
2019 }
2120
2221 func favoritesHTML(data favoritesRenderData) string {
@@ -25,7 +24,7 @@ func favoritesHTML(data favoritesRenderData) string {
2524
2625 func favoritesDocumentTitle(data favoritesRenderData) string {
2726 count := len(data.Favorites)
28- if data.FolderFilter != nil || data.TagFilter != nil {
27+ if data.TagFilter != nil || data.UntaggedFilter {
2928 if count == 1 {
3029 return "Favorites — 1 filtered bookmark"
3130 }
@@ -38,11 +37,7 @@ func favoritesDocumentTitle(data favoritesRenderData) string {
3837 }
3938
4039 func favoritesSummary(data favoritesRenderData) string {
41- return fmt.Sprintf("%d favorites · %d folders · %d tags", len(data.Favorites), len(data.Folders), len(data.Tags))
42-}
43-
44-func foldersSummary(data favoritesRenderData) string {
45- return fmt.Sprintf("%d folders", len(data.Folders))
40+ return fmt.Sprintf("%d favorites · %d tags", len(data.Favorites), len(data.Tags))
4641 }
4742
4843 func tagsSummary(data favoritesRenderData) string {
@@ -57,12 +52,12 @@ func favoriteFilterClass(active bool) string {
5752 return classes
5853 }
5954
60-func favoriteFolderFilterActive(data favoritesRenderData, folderID entity.FolderID) bool {
61- return data.FolderFilter != nil && *data.FolderFilter == folderID
55+func favoriteUntaggedFilterActive(data favoritesRenderData) bool {
56+ return data.UntaggedFilter
6257 }
6358
6459 func favoriteAllFilterActive(data favoritesRenderData) bool {
65- return data.FolderFilter == nil && data.TagFilter == nil
60+ return data.TagFilter == nil && !data.UntaggedFilter
6661 }
6762
6863 func favoriteTagFilterActive(data favoritesRenderData, tagID entity.TagID) bool {
@@ -80,18 +75,11 @@ func favoriteItemLabel(favorite *entity.Favorite) string {
8075 return label
8176 }
8277
83-func favoriteMetaText(favorite *entity.Favorite, folders []*entity.Folder) string {
84- if favorite == nil {
78+func favoriteMetaText(favorite *entity.Favorite) string {
79+ if favorite == nil || favorite.ShortcutKey == nil {
8580 return ""
8681 }
87- parts := make([]string, 0, 2)
88- if favorite.ShortcutKey != nil {
89- parts = append(parts, fmt.Sprintf("Shortcut %d", *favorite.ShortcutKey))
90- }
91- if name := folderNameForID(folders, favorite.FolderID); name != "" {
92- parts = append(parts, "Folder "+name)
93- }
94- return strings.Join(parts, " · ")
82+ return fmt.Sprintf("Shortcut %d", *favorite.ShortcutKey)
9583 }
9684
9785 func favoriteTagButtonAction(favorite *entity.Favorite, tag *entity.Tag) string {
@@ -136,38 +124,12 @@ func safeTagColor(raw string) string {
136124 return value
137125 }
138126
139-func folderDisplayName(folder *entity.Folder) string {
140- if folder == nil {
141- return ""
142- }
143- if strings.TrimSpace(folder.Icon) != "" {
144- return folder.Icon + " " + folder.Name
145- }
146- return folder.Name
147-}
148-
149-func folderNameForID(folders []*entity.Folder, id *entity.FolderID) string {
150- if id == nil {
151- return "Unfiled"
152- }
153- for _, folder := range folders {
154- if folder != nil && folder.ID == *id {
155- return folderDisplayName(folder)
156- }
157- }
158- return "Unknown"
159-}
160-
161-func folderSelected(selected *entity.FolderID, folderID entity.FolderID) bool {
162- return selected != nil && *selected == folderID
163-}
164-
165127 func shortcutSelected(selected *int, value int) bool {
166128 return selected != nil && *selected == value
167129 }
168130
169-func filterFavorites(favorites []*entity.Favorite, folderID *entity.FolderID, tagID *entity.TagID) []*entity.Favorite {
170- if folderID == nil && tagID == nil {
131+func filterFavorites(favorites []*entity.Favorite, tagID *entity.TagID, untagged bool) []*entity.Favorite {
132+ if tagID == nil && !untagged {
171133 return favorites
172134 }
173135 out := make([]*entity.Favorite, 0, len(favorites))
@@ -175,16 +137,11 @@ func filterFavorites(favorites []*entity.Favorite, folderID *entity.FolderID, ta
175137 if favorite == nil {
176138 continue
177139 }
178- if folderID != nil {
179- if *folderID == 0 {
180- if favorite.FolderID != nil {
181- continue
182- }
183- } else if favorite.FolderID == nil || *favorite.FolderID != *folderID {
140+ if untagged {
141+ if len(favorite.Tags) != 0 {
184142 continue
185143 }
186- }
187- if tagID != nil && !favorite.HasTag(*tagID) {
144+ } else if tagID != nil && !favorite.HasTag(*tagID) {
188145 continue
189146 }
190147 out = append(out, favorite)
@@ -10,13 +10,12 @@ import (
10 const defaultTagColor = "#808080"10 const defaultTagColor = "#808080"
11 11
12 type favoritesRenderData struct {12 type favoritesRenderData struct {
13- Favorites []*entity.Favorite13+ Favorites []*entity.Favorite
14- Folders []*entity.Folder14+ Tags []*entity.Tag
15- Tags []*entity.Tag15+ TagFilter *entity.TagID
16- FolderFilter *entity.FolderID16+ UntaggedFilter bool
17- TagFilter *entity.TagID17+ Notice string
18- Notice string18+ Error string
19- Error string
20 }19 }
21 20
22 func favoritesHTML(data favoritesRenderData) string {21 func favoritesHTML(data favoritesRenderData) string {
@@ -25,7 +24,7 @@ func favoritesHTML(data favoritesRenderData) string {
25 24
26 func favoritesDocumentTitle(data favoritesRenderData) string {25 func favoritesDocumentTitle(data favoritesRenderData) string {
27 count := len(data.Favorites)26 count := len(data.Favorites)
28- if data.FolderFilter != nil || data.TagFilter != nil {27+ if data.TagFilter != nil || data.UntaggedFilter {
29 if count == 1 {28 if count == 1 {
30 return "Favorites — 1 filtered bookmark"29 return "Favorites — 1 filtered bookmark"
31 }30 }
@@ -38,11 +37,7 @@ func favoritesDocumentTitle(data favoritesRenderData) string {
38 }37 }
39 38
40 func favoritesSummary(data favoritesRenderData) string {39 func favoritesSummary(data favoritesRenderData) string {
41- return fmt.Sprintf("%d favorites · %d folders · %d tags", len(data.Favorites), len(data.Folders), len(data.Tags))40+ return fmt.Sprintf("%d favorites · %d tags", len(data.Favorites), len(data.Tags))
42-}
43-
44-func foldersSummary(data favoritesRenderData) string {
45- return fmt.Sprintf("%d folders", len(data.Folders))
46 }41 }
47 42
48 func tagsSummary(data favoritesRenderData) string {43 func tagsSummary(data favoritesRenderData) string {
@@ -57,12 +52,12 @@ func favoriteFilterClass(active bool) string {
57 return classes52 return classes
58 }53 }
59 54
60-func favoriteFolderFilterActive(data favoritesRenderData, folderID entity.FolderID) bool {55+func favoriteUntaggedFilterActive(data favoritesRenderData) bool {
61- return data.FolderFilter != nil && *data.FolderFilter == folderID56+ return data.UntaggedFilter
62 }57 }
63 58
64 func favoriteAllFilterActive(data favoritesRenderData) bool {59 func favoriteAllFilterActive(data favoritesRenderData) bool {
65- return data.FolderFilter == nil && data.TagFilter == nil60+ return data.TagFilter == nil && !data.UntaggedFilter
66 }61 }
67 62
68 func favoriteTagFilterActive(data favoritesRenderData, tagID entity.TagID) bool {63 func favoriteTagFilterActive(data favoritesRenderData, tagID entity.TagID) bool {
@@ -80,18 +75,11 @@ func favoriteItemLabel(favorite *entity.Favorite) string {
80 return label75 return label
81 }76 }
82 77
83-func favoriteMetaText(favorite *entity.Favorite, folders []*entity.Folder) string {78+func favoriteMetaText(favorite *entity.Favorite) string {
84- if favorite == nil {79+ if favorite == nil || favorite.ShortcutKey == nil {
85 return ""80 return ""
86 }81 }
87- parts := make([]string, 0, 2)82+ return fmt.Sprintf("Shortcut %d", *favorite.ShortcutKey)
88- if favorite.ShortcutKey != nil {
89- parts = append(parts, fmt.Sprintf("Shortcut %d", *favorite.ShortcutKey))
90- }
91- if name := folderNameForID(folders, favorite.FolderID); name != "" {
92- parts = append(parts, "Folder "+name)
93- }
94- return strings.Join(parts, " · ")
95 }83 }
96 84
97 func favoriteTagButtonAction(favorite *entity.Favorite, tag *entity.Tag) string {85 func favoriteTagButtonAction(favorite *entity.Favorite, tag *entity.Tag) string {
@@ -136,38 +124,12 @@ func safeTagColor(raw string) string {
136 return value124 return value
137 }125 }
138 126
139-func folderDisplayName(folder *entity.Folder) string {
140- if folder == nil {
141- return ""
142- }
143- if strings.TrimSpace(folder.Icon) != "" {
144- return folder.Icon + " " + folder.Name
145- }
146- return folder.Name
147-}
148-
149-func folderNameForID(folders []*entity.Folder, id *entity.FolderID) string {
150- if id == nil {
151- return "Unfiled"
152- }
153- for _, folder := range folders {
154- if folder != nil && folder.ID == *id {
155- return folderDisplayName(folder)
156- }
157- }
158- return "Unknown"
159-}
160-
161-func folderSelected(selected *entity.FolderID, folderID entity.FolderID) bool {
162- return selected != nil && *selected == folderID
163-}
164-
165 func shortcutSelected(selected *int, value int) bool {127 func shortcutSelected(selected *int, value int) bool {
166 return selected != nil && *selected == value128 return selected != nil && *selected == value
167 }129 }
168 130
169-func filterFavorites(favorites []*entity.Favorite, folderID *entity.FolderID, tagID *entity.TagID) []*entity.Favorite {131+func filterFavorites(favorites []*entity.Favorite, tagID *entity.TagID, untagged bool) []*entity.Favorite {
170- if folderID == nil && tagID == nil {132+ if tagID == nil && !untagged {
171 return favorites133 return favorites
172 }134 }
173 out := make([]*entity.Favorite, 0, len(favorites))135 out := make([]*entity.Favorite, 0, len(favorites))
@@ -175,16 +137,11 @@ func filterFavorites(favorites []*entity.Favorite, folderID *entity.FolderID, ta
175 if favorite == nil {137 if favorite == nil {
176 continue138 continue
177 }139 }
178- if folderID != nil {140+ if untagged {
179- if *folderID == 0 {141+ if len(favorite.Tags) != 0 {
180- if favorite.FolderID != nil {
181- continue
182- }
183- } else if favorite.FolderID == nil || *favorite.FolderID != *folderID {
184 continue142 continue
185 }143 }
186- }144+ } else if tagID != nil && !favorite.HasTag(*tagID) {
187- if tagID != nil && !favorite.HasTag(*tagID) {
188 continue145 continue
189 }146 }
190 out = append(out, favorite)147 out = append(out, favorite)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close