opentwig 1.0.7 → 1.1.1

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 (62) hide show
  1. package/AGENTS.md +172 -0
  2. package/API.md +582 -0
  3. package/CODE_OF_CONDUCT.md +91 -0
  4. package/CONTRIBUTING.md +312 -0
  5. package/README.md +164 -7
  6. package/SECURITY.md +56 -0
  7. package/THEME_DEVELOPMENT.md +388 -0
  8. package/package.json +19 -4
  9. package/src/constants.js +14 -2
  10. package/src/index.js +14 -2
  11. package/src/live-ui/editor.js +174 -0
  12. package/src/live-ui/preview.js +77 -0
  13. package/src/live-ui/sidebar.js +525 -0
  14. package/src/utils/escapeHTML.js +10 -0
  15. package/src/utils/favicon.js +20 -0
  16. package/src/utils/generateOGImage.js +51 -10
  17. package/src/utils/loadConfig.js +3 -1
  18. package/src/utils/parseArgs.js +33 -2
  19. package/src/utils/readImageAsBase64.js +16 -4
  20. package/src/utils/setupWatcher.js +69 -0
  21. package/src/utils/showHelp.js +15 -2
  22. package/src/utils/startLiveServer.js +221 -0
  23. package/src/utils/websocketServer.js +53 -0
  24. package/theme/dark/style.css +1 -0
  25. package/theme/default/index.js +12 -8
  26. package/validateConfig.js +59 -0
  27. package/vitest.config.js +20 -0
  28. package/website/README.md +42 -0
  29. package/website/components.json +16 -0
  30. package/website/eslint.config.js +36 -0
  31. package/website/package-lock.json +4136 -0
  32. package/website/package.json +41 -0
  33. package/website/shadcn-svelte.md +118 -0
  34. package/website/src/app.d.ts +13 -0
  35. package/website/src/lib/components/ui/badge/badge.svelte +50 -0
  36. package/website/src/lib/components/ui/badge/index.ts +2 -0
  37. package/website/src/lib/components/ui/button/button.svelte +82 -0
  38. package/website/src/lib/components/ui/button/index.ts +17 -0
  39. package/website/src/lib/components/ui/card/card-action.svelte +20 -0
  40. package/website/src/lib/components/ui/card/card-content.svelte +15 -0
  41. package/website/src/lib/components/ui/card/card-description.svelte +20 -0
  42. package/website/src/lib/components/ui/card/card-footer.svelte +20 -0
  43. package/website/src/lib/components/ui/card/card-header.svelte +23 -0
  44. package/website/src/lib/components/ui/card/card-title.svelte +20 -0
  45. package/website/src/lib/components/ui/card/card.svelte +23 -0
  46. package/website/src/lib/components/ui/card/index.ts +25 -0
  47. package/website/src/lib/components/ui/separator/index.ts +7 -0
  48. package/website/src/lib/components/ui/separator/separator.svelte +21 -0
  49. package/website/src/lib/components/ui/tooltip/index.ts +19 -0
  50. package/website/src/lib/components/ui/tooltip/tooltip-content.svelte +52 -0
  51. package/website/src/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
  52. package/website/src/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
  53. package/website/src/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  54. package/website/src/lib/components/ui/tooltip/tooltip.svelte +7 -0
  55. package/website/src/lib/index.ts +1 -0
  56. package/website/src/lib/utils.ts +13 -0
  57. package/website/src/routes/+layout.svelte +23 -0
  58. package/website/src/routes/+page.server.ts +82 -0
  59. package/website/src/routes/+page.svelte +892 -0
  60. package/website/static/robots.txt +3 -0
  61. package/website/svelte.config.js +31 -0
  62. package/website/vite.config.ts +5 -0
