@mdguggenbichler/slugbase-core 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/frontend/index.tsx +3 -3
  2. package/frontend/public/favicon.svg +1 -0
  3. package/frontend/public/slugbase_icon_blue.svg +1 -0
  4. package/frontend/public/slugbase_icon_white.png +0 -0
  5. package/frontend/public/slugbase_icon_white.svg +1 -0
  6. package/frontend/src/App.tsx +179 -0
  7. package/frontend/src/api/client.ts +134 -0
  8. package/frontend/src/components/AppSidebar.tsx +214 -0
  9. package/frontend/src/components/EmptyState.tsx +33 -0
  10. package/frontend/src/components/Favicon.tsx +76 -0
  11. package/frontend/src/components/FilterChips.tsx +60 -0
  12. package/frontend/src/components/FolderIcon.tsx +207 -0
  13. package/frontend/src/components/GlobalSearch.tsx +275 -0
  14. package/frontend/src/components/Layout.tsx +60 -0
  15. package/frontend/src/components/PageHeader.tsx +31 -0
  16. package/frontend/src/components/ScopeSegmentedControl.tsx +42 -0
  17. package/frontend/src/components/SentryDebug.tsx +32 -0
  18. package/frontend/src/components/StatCard.tsx +66 -0
  19. package/frontend/src/components/TopBar.tsx +63 -0
  20. package/frontend/src/components/UserDropdown.tsx +86 -0
  21. package/frontend/src/components/admin/AdminAI.tsx +207 -0
  22. package/frontend/src/components/admin/AdminOIDCProviders.tsx +183 -0
  23. package/frontend/src/components/admin/AdminSettings.tsx +413 -0
  24. package/frontend/src/components/admin/AdminTeams.tsx +177 -0
  25. package/frontend/src/components/admin/AdminUsers.tsx +225 -0
  26. package/frontend/src/components/bookmarks/BookmarkCard.tsx +312 -0
  27. package/frontend/src/components/bookmarks/BookmarkListItem.tsx +159 -0
  28. package/frontend/src/components/bookmarks/BookmarkTableView.tsx +419 -0
  29. package/frontend/src/components/bookmarks/BulkActionModals.tsx +162 -0
  30. package/frontend/src/components/bookmarks/FilterChips.tsx +5 -0
  31. package/frontend/src/components/modals/BookmarkModal.tsx +493 -0
  32. package/frontend/src/components/modals/FolderModal.tsx +306 -0
  33. package/frontend/src/components/modals/ImportModal.tsx +232 -0
  34. package/frontend/src/components/modals/OIDCProviderModal.tsx +284 -0
  35. package/frontend/src/components/modals/SharingModal.tsx +96 -0
  36. package/frontend/src/components/modals/TagModal.tsx +101 -0
  37. package/frontend/src/components/modals/TeamAssignmentModal.tsx +354 -0
  38. package/frontend/src/components/modals/TeamModal.tsx +117 -0
  39. package/frontend/src/components/modals/UserModal.tsx +225 -0
  40. package/frontend/src/components/profile/CreateTokenModal.tsx +172 -0
  41. package/frontend/src/components/sharing/ShareResourceDialog.tsx +422 -0
  42. package/frontend/src/components/ui/Autocomplete.tsx +155 -0
  43. package/frontend/src/components/ui/Button.tsx +68 -0
  44. package/frontend/src/components/ui/ConfirmDialog.tsx +79 -0
  45. package/frontend/src/components/ui/FormFieldWrapper.tsx +36 -0
  46. package/frontend/src/components/ui/ModalFooterActions.tsx +49 -0
  47. package/frontend/src/components/ui/ModalSection.tsx +34 -0
  48. package/frontend/src/components/ui/PageLoadingSkeleton.tsx +24 -0
  49. package/frontend/src/components/ui/Select.tsx +61 -0
  50. package/frontend/src/components/ui/SharingField.tsx +298 -0
  51. package/frontend/src/components/ui/Toast.tsx +47 -0
  52. package/frontend/src/components/ui/Tooltip.tsx +21 -0
  53. package/frontend/src/components/ui/alert-dialog.tsx +139 -0
  54. package/frontend/src/components/ui/badge.tsx +36 -0
  55. package/frontend/src/components/ui/button-base.tsx +57 -0
  56. package/frontend/src/components/ui/card.tsx +76 -0
  57. package/frontend/src/components/ui/command.tsx +161 -0
  58. package/frontend/src/components/ui/dialog.tsx +120 -0
  59. package/frontend/src/components/ui/dropdown-menu.tsx +199 -0
  60. package/frontend/src/components/ui/input.tsx +22 -0
  61. package/frontend/src/components/ui/label.tsx +24 -0
  62. package/frontend/src/components/ui/popover.tsx +33 -0
  63. package/frontend/src/components/ui/progress.tsx +26 -0
  64. package/frontend/src/components/ui/scroll-area.tsx +48 -0
  65. package/frontend/src/components/ui/select-base.tsx +159 -0
  66. package/frontend/src/components/ui/separator.tsx +29 -0
  67. package/frontend/src/components/ui/sheet.tsx +140 -0
  68. package/frontend/src/components/ui/sidebar.tsx +783 -0
  69. package/frontend/src/components/ui/skeleton.tsx +15 -0
  70. package/frontend/src/components/ui/sonner.tsx +46 -0
  71. package/frontend/src/components/ui/switch.tsx +28 -0
  72. package/frontend/src/components/ui/table.tsx +120 -0
  73. package/frontend/src/components/ui/tooltip-base.tsx +30 -0
  74. package/frontend/src/config/api.ts +16 -0
  75. package/frontend/src/config/mode.ts +6 -0
  76. package/frontend/src/contexts/AppConfigContext.tsx +39 -0
  77. package/frontend/src/contexts/AuthContext.tsx +137 -0
  78. package/frontend/src/contexts/SearchCommandContext.tsx +28 -0
  79. package/frontend/src/hooks/use-mobile.tsx +19 -0
  80. package/frontend/src/hooks/useConfirmDialog.ts +63 -0
  81. package/frontend/src/hooks/useMarketingTheme.ts +47 -0
  82. package/frontend/src/i18n.ts +39 -0
  83. package/frontend/src/index.css +117 -0
  84. package/frontend/src/instrument.ts +20 -0
  85. package/frontend/src/lib/utils.ts +6 -0
  86. package/frontend/src/locales/de.json +899 -0
  87. package/frontend/src/locales/en.json +937 -0
  88. package/frontend/src/locales/es.json +884 -0
  89. package/frontend/src/locales/fr.json +550 -0
  90. package/frontend/src/locales/it.json +535 -0
  91. package/frontend/src/locales/ja.json +535 -0
  92. package/frontend/src/locales/nl.json +550 -0
  93. package/frontend/src/locales/pl.json +535 -0
  94. package/frontend/src/locales/pt.json +535 -0
  95. package/frontend/src/locales/ru.json +535 -0
  96. package/frontend/src/locales/zh.json +535 -0
  97. package/frontend/src/main.tsx +44 -0
  98. package/frontend/src/pages/Bookmarks.tsx +1004 -0
  99. package/frontend/src/pages/Dashboard.tsx +427 -0
  100. package/frontend/src/pages/Folders.tsx +578 -0
  101. package/frontend/src/pages/GoPreferences.tsx +134 -0
  102. package/frontend/src/pages/Login.tsx +196 -0
  103. package/frontend/src/pages/PasswordReset.tsx +242 -0
  104. package/frontend/src/pages/Profile.tsx +593 -0
  105. package/frontend/src/pages/SearchEngineGuide.tsx +135 -0
  106. package/frontend/src/pages/Setup.tsx +210 -0
  107. package/frontend/src/pages/Signup.tsx +199 -0
  108. package/frontend/src/pages/Tags.tsx +421 -0
  109. package/frontend/src/pages/VerifyEmail.tsx +254 -0
  110. package/frontend/src/pages/admin/AdminAIPage.tsx +5 -0
  111. package/frontend/src/pages/admin/AdminLayout.tsx +40 -0
  112. package/frontend/src/pages/admin/AdminMembersPage.tsx +5 -0
  113. package/frontend/src/pages/admin/AdminOIDCPage.tsx +5 -0
  114. package/frontend/src/pages/admin/AdminSettingsPage.tsx +5 -0
  115. package/frontend/src/pages/admin/AdminTeamsPage.tsx +5 -0
  116. package/frontend/src/utils/favicon.ts +36 -0
  117. package/frontend/src/utils/formatRelativeTime.ts +37 -0
  118. package/frontend/src/utils/safeHref.ts +31 -0
  119. package/frontend/src/vite-env.d.ts +10 -0
  120. package/package.json +9 -1
