detached e68eda9

fix(parser): support both {query} and %s placeholders in search shortcuts

Search shortcuts now work with both placeholder formats:
- {query} - used in test configurations
- %s - used in production configurations

This allows users to define shortcuts in either format without breaking
functionality.

Also fixes domain-only URL detection (e.g., "github.com") which were
incorrectly treated as search queries instead of direct navigation.

Changes:
- Support both placeholder styles via strings.ReplaceAll
- Populate DetectedShortcut metadata (key, query, URL, description)
- Prioritize scheme detection over search query heuristics
- Regenerate database mocks to include favorites methods
commit
e68eda9215f57965afb36e11ff159fe76f2a6a7a
author
bnema · · 2025-11-17 20:56 UTC
committer
bnema · · 2025-11-17 21:03 UTC
parent
b3a6cbe
signature
unsigned
2 files · +275 -10
M internal/db/mocks/mock_db.go +256 -0
@@ -337,6 +337,146 @@ func (mr *MockCertificateQuerierMockRecorder) StoreCertificateValidation(ctx, ho
337337 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreCertificateValidation", reflect.TypeOf((*MockCertificateQuerier)(nil).StoreCertificateValidation), ctx, hostname, certificateHash, userDecision, expiresAt)
338338 }
339339
340+// MockFavoritesQuerier is a mock of FavoritesQuerier interface.
341+type MockFavoritesQuerier struct {
342+ ctrl *gomock.Controller
343+ recorder *MockFavoritesQuerierMockRecorder
344+ isgomock struct{}
345+}
346+
347+// MockFavoritesQuerierMockRecorder is the mock recorder for MockFavoritesQuerier.
348+type MockFavoritesQuerierMockRecorder struct {
349+ mock *MockFavoritesQuerier
350+}
351+
352+// NewMockFavoritesQuerier creates a new mock instance.
353+func NewMockFavoritesQuerier(ctrl *gomock.Controller) *MockFavoritesQuerier {
354+ mock := &MockFavoritesQuerier{ctrl: ctrl}
355+ mock.recorder = &MockFavoritesQuerierMockRecorder{mock}
356+ return mock
357+}
358+
359+// EXPECT returns an object that allows the caller to indicate expected use.
360+func (m *MockFavoritesQuerier) EXPECT() *MockFavoritesQuerierMockRecorder {
361+ return m.recorder
362+}
363+
364+// CreateFavorite mocks base method.
365+func (m *MockFavoritesQuerier) CreateFavorite(ctx context.Context, url string, title, faviconUrl sql.NullString) error {
366+ m.ctrl.T.Helper()
367+ ret := m.ctrl.Call(m, "CreateFavorite", ctx, url, title, faviconUrl)
368+ ret0, _ := ret[0].(error)
369+ return ret0
370+}
371+
372+// CreateFavorite indicates an expected call of CreateFavorite.
373+func (mr *MockFavoritesQuerierMockRecorder) CreateFavorite(ctx, url, title, faviconUrl any) *gomock.Call {
374+ mr.mock.ctrl.T.Helper()
375+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFavorite", reflect.TypeOf((*MockFavoritesQuerier)(nil).CreateFavorite), ctx, url, title, faviconUrl)
376+}
377+
378+// DeleteFavorite mocks base method.
379+func (m *MockFavoritesQuerier) DeleteFavorite(ctx context.Context, url string) error {
380+ m.ctrl.T.Helper()
381+ ret := m.ctrl.Call(m, "DeleteFavorite", ctx, url)
382+ ret0, _ := ret[0].(error)
383+ return ret0
384+}
385+
386+// DeleteFavorite indicates an expected call of DeleteFavorite.
387+func (mr *MockFavoritesQuerierMockRecorder) DeleteFavorite(ctx, url any) *gomock.Call {
388+ mr.mock.ctrl.T.Helper()
389+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFavorite", reflect.TypeOf((*MockFavoritesQuerier)(nil).DeleteFavorite), ctx, url)
390+}
391+
392+// GetAllFavorites mocks base method.
393+func (m *MockFavoritesQuerier) GetAllFavorites(ctx context.Context) ([]db.Favorite, error) {
394+ m.ctrl.T.Helper()
395+ ret := m.ctrl.Call(m, "GetAllFavorites", ctx)
396+ ret0, _ := ret[0].([]db.Favorite)
397+ ret1, _ := ret[1].(error)
398+ return ret0, ret1
399+}
400+
401+// GetAllFavorites indicates an expected call of GetAllFavorites.
402+func (mr *MockFavoritesQuerierMockRecorder) GetAllFavorites(ctx any) *gomock.Call {
403+ mr.mock.ctrl.T.Helper()
404+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllFavorites", reflect.TypeOf((*MockFavoritesQuerier)(nil).GetAllFavorites), ctx)
405+}
406+
407+// GetFavoriteByURL mocks base method.
408+func (m *MockFavoritesQuerier) GetFavoriteByURL(ctx context.Context, url string) (db.Favorite, error) {
409+ m.ctrl.T.Helper()
410+ ret := m.ctrl.Call(m, "GetFavoriteByURL", ctx, url)
411+ ret0, _ := ret[0].(db.Favorite)
412+ ret1, _ := ret[1].(error)
413+ return ret0, ret1
414+}
415+
416+// GetFavoriteByURL indicates an expected call of GetFavoriteByURL.
417+func (mr *MockFavoritesQuerierMockRecorder) GetFavoriteByURL(ctx, url any) *gomock.Call {
418+ mr.mock.ctrl.T.Helper()
419+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFavoriteByURL", reflect.TypeOf((*MockFavoritesQuerier)(nil).GetFavoriteByURL), ctx, url)
420+}
421+
422+// GetFavoriteCount mocks base method.
423+func (m *MockFavoritesQuerier) GetFavoriteCount(ctx context.Context) (int64, error) {
424+ m.ctrl.T.Helper()
425+ ret := m.ctrl.Call(m, "GetFavoriteCount", ctx)
426+ ret0, _ := ret[0].(int64)
427+ ret1, _ := ret[1].(error)
428+ return ret0, ret1
429+}
430+
431+// GetFavoriteCount indicates an expected call of GetFavoriteCount.
432+func (mr *MockFavoritesQuerierMockRecorder) GetFavoriteCount(ctx any) *gomock.Call {
433+ mr.mock.ctrl.T.Helper()
434+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFavoriteCount", reflect.TypeOf((*MockFavoritesQuerier)(nil).GetFavoriteCount), ctx)
435+}
436+
437+// IsFavorite mocks base method.
438+func (m *MockFavoritesQuerier) IsFavorite(ctx context.Context, url string) (int64, error) {
439+ m.ctrl.T.Helper()
440+ ret := m.ctrl.Call(m, "IsFavorite", ctx, url)
441+ ret0, _ := ret[0].(int64)
442+ ret1, _ := ret[1].(error)
443+ return ret0, ret1
444+}
445+
446+// IsFavorite indicates an expected call of IsFavorite.
447+func (mr *MockFavoritesQuerierMockRecorder) IsFavorite(ctx, url any) *gomock.Call {
448+ mr.mock.ctrl.T.Helper()
449+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsFavorite", reflect.TypeOf((*MockFavoritesQuerier)(nil).IsFavorite), ctx, url)
450+}
451+
452+// UpdateFavorite mocks base method.
453+func (m *MockFavoritesQuerier) UpdateFavorite(ctx context.Context, title, faviconUrl sql.NullString, url string) error {
454+ m.ctrl.T.Helper()
455+ ret := m.ctrl.Call(m, "UpdateFavorite", ctx, title, faviconUrl, url)
456+ ret0, _ := ret[0].(error)
457+ return ret0
458+}
459+
460+// UpdateFavorite indicates an expected call of UpdateFavorite.
461+func (mr *MockFavoritesQuerierMockRecorder) UpdateFavorite(ctx, title, faviconUrl, url any) *gomock.Call {
462+ mr.mock.ctrl.T.Helper()
463+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFavorite", reflect.TypeOf((*MockFavoritesQuerier)(nil).UpdateFavorite), ctx, title, faviconUrl, url)
464+}
465+
466+// UpdateFavoritePosition mocks base method.
467+func (m *MockFavoritesQuerier) UpdateFavoritePosition(ctx context.Context, position int64, url string) error {
468+ m.ctrl.T.Helper()
469+ ret := m.ctrl.Call(m, "UpdateFavoritePosition", ctx, position, url)
470+ ret0, _ := ret[0].(error)
471+ return ret0
472+}
473+
474+// UpdateFavoritePosition indicates an expected call of UpdateFavoritePosition.
475+func (mr *MockFavoritesQuerierMockRecorder) UpdateFavoritePosition(ctx, position, url any) *gomock.Call {
476+ mr.mock.ctrl.T.Helper()
477+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFavoritePosition", reflect.TypeOf((*MockFavoritesQuerier)(nil).UpdateFavoritePosition), ctx, position, url)
478+}
479+
340480 // MockDatabaseQuerier is a mock of DatabaseQuerier interface.
341481 type MockDatabaseQuerier struct {
342482 ctrl *gomock.Controller
@@ -375,6 +515,20 @@ func (mr *MockDatabaseQuerierMockRecorder) AddOrUpdateHistory(ctx, url, title an
375515 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddOrUpdateHistory", reflect.TypeOf((*MockDatabaseQuerier)(nil).AddOrUpdateHistory), ctx, url, title)
376516 }
377517
518+// CreateFavorite mocks base method.
519+func (m *MockDatabaseQuerier) CreateFavorite(ctx context.Context, url string, title, faviconUrl sql.NullString) error {
520+ m.ctrl.T.Helper()
521+ ret := m.ctrl.Call(m, "CreateFavorite", ctx, url, title, faviconUrl)
522+ ret0, _ := ret[0].(error)
523+ return ret0
524+}
525+
526+// CreateFavorite indicates an expected call of CreateFavorite.
527+func (mr *MockDatabaseQuerierMockRecorder) CreateFavorite(ctx, url, title, faviconUrl any) *gomock.Call {
528+ mr.mock.ctrl.T.Helper()
529+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFavorite", reflect.TypeOf((*MockDatabaseQuerier)(nil).CreateFavorite), ctx, url, title, faviconUrl)
530+}
531+
378532 // DeleteCertificateValidation mocks base method.
379533 func (m *MockDatabaseQuerier) DeleteCertificateValidation(ctx context.Context, hostname, certificateHash string) error {
380534 m.ctrl.T.Helper()
@@ -403,6 +557,20 @@ func (mr *MockDatabaseQuerierMockRecorder) DeleteExpiredCertificateValidations(c
403557 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteExpiredCertificateValidations", reflect.TypeOf((*MockDatabaseQuerier)(nil).DeleteExpiredCertificateValidations), ctx)
404558 }
405559
560+// DeleteFavorite mocks base method.
561+func (m *MockDatabaseQuerier) DeleteFavorite(ctx context.Context, url string) error {
562+ m.ctrl.T.Helper()
563+ ret := m.ctrl.Call(m, "DeleteFavorite", ctx, url)
564+ ret0, _ := ret[0].(error)
565+ return ret0
566+}
567+
568+// DeleteFavorite indicates an expected call of DeleteFavorite.
569+func (mr *MockDatabaseQuerierMockRecorder) DeleteFavorite(ctx, url any) *gomock.Call {
570+ mr.mock.ctrl.T.Helper()
571+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFavorite", reflect.TypeOf((*MockDatabaseQuerier)(nil).DeleteFavorite), ctx, url)
572+}
573+
406574 // DeleteHistory mocks base method.
407575 func (m *MockDatabaseQuerier) DeleteHistory(ctx context.Context, id int64) error {
408576 m.ctrl.T.Helper()
@@ -431,6 +599,21 @@ func (mr *MockDatabaseQuerierMockRecorder) DeleteZoomLevel(ctx, domain any) *gom
431599 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteZoomLevel", reflect.TypeOf((*MockDatabaseQuerier)(nil).DeleteZoomLevel), ctx, domain)
432600 }
433601
602+// GetAllFavorites mocks base method.
603+func (m *MockDatabaseQuerier) GetAllFavorites(ctx context.Context) ([]db.Favorite, error) {
604+ m.ctrl.T.Helper()
605+ ret := m.ctrl.Call(m, "GetAllFavorites", ctx)
606+ ret0, _ := ret[0].([]db.Favorite)
607+ ret1, _ := ret[1].(error)
608+ return ret0, ret1
609+}
610+
611+// GetAllFavorites indicates an expected call of GetAllFavorites.
612+func (mr *MockDatabaseQuerierMockRecorder) GetAllFavorites(ctx any) *gomock.Call {
613+ mr.mock.ctrl.T.Helper()
614+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllFavorites", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetAllFavorites), ctx)
615+}
616+
434617 // GetCertificateValidation mocks base method.
435618 func (m *MockDatabaseQuerier) GetCertificateValidation(ctx context.Context, hostname, certificateHash string) (db.CertificateValidation, error) {
436619 m.ctrl.T.Helper()
@@ -461,6 +644,36 @@ func (mr *MockDatabaseQuerierMockRecorder) GetCertificateValidationByHostname(ct
461644 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCertificateValidationByHostname", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetCertificateValidationByHostname), ctx, hostname)
462645 }
463646
647+// GetFavoriteByURL mocks base method.
648+func (m *MockDatabaseQuerier) GetFavoriteByURL(ctx context.Context, url string) (db.Favorite, error) {
649+ m.ctrl.T.Helper()
650+ ret := m.ctrl.Call(m, "GetFavoriteByURL", ctx, url)
651+ ret0, _ := ret[0].(db.Favorite)
652+ ret1, _ := ret[1].(error)
653+ return ret0, ret1
654+}
655+
656+// GetFavoriteByURL indicates an expected call of GetFavoriteByURL.
657+func (mr *MockDatabaseQuerierMockRecorder) GetFavoriteByURL(ctx, url any) *gomock.Call {
658+ mr.mock.ctrl.T.Helper()
659+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFavoriteByURL", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetFavoriteByURL), ctx, url)
660+}
661+
662+// GetFavoriteCount mocks base method.
663+func (m *MockDatabaseQuerier) GetFavoriteCount(ctx context.Context) (int64, error) {
664+ m.ctrl.T.Helper()
665+ ret := m.ctrl.Call(m, "GetFavoriteCount", ctx)
666+ ret0, _ := ret[0].(int64)
667+ ret1, _ := ret[1].(error)
668+ return ret0, ret1
669+}
670+
671+// GetFavoriteCount indicates an expected call of GetFavoriteCount.
672+func (mr *MockDatabaseQuerierMockRecorder) GetFavoriteCount(ctx any) *gomock.Call {
673+ mr.mock.ctrl.T.Helper()
674+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFavoriteCount", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetFavoriteCount), ctx)
675+}
676+
464677 // GetHistory mocks base method.
465678 func (m *MockDatabaseQuerier) GetHistory(ctx context.Context, limit int64) ([]db.History, error) {
466679 m.ctrl.T.Helper()
@@ -521,6 +734,21 @@ func (mr *MockDatabaseQuerierMockRecorder) GetZoomLevel(ctx, domain any) *gomock
521734 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetZoomLevel", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetZoomLevel), ctx, domain)
522735 }
523736
737+// IsFavorite mocks base method.
738+func (m *MockDatabaseQuerier) IsFavorite(ctx context.Context, url string) (int64, error) {
739+ m.ctrl.T.Helper()
740+ ret := m.ctrl.Call(m, "IsFavorite", ctx, url)
741+ ret0, _ := ret[0].(int64)
742+ ret1, _ := ret[1].(error)
743+ return ret0, ret1
744+}
745+
746+// IsFavorite indicates an expected call of IsFavorite.
747+func (mr *MockDatabaseQuerierMockRecorder) IsFavorite(ctx, url any) *gomock.Call {
748+ mr.mock.ctrl.T.Helper()
749+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsFavorite", reflect.TypeOf((*MockDatabaseQuerier)(nil).IsFavorite), ctx, url)
750+}
751+
524752 // ListCertificateValidations mocks base method.
525753 func (m *MockDatabaseQuerier) ListCertificateValidations(ctx context.Context) ([]db.CertificateValidation, error) {
526754 m.ctrl.T.Helper()
@@ -594,6 +822,34 @@ func (mr *MockDatabaseQuerierMockRecorder) StoreCertificateValidation(ctx, hostn
594822 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreCertificateValidation", reflect.TypeOf((*MockDatabaseQuerier)(nil).StoreCertificateValidation), ctx, hostname, certificateHash, userDecision, expiresAt)
595823 }
596824
825+// UpdateFavorite mocks base method.
826+func (m *MockDatabaseQuerier) UpdateFavorite(ctx context.Context, title, faviconUrl sql.NullString, url string) error {
827+ m.ctrl.T.Helper()
828+ ret := m.ctrl.Call(m, "UpdateFavorite", ctx, title, faviconUrl, url)
829+ ret0, _ := ret[0].(error)
830+ return ret0
831+}
832+
833+// UpdateFavorite indicates an expected call of UpdateFavorite.
834+func (mr *MockDatabaseQuerierMockRecorder) UpdateFavorite(ctx, title, faviconUrl, url any) *gomock.Call {
835+ mr.mock.ctrl.T.Helper()
836+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFavorite", reflect.TypeOf((*MockDatabaseQuerier)(nil).UpdateFavorite), ctx, title, faviconUrl, url)
837+}
838+
839+// UpdateFavoritePosition mocks base method.
840+func (m *MockDatabaseQuerier) UpdateFavoritePosition(ctx context.Context, position int64, url string) error {
841+ m.ctrl.T.Helper()
842+ ret := m.ctrl.Call(m, "UpdateFavoritePosition", ctx, position, url)
843+ ret0, _ := ret[0].(error)
844+ return ret0
845+}
846+
847+// UpdateFavoritePosition indicates an expected call of UpdateFavoritePosition.
848+func (mr *MockDatabaseQuerierMockRecorder) UpdateFavoritePosition(ctx, position, url any) *gomock.Call {
849+ mr.mock.ctrl.T.Helper()
850+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFavoritePosition", reflect.TypeOf((*MockDatabaseQuerier)(nil).UpdateFavoritePosition), ctx, position, url)
851+}
852+
597853 // UpdateHistoryFavicon mocks base method.
598854 func (m *MockDatabaseQuerier) UpdateHistoryFavicon(ctx context.Context, faviconUrl sql.NullString, url string) error {
599855 m.ctrl.T.Helper()
@@ -337,6 +337,146 @@ func (mr *MockCertificateQuerierMockRecorder) StoreCertificateValidation(ctx, ho
337 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreCertificateValidation", reflect.TypeOf((*MockCertificateQuerier)(nil).StoreCertificateValidation), ctx, hostname, certificateHash, userDecision, expiresAt)337 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreCertificateValidation", reflect.TypeOf((*MockCertificateQuerier)(nil).StoreCertificateValidation), ctx, hostname, certificateHash, userDecision, expiresAt)
338 }338 }
339 339
340+// MockFavoritesQuerier is a mock of FavoritesQuerier interface.
341+type MockFavoritesQuerier struct {
342+ ctrl *gomock.Controller
343+ recorder *MockFavoritesQuerierMockRecorder
344+ isgomock struct{}
345+}
346+
347+// MockFavoritesQuerierMockRecorder is the mock recorder for MockFavoritesQuerier.
348+type MockFavoritesQuerierMockRecorder struct {
349+ mock *MockFavoritesQuerier
350+}
351+
352+// NewMockFavoritesQuerier creates a new mock instance.
353+func NewMockFavoritesQuerier(ctrl *gomock.Controller) *MockFavoritesQuerier {
354+ mock := &MockFavoritesQuerier{ctrl: ctrl}
355+ mock.recorder = &MockFavoritesQuerierMockRecorder{mock}
356+ return mock
357+}
358+
359+// EXPECT returns an object that allows the caller to indicate expected use.
360+func (m *MockFavoritesQuerier) EXPECT() *MockFavoritesQuerierMockRecorder {
361+ return m.recorder
362+}
363+
364+// CreateFavorite mocks base method.
365+func (m *MockFavoritesQuerier) CreateFavorite(ctx context.Context, url string, title, faviconUrl sql.NullString) error {
366+ m.ctrl.T.Helper()
367+ ret := m.ctrl.Call(m, "CreateFavorite", ctx, url, title, faviconUrl)
368+ ret0, _ := ret[0].(error)
369+ return ret0
370+}
371+
372+// CreateFavorite indicates an expected call of CreateFavorite.
373+func (mr *MockFavoritesQuerierMockRecorder) CreateFavorite(ctx, url, title, faviconUrl any) *gomock.Call {
374+ mr.mock.ctrl.T.Helper()
375+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFavorite", reflect.TypeOf((*MockFavoritesQuerier)(nil).CreateFavorite), ctx, url, title, faviconUrl)
376+}
377+
378+// DeleteFavorite mocks base method.
379+func (m *MockFavoritesQuerier) DeleteFavorite(ctx context.Context, url string) error {
380+ m.ctrl.T.Helper()
381+ ret := m.ctrl.Call(m, "DeleteFavorite", ctx, url)
382+ ret0, _ := ret[0].(error)
383+ return ret0
384+}
385+
386+// DeleteFavorite indicates an expected call of DeleteFavorite.
387+func (mr *MockFavoritesQuerierMockRecorder) DeleteFavorite(ctx, url any) *gomock.Call {
388+ mr.mock.ctrl.T.Helper()
389+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFavorite", reflect.TypeOf((*MockFavoritesQuerier)(nil).DeleteFavorite), ctx, url)
390+}
391+
392+// GetAllFavorites mocks base method.
393+func (m *MockFavoritesQuerier) GetAllFavorites(ctx context.Context) ([]db.Favorite, error) {
394+ m.ctrl.T.Helper()
395+ ret := m.ctrl.Call(m, "GetAllFavorites", ctx)
396+ ret0, _ := ret[0].([]db.Favorite)
397+ ret1, _ := ret[1].(error)
398+ return ret0, ret1
399+}
400+
401+// GetAllFavorites indicates an expected call of GetAllFavorites.
402+func (mr *MockFavoritesQuerierMockRecorder) GetAllFavorites(ctx any) *gomock.Call {
403+ mr.mock.ctrl.T.Helper()
404+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllFavorites", reflect.TypeOf((*MockFavoritesQuerier)(nil).GetAllFavorites), ctx)
405+}
406+
407+// GetFavoriteByURL mocks base method.
408+func (m *MockFavoritesQuerier) GetFavoriteByURL(ctx context.Context, url string) (db.Favorite, error) {
409+ m.ctrl.T.Helper()
410+ ret := m.ctrl.Call(m, "GetFavoriteByURL", ctx, url)
411+ ret0, _ := ret[0].(db.Favorite)
412+ ret1, _ := ret[1].(error)
413+ return ret0, ret1
414+}
415+
416+// GetFavoriteByURL indicates an expected call of GetFavoriteByURL.
417+func (mr *MockFavoritesQuerierMockRecorder) GetFavoriteByURL(ctx, url any) *gomock.Call {
418+ mr.mock.ctrl.T.Helper()
419+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFavoriteByURL", reflect.TypeOf((*MockFavoritesQuerier)(nil).GetFavoriteByURL), ctx, url)
420+}
421+
422+// GetFavoriteCount mocks base method.
423+func (m *MockFavoritesQuerier) GetFavoriteCount(ctx context.Context) (int64, error) {
424+ m.ctrl.T.Helper()
425+ ret := m.ctrl.Call(m, "GetFavoriteCount", ctx)
426+ ret0, _ := ret[0].(int64)
427+ ret1, _ := ret[1].(error)
428+ return ret0, ret1
429+}
430+
431+// GetFavoriteCount indicates an expected call of GetFavoriteCount.
432+func (mr *MockFavoritesQuerierMockRecorder) GetFavoriteCount(ctx any) *gomock.Call {
433+ mr.mock.ctrl.T.Helper()
434+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFavoriteCount", reflect.TypeOf((*MockFavoritesQuerier)(nil).GetFavoriteCount), ctx)
435+}
436+
437+// IsFavorite mocks base method.
438+func (m *MockFavoritesQuerier) IsFavorite(ctx context.Context, url string) (int64, error) {
439+ m.ctrl.T.Helper()
440+ ret := m.ctrl.Call(m, "IsFavorite", ctx, url)
441+ ret0, _ := ret[0].(int64)
442+ ret1, _ := ret[1].(error)
443+ return ret0, ret1
444+}
445+
446+// IsFavorite indicates an expected call of IsFavorite.
447+func (mr *MockFavoritesQuerierMockRecorder) IsFavorite(ctx, url any) *gomock.Call {
448+ mr.mock.ctrl.T.Helper()
449+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsFavorite", reflect.TypeOf((*MockFavoritesQuerier)(nil).IsFavorite), ctx, url)
450+}
451+
452+// UpdateFavorite mocks base method.
453+func (m *MockFavoritesQuerier) UpdateFavorite(ctx context.Context, title, faviconUrl sql.NullString, url string) error {
454+ m.ctrl.T.Helper()
455+ ret := m.ctrl.Call(m, "UpdateFavorite", ctx, title, faviconUrl, url)
456+ ret0, _ := ret[0].(error)
457+ return ret0
458+}
459+
460+// UpdateFavorite indicates an expected call of UpdateFavorite.
461+func (mr *MockFavoritesQuerierMockRecorder) UpdateFavorite(ctx, title, faviconUrl, url any) *gomock.Call {
462+ mr.mock.ctrl.T.Helper()
463+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFavorite", reflect.TypeOf((*MockFavoritesQuerier)(nil).UpdateFavorite), ctx, title, faviconUrl, url)
464+}
465+
466+// UpdateFavoritePosition mocks base method.
467+func (m *MockFavoritesQuerier) UpdateFavoritePosition(ctx context.Context, position int64, url string) error {
468+ m.ctrl.T.Helper()
469+ ret := m.ctrl.Call(m, "UpdateFavoritePosition", ctx, position, url)
470+ ret0, _ := ret[0].(error)
471+ return ret0
472+}
473+
474+// UpdateFavoritePosition indicates an expected call of UpdateFavoritePosition.
475+func (mr *MockFavoritesQuerierMockRecorder) UpdateFavoritePosition(ctx, position, url any) *gomock.Call {
476+ mr.mock.ctrl.T.Helper()
477+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFavoritePosition", reflect.TypeOf((*MockFavoritesQuerier)(nil).UpdateFavoritePosition), ctx, position, url)
478+}
479+
340 // MockDatabaseQuerier is a mock of DatabaseQuerier interface.480 // MockDatabaseQuerier is a mock of DatabaseQuerier interface.
341 type MockDatabaseQuerier struct {481 type MockDatabaseQuerier struct {
342 ctrl *gomock.Controller482 ctrl *gomock.Controller
@@ -375,6 +515,20 @@ func (mr *MockDatabaseQuerierMockRecorder) AddOrUpdateHistory(ctx, url, title an
375 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddOrUpdateHistory", reflect.TypeOf((*MockDatabaseQuerier)(nil).AddOrUpdateHistory), ctx, url, title)515 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddOrUpdateHistory", reflect.TypeOf((*MockDatabaseQuerier)(nil).AddOrUpdateHistory), ctx, url, title)
376 }516 }
377 517
518+// CreateFavorite mocks base method.
519+func (m *MockDatabaseQuerier) CreateFavorite(ctx context.Context, url string, title, faviconUrl sql.NullString) error {
520+ m.ctrl.T.Helper()
521+ ret := m.ctrl.Call(m, "CreateFavorite", ctx, url, title, faviconUrl)
522+ ret0, _ := ret[0].(error)
523+ return ret0
524+}
525+
526+// CreateFavorite indicates an expected call of CreateFavorite.
527+func (mr *MockDatabaseQuerierMockRecorder) CreateFavorite(ctx, url, title, faviconUrl any) *gomock.Call {
528+ mr.mock.ctrl.T.Helper()
529+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFavorite", reflect.TypeOf((*MockDatabaseQuerier)(nil).CreateFavorite), ctx, url, title, faviconUrl)
530+}
531+
378 // DeleteCertificateValidation mocks base method.532 // DeleteCertificateValidation mocks base method.
379 func (m *MockDatabaseQuerier) DeleteCertificateValidation(ctx context.Context, hostname, certificateHash string) error {533 func (m *MockDatabaseQuerier) DeleteCertificateValidation(ctx context.Context, hostname, certificateHash string) error {
380 m.ctrl.T.Helper()534 m.ctrl.T.Helper()
@@ -403,6 +557,20 @@ func (mr *MockDatabaseQuerierMockRecorder) DeleteExpiredCertificateValidations(c
403 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteExpiredCertificateValidations", reflect.TypeOf((*MockDatabaseQuerier)(nil).DeleteExpiredCertificateValidations), ctx)557 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteExpiredCertificateValidations", reflect.TypeOf((*MockDatabaseQuerier)(nil).DeleteExpiredCertificateValidations), ctx)
404 }558 }
405 559
560+// DeleteFavorite mocks base method.
561+func (m *MockDatabaseQuerier) DeleteFavorite(ctx context.Context, url string) error {
562+ m.ctrl.T.Helper()
563+ ret := m.ctrl.Call(m, "DeleteFavorite", ctx, url)
564+ ret0, _ := ret[0].(error)
565+ return ret0
566+}
567+
568+// DeleteFavorite indicates an expected call of DeleteFavorite.
569+func (mr *MockDatabaseQuerierMockRecorder) DeleteFavorite(ctx, url any) *gomock.Call {
570+ mr.mock.ctrl.T.Helper()
571+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFavorite", reflect.TypeOf((*MockDatabaseQuerier)(nil).DeleteFavorite), ctx, url)
572+}
573+
406 // DeleteHistory mocks base method.574 // DeleteHistory mocks base method.
407 func (m *MockDatabaseQuerier) DeleteHistory(ctx context.Context, id int64) error {575 func (m *MockDatabaseQuerier) DeleteHistory(ctx context.Context, id int64) error {
408 m.ctrl.T.Helper()576 m.ctrl.T.Helper()
@@ -431,6 +599,21 @@ func (mr *MockDatabaseQuerierMockRecorder) DeleteZoomLevel(ctx, domain any) *gom
431 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteZoomLevel", reflect.TypeOf((*MockDatabaseQuerier)(nil).DeleteZoomLevel), ctx, domain)599 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteZoomLevel", reflect.TypeOf((*MockDatabaseQuerier)(nil).DeleteZoomLevel), ctx, domain)
432 }600 }
433 601
602+// GetAllFavorites mocks base method.
603+func (m *MockDatabaseQuerier) GetAllFavorites(ctx context.Context) ([]db.Favorite, error) {
604+ m.ctrl.T.Helper()
605+ ret := m.ctrl.Call(m, "GetAllFavorites", ctx)
606+ ret0, _ := ret[0].([]db.Favorite)
607+ ret1, _ := ret[1].(error)
608+ return ret0, ret1
609+}
610+
611+// GetAllFavorites indicates an expected call of GetAllFavorites.
612+func (mr *MockDatabaseQuerierMockRecorder) GetAllFavorites(ctx any) *gomock.Call {
613+ mr.mock.ctrl.T.Helper()
614+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllFavorites", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetAllFavorites), ctx)
615+}
616+
434 // GetCertificateValidation mocks base method.617 // GetCertificateValidation mocks base method.
435 func (m *MockDatabaseQuerier) GetCertificateValidation(ctx context.Context, hostname, certificateHash string) (db.CertificateValidation, error) {618 func (m *MockDatabaseQuerier) GetCertificateValidation(ctx context.Context, hostname, certificateHash string) (db.CertificateValidation, error) {
436 m.ctrl.T.Helper()619 m.ctrl.T.Helper()
@@ -461,6 +644,36 @@ func (mr *MockDatabaseQuerierMockRecorder) GetCertificateValidationByHostname(ct
461 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCertificateValidationByHostname", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetCertificateValidationByHostname), ctx, hostname)644 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCertificateValidationByHostname", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetCertificateValidationByHostname), ctx, hostname)
462 }645 }
463 646
647+// GetFavoriteByURL mocks base method.
648+func (m *MockDatabaseQuerier) GetFavoriteByURL(ctx context.Context, url string) (db.Favorite, error) {
649+ m.ctrl.T.Helper()
650+ ret := m.ctrl.Call(m, "GetFavoriteByURL", ctx, url)
651+ ret0, _ := ret[0].(db.Favorite)
652+ ret1, _ := ret[1].(error)
653+ return ret0, ret1
654+}
655+
656+// GetFavoriteByURL indicates an expected call of GetFavoriteByURL.
657+func (mr *MockDatabaseQuerierMockRecorder) GetFavoriteByURL(ctx, url any) *gomock.Call {
658+ mr.mock.ctrl.T.Helper()
659+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFavoriteByURL", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetFavoriteByURL), ctx, url)
660+}
661+
662+// GetFavoriteCount mocks base method.
663+func (m *MockDatabaseQuerier) GetFavoriteCount(ctx context.Context) (int64, error) {
664+ m.ctrl.T.Helper()
665+ ret := m.ctrl.Call(m, "GetFavoriteCount", ctx)
666+ ret0, _ := ret[0].(int64)
667+ ret1, _ := ret[1].(error)
668+ return ret0, ret1
669+}
670+
671+// GetFavoriteCount indicates an expected call of GetFavoriteCount.
672+func (mr *MockDatabaseQuerierMockRecorder) GetFavoriteCount(ctx any) *gomock.Call {
673+ mr.mock.ctrl.T.Helper()
674+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFavoriteCount", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetFavoriteCount), ctx)
675+}
676+
464 // GetHistory mocks base method.677 // GetHistory mocks base method.
465 func (m *MockDatabaseQuerier) GetHistory(ctx context.Context, limit int64) ([]db.History, error) {678 func (m *MockDatabaseQuerier) GetHistory(ctx context.Context, limit int64) ([]db.History, error) {
466 m.ctrl.T.Helper()679 m.ctrl.T.Helper()
@@ -521,6 +734,21 @@ func (mr *MockDatabaseQuerierMockRecorder) GetZoomLevel(ctx, domain any) *gomock
521 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetZoomLevel", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetZoomLevel), ctx, domain)734 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetZoomLevel", reflect.TypeOf((*MockDatabaseQuerier)(nil).GetZoomLevel), ctx, domain)
522 }735 }
523 736
737+// IsFavorite mocks base method.
738+func (m *MockDatabaseQuerier) IsFavorite(ctx context.Context, url string) (int64, error) {
739+ m.ctrl.T.Helper()
740+ ret := m.ctrl.Call(m, "IsFavorite", ctx, url)
741+ ret0, _ := ret[0].(int64)
742+ ret1, _ := ret[1].(error)
743+ return ret0, ret1
744+}
745+
746+// IsFavorite indicates an expected call of IsFavorite.
747+func (mr *MockDatabaseQuerierMockRecorder) IsFavorite(ctx, url any) *gomock.Call {
748+ mr.mock.ctrl.T.Helper()
749+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsFavorite", reflect.TypeOf((*MockDatabaseQuerier)(nil).IsFavorite), ctx, url)
750+}
751+
524 // ListCertificateValidations mocks base method.752 // ListCertificateValidations mocks base method.
525 func (m *MockDatabaseQuerier) ListCertificateValidations(ctx context.Context) ([]db.CertificateValidation, error) {753 func (m *MockDatabaseQuerier) ListCertificateValidations(ctx context.Context) ([]db.CertificateValidation, error) {
526 m.ctrl.T.Helper()754 m.ctrl.T.Helper()
@@ -594,6 +822,34 @@ func (mr *MockDatabaseQuerierMockRecorder) StoreCertificateValidation(ctx, hostn
594 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreCertificateValidation", reflect.TypeOf((*MockDatabaseQuerier)(nil).StoreCertificateValidation), ctx, hostname, certificateHash, userDecision, expiresAt)822 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreCertificateValidation", reflect.TypeOf((*MockDatabaseQuerier)(nil).StoreCertificateValidation), ctx, hostname, certificateHash, userDecision, expiresAt)
595 }823 }
596 824
825+// UpdateFavorite mocks base method.
826+func (m *MockDatabaseQuerier) UpdateFavorite(ctx context.Context, title, faviconUrl sql.NullString, url string) error {
827+ m.ctrl.T.Helper()
828+ ret := m.ctrl.Call(m, "UpdateFavorite", ctx, title, faviconUrl, url)
829+ ret0, _ := ret[0].(error)
830+ return ret0
831+}
832+
833+// UpdateFavorite indicates an expected call of UpdateFavorite.
834+func (mr *MockDatabaseQuerierMockRecorder) UpdateFavorite(ctx, title, faviconUrl, url any) *gomock.Call {
835+ mr.mock.ctrl.T.Helper()
836+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFavorite", reflect.TypeOf((*MockDatabaseQuerier)(nil).UpdateFavorite), ctx, title, faviconUrl, url)
837+}
838+
839+// UpdateFavoritePosition mocks base method.
840+func (m *MockDatabaseQuerier) UpdateFavoritePosition(ctx context.Context, position int64, url string) error {
841+ m.ctrl.T.Helper()
842+ ret := m.ctrl.Call(m, "UpdateFavoritePosition", ctx, position, url)
843+ ret0, _ := ret[0].(error)
844+ return ret0
845+}
846+
847+// UpdateFavoritePosition indicates an expected call of UpdateFavoritePosition.
848+func (mr *MockDatabaseQuerierMockRecorder) UpdateFavoritePosition(ctx, position, url any) *gomock.Call {
849+ mr.mock.ctrl.T.Helper()
850+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFavoritePosition", reflect.TypeOf((*MockDatabaseQuerier)(nil).UpdateFavoritePosition), ctx, position, url)
851+}
852+
597 // UpdateHistoryFavicon mocks base method.853 // UpdateHistoryFavicon mocks base method.
598 func (m *MockDatabaseQuerier) UpdateHistoryFavicon(ctx context.Context, faviconUrl sql.NullString, url string) error {854 func (m *MockDatabaseQuerier) UpdateHistoryFavicon(ctx context.Context, faviconUrl sql.NullString, url string) error {
599 m.ctrl.T.Helper()855 m.ctrl.T.Helper()
M internal/parser/parser.go +19 -10
@@ -36,13 +36,8 @@ func (p *Parser) ParseInput(input string) (*ParseResult, error) {
3636 var resultType InputType
3737 var confidence float64
3838
39- // Check if this is a search query result
40- isSearchResult := strings.Contains(finalURL, neturl.QueryEscape(input))
41-
39+ // Check scheme first - if it has a valid scheme, it's a direct URL
4240 switch {
43- case isSearchResult:
44- resultType = InputTypeFallbackSearch
45- confidence = 0.1
4641 case strings.HasPrefix(finalURL, "http://"),
4742 strings.HasPrefix(finalURL, "https://"),
4843 strings.HasPrefix(finalURL, "dumb://"),
@@ -50,16 +45,25 @@ func (p *Parser) ParseInput(input string) (*ParseResult, error) {
5045 resultType = InputTypeDirectURL
5146 confidence = 1.0
5247 default:
48+ // No valid scheme, treat as search
5349 resultType = InputTypeFallbackSearch
5450 confidence = 0.1
5551 }
5652
57- // Check if it was a shortcut
53+ // Check if it was a shortcut and populate shortcut info
54+ var detectedShortcut *DetectedShortcut
5855 if idx := strings.Index(input, ":"); idx > 0 && idx < 10 && !strings.Contains(input, "://") {
59- shortcut := strings.TrimSpace(input[:idx])
60- if _, exists := p.config.SearchShortcuts[shortcut]; exists {
56+ shortcutKey := strings.TrimSpace(input[:idx])
57+ query := strings.TrimSpace(input[idx+1:])
58+ if shortcutCfg, exists := p.config.SearchShortcuts[shortcutKey]; exists {
6159 resultType = InputTypeSearchShortcut
6260 confidence = 0.95
61+ detectedShortcut = &DetectedShortcut{
62+ Key: shortcutKey,
63+ Query: query,
64+ URL: finalURL,
65+ Description: shortcutCfg.Description,
66+ }
6367 }
6468 }
6569
@@ -68,6 +72,7 @@ func (p *Parser) ParseInput(input string) (*ParseResult, error) {
6872 URL: finalURL,
6973 Query: input,
7074 Confidence: confidence,
75+ Shortcut: detectedShortcut,
7176 ProcessingTime: time.Since(startTime),
7277 }, nil
7378 }
@@ -101,7 +106,11 @@ func (p *Parser) parseInputUsingCLILogic(input string) (string, error) {
101106 if query != "" {
102107 // First check configuration-based shortcuts
103108 if shortcutCfg, exists := p.config.SearchShortcuts[shortcut]; exists {
104- return fmt.Sprintf(shortcutCfg.URL, neturl.QueryEscape(query)), nil
109+ encodedQuery := neturl.QueryEscape(query)
110+ // Support both {query} and %s placeholders
111+ url := strings.ReplaceAll(shortcutCfg.URL, "{query}", encodedQuery)
112+ url = strings.ReplaceAll(url, "%s", encodedQuery)
113+ return url, nil
105114 }
106115
107116 return "", fmt.Errorf("unknown shortcut '%s'", shortcut)
@@ -36,13 +36,8 @@ func (p *Parser) ParseInput(input string) (*ParseResult, error) {
36 var resultType InputType36 var resultType InputType
37 var confidence float6437 var confidence float64
38 38
39- // Check if this is a search query result39+ // Check scheme first - if it has a valid scheme, it's a direct URL
40- isSearchResult := strings.Contains(finalURL, neturl.QueryEscape(input))
41-
42 switch {40 switch {
43- case isSearchResult:
44- resultType = InputTypeFallbackSearch
45- confidence = 0.1
46 case strings.HasPrefix(finalURL, "http://"),41 case strings.HasPrefix(finalURL, "http://"),
47 strings.HasPrefix(finalURL, "https://"),42 strings.HasPrefix(finalURL, "https://"),
48 strings.HasPrefix(finalURL, "dumb://"),43 strings.HasPrefix(finalURL, "dumb://"),
@@ -50,16 +45,25 @@ func (p *Parser) ParseInput(input string) (*ParseResult, error) {
50 resultType = InputTypeDirectURL45 resultType = InputTypeDirectURL
51 confidence = 1.046 confidence = 1.0
52 default:47 default:
48+ // No valid scheme, treat as search
53 resultType = InputTypeFallbackSearch49 resultType = InputTypeFallbackSearch
54 confidence = 0.150 confidence = 0.1
55 }51 }
56 52
57- // Check if it was a shortcut53+ // Check if it was a shortcut and populate shortcut info
54+ var detectedShortcut *DetectedShortcut
58 if idx := strings.Index(input, ":"); idx > 0 && idx < 10 && !strings.Contains(input, "://") {55 if idx := strings.Index(input, ":"); idx > 0 && idx < 10 && !strings.Contains(input, "://") {
59- shortcut := strings.TrimSpace(input[:idx])56+ shortcutKey := strings.TrimSpace(input[:idx])
60- if _, exists := p.config.SearchShortcuts[shortcut]; exists {57+ query := strings.TrimSpace(input[idx+1:])
58+ if shortcutCfg, exists := p.config.SearchShortcuts[shortcutKey]; exists {
61 resultType = InputTypeSearchShortcut59 resultType = InputTypeSearchShortcut
62 confidence = 0.9560 confidence = 0.95
61+ detectedShortcut = &DetectedShortcut{
62+ Key: shortcutKey,
63+ Query: query,
64+ URL: finalURL,
65+ Description: shortcutCfg.Description,
66+ }
63 }67 }
64 }68 }
65 69
@@ -68,6 +72,7 @@ func (p *Parser) ParseInput(input string) (*ParseResult, error) {
68 URL: finalURL,72 URL: finalURL,
69 Query: input,73 Query: input,
70 Confidence: confidence,74 Confidence: confidence,
75+ Shortcut: detectedShortcut,
71 ProcessingTime: time.Since(startTime),76 ProcessingTime: time.Since(startTime),
72 }, nil77 }, nil
73 }78 }
@@ -101,7 +106,11 @@ func (p *Parser) parseInputUsingCLILogic(input string) (string, error) {
101 if query != "" {106 if query != "" {
102 // First check configuration-based shortcuts107 // First check configuration-based shortcuts
103 if shortcutCfg, exists := p.config.SearchShortcuts[shortcut]; exists {108 if shortcutCfg, exists := p.config.SearchShortcuts[shortcut]; exists {
104- return fmt.Sprintf(shortcutCfg.URL, neturl.QueryEscape(query)), nil109+ encodedQuery := neturl.QueryEscape(query)
110+ // Support both {query} and %s placeholders
111+ url := strings.ReplaceAll(shortcutCfg.URL, "{query}", encodedQuery)
112+ url = strings.ReplaceAll(url, "%s", encodedQuery)
113+ return url, nil
105 }114 }
106 115
107 return "", fmt.Errorf("unknown shortcut '%s'", shortcut)116 return "", fmt.Errorf("unknown shortcut '%s'", shortcut)

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