windmill-components 1.700.2 → 1.700.3
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.
- package/dist/appPolicy/myFunction.es.js +1337 -0
- package/dist/sharedUtils/common.d.ts +2 -5
- package/dist/sharedUtils/components/apps/components/display/dbtable/queries/select.d.ts +0 -2
- package/dist/sharedUtils/components/apps/components/display/dbtable/utils.d.ts +3 -14
- package/dist/sharedUtils/components/apps/editor/appPolicy.d.ts +1 -1
- package/dist/sharedUtils/components/apps/editor/appUtilsS3.d.ts +1 -12
- package/dist/sharedUtils/components/apps/editor/component/components.d.ts +2 -68
- package/dist/sharedUtils/components/apps/inputType.d.ts +2 -4
- package/dist/sharedUtils/components/apps/sharedTypes.d.ts +0 -2
- package/dist/sharedUtils/components/dbTypes.d.ts +0 -3
- package/dist/sharedUtils/components/raw_apps/rawAppPolicy.d.ts +1 -1
- package/dist/sharedUtils/components/raw_apps/utils.d.ts +1 -1
- package/dist/sharedUtils/components/triggers/utils.d.ts +3 -2
- package/dist/sharedUtils/gen/schemas.gen.d.ts +71 -915
- package/dist/sharedUtils/gen/services.gen.d.ts +23 -329
- package/dist/sharedUtils/gen/types.gen.d.ts +141 -1870
- package/dist/sharedUtils/hub.d.ts +0 -1
- package/dist/sharedUtils/jsr.json +5 -5
- package/dist/sharedUtils/lib.d.ts +1 -1
- package/dist/sharedUtils/lib.es.js +79 -241
- package/dist/sharedUtils/package.json +11 -11
- package/dist/sharedUtils/stores.d.ts +0 -1
- package/dist/sharedUtils/svelte5Utils.svelte.d.ts +1 -32
- package/dist/sharedUtils/utils.d.ts +4 -19
- package/package/components/DisplayResultControlBar.svelte +26 -11
- package/package/components/JobArgs.svelte +43 -24
- package/package/components/ShareModal.svelte.d.ts +1 -1
- package/package/components/apps/components/helpers/RunnableComponent.svelte.d.ts +0 -3
- package/package/components/copilot/CustomAIPrompts.svelte +3 -2
- package/package/components/copilot/chat/AIChatInput.svelte +2 -0
- package/package/components/copilot/chat/AIChatManager.svelte.js +52 -14
- package/package/components/copilot/chat/CreatedResourceActionDrawers.svelte +15 -0
- package/package/components/copilot/chat/ToolMessageActions.svelte +4 -2
- package/package/components/copilot/chat/app/core.js +2 -30
- package/package/components/copilot/chat/flow/core.js +13 -351
- package/package/components/copilot/chat/flow/editableFlowJson.d.ts +52 -0
- package/package/components/copilot/chat/flow/editableFlowJson.js +328 -0
- package/package/components/copilot/chat/flow/inlineScriptsUtils.js +2 -2
- package/package/components/copilot/chat/global/core.d.ts +5 -0
- package/package/components/copilot/chat/global/core.js +1739 -0
- package/package/components/copilot/chat/global/core.test.d.ts +1 -0
- package/package/components/copilot/chat/global/core.test.js +123 -0
- package/package/components/copilot/chat/global/deployRequests.d.ts +7 -0
- package/package/components/copilot/chat/global/deployRequests.js +76 -0
- package/package/components/copilot/chat/global/deployRequests.test.d.ts +1 -0
- package/package/components/copilot/chat/global/deployRequests.test.js +142 -0
- package/package/components/copilot/chat/global/draftStore.svelte.d.ts +55 -0
- package/package/components/copilot/chat/global/draftStore.svelte.js +78 -0
- package/package/components/copilot/chat/global/draftStore.test.d.ts +1 -0
- package/package/components/copilot/chat/global/draftStore.test.js +44 -0
- package/package/components/copilot/chat/global/gate.d.ts +1 -0
- package/package/components/copilot/chat/global/gate.js +27 -0
- package/package/components/copilot/chat/shared.d.ts +16 -2
- package/package/components/copilot/chat/shared.js +40 -0
- package/package/components/copilot/chat/workspaceToolsZod.gen.d.ts +28 -9
- package/package/components/copilot/chat/workspaceToolsZod.gen.js +19 -0
- package/package/components/raw_apps/templates.d.ts +77 -0
- package/package/components/raw_apps/templates.js +618 -0
- package/package/components/runs/runsFilter.d.ts +1 -1
- package/package/components/settings/AIPromptsModal.svelte +5 -2
- package/package/components/triggers/azure/AzureTriggerEditorConfigSection.svelte.d.ts +1 -1
- package/package/gen/core/OpenAPI.js +1 -1
- package/package/gen/schemas.gen.d.ts +37 -355
- package/package/gen/schemas.gen.js +39 -359
- package/package/gen/services.gen.d.ts +4 -280
- package/package/gen/services.gen.js +7 -565
- package/package/gen/types.gen.d.ts +77 -1135
- package/package/system_prompts/index.d.ts +2 -0
- package/package/system_prompts/index.js +8 -0
- package/package/system_prompts/prompts.d.ts +2 -0
- package/package/system_prompts/prompts.js +381 -0
- package/package/utils_deployable.d.ts +5 -318
- package/package.json +1 -1
- package/dist/sharedUtils/components/assets/lib.d.ts +0 -25
- package/dist/sharedUtils/components/icons/index.d.ts +0 -101
|
@@ -466,6 +466,25 @@ export declare const azureTriggerRequestSchema: z.ZodObject<{
|
|
|
466
466
|
preserve_permissioned_as: z.ZodOptional<z.ZodBoolean>;
|
|
467
467
|
labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
468
468
|
}, z.core.$strip>;
|
|
469
|
+
export declare const variableRequestSchema: z.ZodObject<{
|
|
470
|
+
path: z.ZodString;
|
|
471
|
+
value: z.ZodString;
|
|
472
|
+
is_secret: z.ZodBoolean;
|
|
473
|
+
description: z.ZodString;
|
|
474
|
+
account: z.ZodOptional<z.ZodNumber>;
|
|
475
|
+
is_oauth: z.ZodOptional<z.ZodBoolean>;
|
|
476
|
+
expires_at: z.ZodOptional<z.ZodString>;
|
|
477
|
+
labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
478
|
+
ws_specific: z.ZodOptional<z.ZodBoolean>;
|
|
479
|
+
}, z.core.$strip>;
|
|
480
|
+
export declare const resourceRequestSchema: z.ZodObject<{
|
|
481
|
+
path: z.ZodString;
|
|
482
|
+
value: z.ZodAny;
|
|
483
|
+
description: z.ZodOptional<z.ZodString>;
|
|
484
|
+
resource_type: z.ZodString;
|
|
485
|
+
labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
486
|
+
ws_specific: z.ZodOptional<z.ZodBoolean>;
|
|
487
|
+
}, z.core.$strip>;
|
|
469
488
|
export declare const triggerRequestSchemas: {
|
|
470
489
|
readonly http: z.ZodObject<{
|
|
471
490
|
path: z.ZodString;
|
|
@@ -1262,15 +1281,6 @@ export declare const createTriggerToolSchema: z.ZodObject<{
|
|
|
1262
1281
|
suspended: "suspended";
|
|
1263
1282
|
}>>;
|
|
1264
1283
|
base_endpoint: z.ZodOptional<z.ZodString>;
|
|
1265
|
-
azure_mode: z.ZodEnum<{
|
|
1266
|
-
basic_push: "basic_push";
|
|
1267
|
-
namespace_push: "namespace_push";
|
|
1268
|
-
namespace_pull: "namespace_pull";
|
|
1269
|
-
}>;
|
|
1270
|
-
scope_resource_id: z.ZodString;
|
|
1271
|
-
subscription_name: z.ZodString;
|
|
1272
|
-
azure_resource_path: z.ZodString;
|
|
1273
|
-
topic_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1274
1284
|
labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1275
1285
|
retry: z.ZodOptional<z.ZodObject<{
|
|
1276
1286
|
constant: z.ZodOptional<z.ZodObject<{
|
|
@@ -1290,6 +1300,15 @@ export declare const createTriggerToolSchema: z.ZodObject<{
|
|
|
1290
1300
|
preserve_permissioned_as: z.ZodOptional<z.ZodBoolean>;
|
|
1291
1301
|
error_handler_path: z.ZodOptional<z.ZodString>;
|
|
1292
1302
|
error_handler_args: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
1303
|
+
azure_resource_path: z.ZodString;
|
|
1304
|
+
azure_mode: z.ZodEnum<{
|
|
1305
|
+
basic_push: "basic_push";
|
|
1306
|
+
namespace_push: "namespace_push";
|
|
1307
|
+
namespace_pull: "namespace_pull";
|
|
1308
|
+
}>;
|
|
1309
|
+
scope_resource_id: z.ZodString;
|
|
1310
|
+
topic_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1311
|
+
subscription_name: z.ZodString;
|
|
1293
1312
|
event_type_filters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1294
1313
|
}, z.core.$strip>]>;
|
|
1295
1314
|
}, z.core.$strip>;
|
|
@@ -385,6 +385,25 @@ export const azureTriggerRequestSchema = z.object({
|
|
|
385
385
|
"preserve_permissioned_as": z.boolean().optional(),
|
|
386
386
|
"labels": z.array(z.string()).optional()
|
|
387
387
|
}).describe("Data for creating or updating an Azure Event Grid trigger.");
|
|
388
|
+
export const variableRequestSchema = z.object({
|
|
389
|
+
"path": z.string().describe("The path to the variable"),
|
|
390
|
+
"value": z.string().describe("The value of the variable"),
|
|
391
|
+
"is_secret": z.boolean().describe("Whether the variable is a secret"),
|
|
392
|
+
"description": z.string().describe("The description of the variable"),
|
|
393
|
+
"account": z.number().int().describe("The account identifier").optional(),
|
|
394
|
+
"is_oauth": z.boolean().describe("Whether the variable is an OAuth variable").optional(),
|
|
395
|
+
"expires_at": z.string().datetime({ offset: true }).describe("The expiration date of the variable").optional(),
|
|
396
|
+
"labels": z.array(z.string()).optional(),
|
|
397
|
+
"ws_specific": z.boolean().optional()
|
|
398
|
+
});
|
|
399
|
+
export const resourceRequestSchema = z.object({
|
|
400
|
+
"path": z.string().describe("The path to the resource"),
|
|
401
|
+
"value": z.any(),
|
|
402
|
+
"description": z.string().describe("The description of the resource").optional(),
|
|
403
|
+
"resource_type": z.string().describe("The resource_type associated with the resource"),
|
|
404
|
+
"labels": z.array(z.string()).optional(),
|
|
405
|
+
"ws_specific": z.boolean().optional()
|
|
406
|
+
});
|
|
388
407
|
export const triggerRequestSchemas = {
|
|
389
408
|
http: httpTriggerRequestSchema,
|
|
390
409
|
websocket: websocketTriggerRequestSchema,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export declare const react19Template: {
|
|
2
|
+
'/index.tsx': string;
|
|
3
|
+
'/App.tsx': string;
|
|
4
|
+
'/index.css': string;
|
|
5
|
+
'/package.json': string;
|
|
6
|
+
};
|
|
7
|
+
export declare const react18Template: {
|
|
8
|
+
'/index.tsx': string;
|
|
9
|
+
'/App.tsx': string;
|
|
10
|
+
'/index.css': string;
|
|
11
|
+
'/package.json': string;
|
|
12
|
+
};
|
|
13
|
+
export declare const svelte5Template: {
|
|
14
|
+
'/index.ts': string;
|
|
15
|
+
'/App.svelte': string;
|
|
16
|
+
'/index.css': string;
|
|
17
|
+
'/package.json': string;
|
|
18
|
+
};
|
|
19
|
+
export declare const vueTemplate: {
|
|
20
|
+
'/index.ts': string;
|
|
21
|
+
'/App.vue': string;
|
|
22
|
+
'/index.css': string;
|
|
23
|
+
'/package.json': string;
|
|
24
|
+
};
|
|
25
|
+
export declare const FRAMEWORK_TEMPLATES: {
|
|
26
|
+
react19: {
|
|
27
|
+
'/index.tsx': string;
|
|
28
|
+
'/App.tsx': string;
|
|
29
|
+
'/index.css': string;
|
|
30
|
+
'/package.json': string;
|
|
31
|
+
};
|
|
32
|
+
react18: {
|
|
33
|
+
'/index.tsx': string;
|
|
34
|
+
'/App.tsx': string;
|
|
35
|
+
'/index.css': string;
|
|
36
|
+
'/package.json': string;
|
|
37
|
+
};
|
|
38
|
+
svelte5: {
|
|
39
|
+
'/index.ts': string;
|
|
40
|
+
'/App.svelte': string;
|
|
41
|
+
'/index.css': string;
|
|
42
|
+
'/package.json': string;
|
|
43
|
+
};
|
|
44
|
+
vue: {
|
|
45
|
+
'/index.ts': string;
|
|
46
|
+
'/App.vue': string;
|
|
47
|
+
'/index.css': string;
|
|
48
|
+
'/package.json': string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export type FrameworkKey = keyof typeof FRAMEWORK_TEMPLATES;
|
|
52
|
+
export declare const STARTER_RUNNABLE_KEY = "a";
|
|
53
|
+
export declare const STARTER_RUNNABLE: {
|
|
54
|
+
name: string;
|
|
55
|
+
fields: {};
|
|
56
|
+
type: string;
|
|
57
|
+
inlineScript: {
|
|
58
|
+
content: string;
|
|
59
|
+
language: string;
|
|
60
|
+
schema: {
|
|
61
|
+
$schema: string;
|
|
62
|
+
properties: {
|
|
63
|
+
x: {
|
|
64
|
+
default: null;
|
|
65
|
+
description: string;
|
|
66
|
+
originalType: string;
|
|
67
|
+
type: string;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
required: string[];
|
|
71
|
+
type: string;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
export declare const appVueRouter = "\n<template>\n <div class=\"container\">\n <!-- Navigation tabs -->\n <nav class=\"tabs\">\n <button \n v-for=\"tab in tabs\" \n :key=\"tab.id\"\n :class=\"{ active: currentTab === tab.id }\"\n @click=\"changeTab(tab.id)\"\n >\n {{ tab.name }}\n </button>\n </nav>\n\n <!-- Content sections -->\n <div class=\"content\">\n <div v-if=\"currentTab === 'home'\" class=\"tab-content\">\n <h2>Home</h2>\n <p>Welcome to the home tab!</p>\n <!-- Nested navigation example -->\n <div class=\"sub-nav\">\n <button \n v-for=\"subItem in ['latest', 'popular']\" \n :key=\"subItem\"\n :class=\"{ active: currentSort === subItem }\"\n @click=\"changeSort(subItem)\"\n >\n {{ subItem }}\n </button>\n </div>\n </div>\n\n <div v-else-if=\"currentTab === 'about'\" class=\"tab-content\">\n <h2>About</h2>\n <p>This is the about section</p>\n </div>\n\n <div v-else-if=\"currentTab === 'contact'\" class=\"tab-content\">\n <h2>Contact</h2>\n <p>Contact information here</p>\n </div>\n </div>\n\n <!-- Debug info -->\n <div class=\"debug\">\n <p>Current Tab: {{ currentTab }}</p>\n <p>Current Sort: {{ currentSort }}</p>\n <p>Current Hash: {{ currentHash }}</p>\n </div>\n </div>\n</template>\n\n<script setup>\nimport { ref, onMounted, onUnmounted } from 'vue';\n\n// Available tabs\nconst tabs = [\n { id: 'home', name: 'Home' },\n { id: 'about', name: 'About' },\n { id: 'contact', name: 'Contact' }\n];\n\nconst currentTab = ref('home');\nconst currentSort = ref('latest');\nconst currentHash = ref('');\n\n// Navigation functions\nfunction changeTab(tabId) {\n currentTab.value = tabId;\n updateHash();\n}\n\nfunction changeSort(sort) {\n currentSort.value = sort;\n updateHash();\n}\n\nfunction updateHash() {\n const params = new URLSearchParams();\n params.set('tab', currentTab.value);\n if (currentSort.value !== 'latest') {\n params.set('sort', currentSort.value);\n }\n window.location.hash = params.toString();\n currentHash.value = window.location.hash;\n}\n\nfunction parseHash() {\n const params = new URLSearchParams(window.location.hash.slice(1));\n currentTab.value = params.get('tab') || 'home';\n currentSort.value = params.get('sort') || 'latest';\n currentHash.value = window.location.hash;\n}\n\n// Hash change handler\nfunction handleHashChange() {\n parseHash();\n}\n\nonMounted(() => {\n if (window.location.hash) {\n parseHash();\n } else {\n updateHash();\n }\n window.addEventListener('hashchange', handleHashChange);\n});\n\nonUnmounted(() => {\n window.removeEventListener('hashchange', handleHashChange);\n});\n</script>\n\n<style scoped>\n.container {\n max-width: 800px;\n margin: 0 auto;\n padding: 20px;\n}\n\n.tabs {\n display: flex;\n gap: 10px;\n margin-bottom: 20px;\n}\n\nbutton {\n padding: 8px 16px;\n border: 1px solid #ddd;\n background: #fff;\n border-radius: 4px;\n cursor: pointer;\n}\n\nbutton.active {\n background: #4CAF50;\n color: white;\n border-color: #4CAF50;\n}\n\n.content {\n padding: 20px;\n border: 1px solid #ddd;\n border-radius: 4px;\n}\n\n.tab-content {\n margin-bottom: 20px;\n}\n\n.sub-nav {\n margin-top: 10px;\n}\n\n.debug {\n margin-top: 20px;\n padding: 10px;\n background: #f5f5f5;\n border-radius: 4px;\n font-size: 0.9em;\n color: #666;\n}\n</style>";
|
|
76
|
+
export declare const appSvelteRouter = "\n<script>\nimport { onMount, onDestroy } from 'svelte';\n\n// Available tabs\nconst tabs = [\n { id: 'home', name: 'Home' },\n { id: 'about', name: 'About' },\n { id: 'contact', name: 'Contact' }\n];\n\nlet currentTab = 'home';\nlet currentSort = 'latest';\nlet currentHash = '';\n\n// Navigation functions\nfunction changeTab(tabId) {\n currentTab = tabId;\n updateHash();\n}\n\nfunction changeSort(sort) {\n currentSort = sort;\n updateHash();\n}\n\nfunction updateHash() {\n const params = new URLSearchParams();\n params.set('tab', currentTab);\n if (currentSort !== 'latest') {\n params.set('sort', currentSort);\n }\n window.location.hash = params.toString();\n currentHash = window.location.hash;\n}\n\nfunction parseHash() {\n const params = new URLSearchParams(window.location.hash.slice(1));\n currentTab = params.get('tab') || 'home';\n currentSort = params.get('sort') || 'latest';\n currentHash = window.location.hash;\n}\n\n// Hash change handler\nfunction handleHashChange() {\n parseHash();\n}\n\nonMount(() => {\n if (window.location.hash) {\n parseHash();\n } else {\n updateHash();\n }\n window.addEventListener('hashchange', handleHashChange);\n});\n\nonDestroy(() => {\n window.removeEventListener('hashchange', handleHashChange);\n});\n</script>\n\n<div class=\"container\">\n <!-- Navigation tabs -->\n <nav class=\"tabs\">\n {#each tabs as tab}\n <button \n class:active={currentTab === tab.id}\n on:click={() => changeTab(tab.id)}\n >\n {tab.name}\n </button>\n {/each}\n </nav>\n\n <!-- Content sections -->\n <div class=\"content\">\n {#if currentTab === 'home'}\n <div class=\"tab-content\">\n <h2>Home</h2>\n <p>Welcome to the home tab!</p>\n <!-- Nested navigation example -->\n <div class=\"sub-nav\">\n {#each ['latest', 'popular'] as subItem}\n <button \n class:active={currentSort === subItem}\n on:click={() => changeSort(subItem)}\n >\n {subItem}\n </button>\n {/each}\n </div>\n </div>\n {:else if currentTab === 'about'}\n <div class=\"tab-content\">\n <h2>About</h2>\n <p>This is the about section</p>\n </div>\n {:else if currentTab === 'contact'}\n <div class=\"tab-content\">\n <h2>Contact</h2>\n <p>Contact information here</p>\n </div>\n {/if}\n </div>\n\n <!-- Debug info -->\n <div class=\"debug\">\n <p>Current Tab: {currentTab}</p>\n <p>Current Sort: {currentSort}</p>\n <p>Current Hash: {currentHash}</p>\n </div>\n</div>\n\n<style>\n.container {\n max-width: 800px;\n margin: 0 auto;\n padding: 20px;\n}\n\n.tabs {\n display: flex;\n gap: 10px;\n margin-bottom: 20px;\n}\n\nbutton {\n padding: 8px 16px;\n border: 1px solid #ddd;\n background: #fff;\n border-radius: 4px;\n cursor: pointer;\n}\n\nbutton.active {\n background: #4CAF50;\n color: white;\n border-color: #4CAF50;\n}\n\n.content {\n padding: 20px;\n border: 1px solid #ddd;\n border-radius: 4px;\n}\n\n.tab-content {\n margin-bottom: 20px;\n}\n\n.sub-nav {\n margin-top: 10px;\n}\n\n.debug {\n margin-top: 20px;\n padding: 10px;\n background: #f5f5f5;\n border-radius: 4px;\n font-size: 0.9em;\n color: #666;\n}\n</style>";
|
|
77
|
+
export declare const appReactRouter = "\nimport React, { useState, useEffect } from 'react';\n\nconst tabs = [\n { id: 'home', name: 'Home' },\n { id: 'about', name: 'About' },\n { id: 'contact', name: 'Contact' }\n];\n\nconst App = () => {\n const [currentTab, setCurrentTab] = useState('home');\n const [currentSort, setCurrentSort] = useState('latest');\n const [currentHash, setCurrentHash] = useState('');\n\n const updateHash = (newTab?: string, newSort?: string) => {\n const params = new URLSearchParams();\n params.set('tab', newTab ?? currentTab);\n if ((newSort ?? currentSort) !== 'latest') {\n params.set('sort', newSort ?? currentSort);\n }\n window.location.hash = params.toString();\n setCurrentHash(window.location.hash);\n if (newTab) setCurrentTab(newTab);\n if (newSort) setCurrentSort(newSort);\n };\n\n const parseHash = () => {\n const params = new URLSearchParams(window.location.hash.slice(1));\n setCurrentTab(params.get('tab') || 'home');\n setCurrentSort(params.get('sort') || 'latest');\n setCurrentHash(window.location.hash);\n };\n\n useEffect(() => {\n if (!window.location.hash) {\n updateHash(currentTab, currentSort);\n } else {\n parseHash();\n }\n\n const handleHashChange = () => parseHash();\n window.addEventListener('hashchange', handleHashChange);\n return () => window.removeEventListener('hashchange', handleHashChange);\n }, []);\n\n return (\n <div className=\"max-w-3xl mx-auto p-5\">\n <nav className=\"flex gap-3 mb-5\">\n {tabs.map(tab => (\n <button\n key={tab.id}\n onClick={() => updateHash(tab.id)}\n className={`px-4 py-2 border rounded-md cursor-pointer\n ${currentTab === tab.id \n ? 'bg-green-500 text-white border-green-500' \n : 'bg-white border-gray-300 hover:bg-gray-50'}`}\n >\n {tab.name}\n </button>\n ))}\n </nav>\n\n <div className=\"p-5 border rounded-md\">\n {currentTab === 'home' && (\n <div className=\"mb-5\">\n <h2 className=\"text-xl font-bold\">Home</h2>\n <p>Welcome to the home tab!</p>\n \n <div className=\"mt-3 flex gap-3\">\n {['latest', 'popular'].map(sort => (\n <button\n key={sort}\n onClick={() => updateHash(currentTab, sort)}\n className={`px-4 py-2 border rounded-md cursor-pointer\n ${\n\t\t\t\t\t\t\t\t\t\t\tcurrentSort === sort\n\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-green-500 text-white border-green-500'\n\t\t\t\t\t\t\t\t\t\t\t\t: 'bg-white border-gray-300 hover:bg-gray-50'\n\t\t\t\t\t\t\t\t\t\t}`}\n >\n {sort}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {currentTab === 'about' && (\n <div>\n <h2 className=\"text-xl font-bold\">About</h2>\n <p>This is the about section</p>\n </div>\n )}\n\n {currentTab === 'contact' && (\n <div>\n <h2 className=\"text-xl font-bold\">Contact</h2>\n <p>Contact information here</p>\n </div>\n )}\n </div>\n\n <div className=\"mt-5 p-3 bg-gray-100 rounded-md text-sm text-gray-600\">\n <p>Current Tab: {currentTab}</p>\n <p>Current Sort: {currentSort}</p>\n <p>Current Hash: {currentHash}</p>\n </div>\n </div>\n );\n};\n\nexport default App\n";
|