@@ -0,0 +1,40 @@
1
+ import { useTranslation } from 'react-i18next';
2
+ import { Outlet } from 'react-router-dom';
3
+ import { ExternalLink } from 'lucide-react';
4
+
5
+ export default function AdminLayout() {
6
+ const { t } = useTranslation();
7
+
8
+ return (
9
+ <div className="space-y-6">
10
+ {/* Header */}
11
+ <div>
12
+ <h1 className="text-2xl font-semibold text-foreground">{t('admin.title')}</h1>
13
+ <p className="mt-2 text-sm text-muted-foreground">{t('admin.description')}</p>
14
+ </div>
15
+
16
+ {/* Child route content */}
17
+ <Outlet />
18
+
19
+ {/* API Documentation Link */}
20
+ <div className="mt-8 pt-6 border-t border-border">
21
+ <div className="flex items-center justify-between">
22
+ <div>
23
+ <p className="text-sm text-muted-foreground">
24
+ {t('admin.apiDocsNote')}
25
+ </p>
26
+ </div>
27
+ <a
28
+ href="/api-docs"
29
+ target="_blank"
30
+ rel="noopener noreferrer"
31
+ className="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary hover:text-primary/90 transition-colors"
32
+ >
33
+ <ExternalLink className="h-4 w-4" />
34
+ {t('admin.viewApiDocs')}
35
+ </a>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ );
40
+ }
@@ -0,0 +1,5 @@
1
+ import AdminUsers from '../../components/admin/AdminUsers';
2
+
3
+ export default function AdminMembersPage() {
4
+ return <AdminUsers />;
5
+ }
@@ -0,0 +1,5 @@
1
+ import AdminOIDCProviders from '../../components/admin/AdminOIDCProviders';
2
+
3
+ export default function AdminOIDCPage() {
4
+ return <AdminOIDCProviders />;
5
+ }
@@ -0,0 +1,5 @@
1
+ import AdminSettings from '../../components/admin/AdminSettings';
2
+
3
+ export default function AdminSettingsPage() {
4
+ return <AdminSettings />;
5
+ }
@@ -0,0 +1,5 @@
1
+ import AdminTeams from '../../components/admin/AdminTeams';
2
+
3
+ export default function AdminTeamsPage() {
4
+ return <AdminTeams />;
5
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Get favicon URL for a given website URL
3
+ * Tries multiple methods to get the favicon
4
+ */
5
+ export function getFaviconUrl(url: string): string {
6
+ try {
7
+ const urlObj = new URL(url);
8
+ const origin = urlObj.origin;
9
+
10
+ // Try common favicon locations
11
+ return `${origin}/favicon.ico`;
12
+ } catch {
13
+ // If URL parsing fails, return empty string
14
+ return '';
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Fetch favicon with fallback handling
20
+ * Returns a favicon URL or empty string if it can't be determined
21
+ */
22
+ export async function fetchFavicon(url: string): Promise<string> {
23
+ try {
24
+ const urlObj = new URL(url);
25
+ const origin = urlObj.origin;
26
+
27
+ // Try direct favicon.ico first (most reliable)
28
+ const directFavicon = `${origin}/favicon.ico`;
29
+
30
+ // Return the direct favicon URL - let the browser handle loading
31
+ // If it fails, the component will show the fallback icon
32
+ return directFavicon;
33
+ } catch {
34
+ return '';
35
+ }
36
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Format an ISO date string as relative time (e.g. "2d ago", "5m ago").
3
+ * Returns "Never" or "-" for null/undefined/empty.
4
+ */
5
+ export function formatRelativeTime(isoString: string | null | undefined): string {
6
+ if (!isoString || isoString.trim() === '') return 'Never';
7
+ const date = new Date(isoString);
8
+ if (Number.isNaN(date.getTime())) return '-';
9
+ const now = Date.now();
10
+ const diffMs = now - date.getTime();
11
+ const diffSec = Math.floor(diffMs / 1000);
12
+ const diffMin = Math.floor(diffSec / 60);
13
+ const diffHour = Math.floor(diffMin / 60);
14
+ const diffDay = Math.floor(diffHour / 24);
15
+ const diffWeek = Math.floor(diffDay / 7);
16
+
17
+ if (diffSec < 10) return 'just now';
18
+ if (diffSec < 60) return `${diffSec}s ago`;
19
+ if (diffMin < 60) return `${diffMin}m ago`;
20
+ if (diffHour < 24) return `${diffHour}h ago`;
21
+ if (diffDay < 7) return `${diffDay}d ago`;
22
+ if (diffWeek < 4) return `${diffWeek}w ago`;
23
+ return date.toLocaleDateString();
24
+ }
25
+
26
+ /**
27
+ * Format an ISO date string for tooltip (full date and time).
28
+ */
29
+ export function formatFullDateTime(isoString: string | null | undefined): string {
30
+ if (!isoString || isoString.trim() === '') return '';
31
+ const date = new Date(isoString);
32
+ if (Number.isNaN(date.getTime())) return '';
33
+ return date.toLocaleString(undefined, {
34
+ dateStyle: 'medium',
35
+ timeStyle: 'short',
36
+ });
37
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Return a safe href for <a> tags (http/https only). Use for bookmark and preference URLs
3
+ * as defense-in-depth; backend already validates on save.
4
+ * Returns '#' for invalid or dangerous schemes (e.g. javascript:, data:) so the link does nothing.
5
+ */
6
+ export function safeHref(url: string | undefined | null): string {
7
+ if (!url || typeof url !== 'string') {
8
+ return '#';
9
+ }
10
+ const trimmed = url.trim();
11
+ if (!trimmed) return '#';
12
+ const lower = trimmed.toLowerCase();
13
+ if (
14
+ lower.startsWith('javascript:') ||
15
+ lower.startsWith('data:') ||
16
+ lower.startsWith('vbscript:') ||
17
+ lower.startsWith('file:') ||
18
+ lower.startsWith('about:')
19
+ ) {
20
+ return '#';
21
+ }
22
+ try {
23
+ const parsed = new URL(trimmed, window.location.origin);
24
+ if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
25
+ return parsed.href;
26
+ }
27
+ } catch {
28
+ // invalid URL
29
+ }
30
+ return '#';
31
+ }
@@ -0,0 +1,10 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ interface ImportMetaEnv {
4
+ readonly VITE_SLUGBASE_MODE?: string;
5
+ readonly VITE_API_URL?: string;
6
+ }
7
+
8
+ interface ImportMeta {
9
+ readonly env: ImportMetaEnv;
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mdguggenbichler/slugbase-core",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "SlugBase core: backend and frontend entrypoints for self-hosted and cloud apps",
5
5
  "type": "module",
6
6
  "exports": {
@@ -12,5 +12,13 @@
12
12
  "scripts": {},
13
13
  "engines": {
14
14
  "node": ">=20"
15
+ },
16
+ "peerDependencies": {
17
+ "react": ">=18",
18
+ "react-dom": ">=18",
19
+ "react-router-dom": ">=6",
20
+ "i18next": ">=23",
21
+ "react-i18next": ">=13",
22
+ "axios": ">=1"
15
23
  }
16
24
  }