@@ -0,0 +1,13 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
7
+
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ export type WithoutChild<T> = T extends { child?: any } ? Omit<T, "child"> : T;
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, "children"> : T;
12
+ export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
13
+ export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null };
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import './layout.css';
3
+ import favicon from '$lib/assets/favicon.svg';
4
+
5
+ let { children } = $props();
6
+ </script>
7
+
8
+ <svelte:head>
9
+ <link rel="icon" href={favicon} />
10
+ <title>OpenTwig - Free & Open Source Link in Bio Generator</title>
11
+ <meta name="description" content="Create beautiful, customizable link in bio pages. OpenTwig is a free, open-source CLI tool that generates static pages you own and control." />
12
+ <meta name="keywords" content="link in bio, bio page, linktree alternative, open source, static site generator, CLI tool" />
13
+ <meta name="author" content="Tufan Tunç" />
14
+ <meta property="og:title" content="OpenTwig - Free & Open Source Link in Bio Generator" />
15
+ <meta property="og:description" content="Create beautiful, customizable link in bio pages. OpenTwig is a free, open-source CLI tool." />
16
+ <meta property="og:type" content="website" />
17
+ <meta property="og:url" content="https://opentwig.dev" />
18
+ <meta name="twitter:card" content="summary_large_image" />
19
+ <meta name="twitter:title" content="OpenTwig - Free & Open Source Link in Bio Generator" />
20
+ <meta name="twitter:description" content="Create beautiful, customizable link in bio pages with OpenTwig." />
21
+ </svelte:head>
22
+
23
+ {@render children()}
@@ -0,0 +1,82 @@
1
+ import type { PageServerLoad } from './$types';
2
+
3
+ interface GitHubRepoData {
4
+ stargazers_count: number;
5
+ }
6
+
7
+ interface NPMDownloadsData {
8
+ downloads: number;
9
+ }
10
+
11
+ // Enable prerendering for this page
12
+ export const prerender = true;
13
+
14
+ function formatNumber(num: number): string {
15
+ if (num >= 1000) {
16
+ return (num / 1000).toFixed(1).replace(/\.0$/, '') + 'k+';
17
+ }
18
+ return num.toString();
19
+ }
20
+
21
+ export const load: PageServerLoad = async () => {
22
+ const GITHUB_REPO = 'tufantunc/opentwig';
23
+ const NPM_PACKAGE = 'opentwig';
24
+
25
+ let githubStars = 0;
26
+ let npmDownloads = 0;
27
+
28
+ try {
29
+ // Fetch GitHub stars
30
+ const githubResponse = await fetch(`https://api.github.com/repos/${GITHUB_REPO}`, {
31
+ headers: {
32
+ 'Accept': 'application/vnd.github.v3+json',
33
+ 'User-Agent': 'OpenTwig-Website'
34
+ }
35
+ });
36
+
37
+ if (githubResponse.ok) {
38
+ const githubData: GitHubRepoData = await githubResponse.json();
39
+ githubStars = githubData.stargazers_count;
40
+ console.log(`✓ GitHub stars fetched: ${githubStars}`);
41
+ } else {
42
+ console.warn(`⚠ GitHub API error: ${githubResponse.status} ${githubResponse.statusText}`);
43
+ }
44
+ } catch (error) {
45
+ console.warn('⚠ Failed to fetch GitHub stars:', error);
46
+ }
47
+
48
+ try {
49
+ // Fetch NPM total downloads (all time)
50
+ const npmResponse = await fetch(
51
+ `https://api.npmjs.org/downloads/range/2015-01-01:${new Date().toISOString().split('T')[0]}/${NPM_PACKAGE}`,
52
+ {
53
+ headers: {
54
+ 'Accept': 'application/json'
55
+ }
56
+ }
57
+ );
58
+
59
+ if (npmResponse.ok) {
60
+ const npmData = await npmResponse.json();
61
+ // Sum up all daily downloads
62
+ npmDownloads = npmData.downloads.reduce(
63
+ (sum: number, day: { downloads: number }) => sum + day.downloads,
64
+ 0
65
+ );
66
+ console.log(`✓ NPM downloads fetched: ${npmDownloads}`);
67
+ } else {
68
+ console.warn(`⚠ NPM API error: ${npmResponse.status} ${npmResponse.statusText}`);
69
+ }
70
+ } catch (error) {
71
+ console.warn('⚠ Failed to fetch NPM downloads:', error);
72
+ }
73
+
74
+ return {
75
+ stats: {
76
+ githubStars,
77
+ npmDownloads,
78
+ formattedGithubStars: formatNumber(githubStars),
79
+ formattedNpmDownloads: formatNumber(npmDownloads)
80
+ }
81
+ };
82
+ };