File size: 5,563 Bytes
b7897bb
 
8ce4d25
 
 
e0b54fb
b7897bb
 
 
 
 
 
 
e0b54fb
 
 
b7897bb
e0b54fb
 
b7897bb
 
 
 
 
fa270d9
b7897bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa270d9
b7897bb
fa270d9
 
 
 
b7897bb
 
 
 
 
 
 
 
 
fa270d9
 
b7897bb
fa270d9
 
 
 
 
 
 
 
 
 
 
b7897bb
fa270d9
 
 
 
 
 
b7897bb
fa270d9
 
 
b7897bb
 
 
 
 
 
 
 
 
 
fa270d9
 
b7897bb
fa270d9
 
b7897bb
 
 
 
 
8ce4d25
 
 
b7897bb
 
 
 
 
 
 
 
 
 
 
8ce4d25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ece4c70
fa270d9
 
ece4c70
 
8ce4d25
 
 
 
 
 
 
 
 
 
 
 
ece4c70
8ce4d25
 
 
2034346
8ce4d25
b7897bb
8ce4d25
 
 
 
b7897bb
8ce4d25
b7897bb
 
2034346
ece4c70
8ce4d25
e0b54fb
fa270d9
 
8ce4d25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
from fasthtml.components import Body, Div, Header, Img, Nav, Title
from fasthtml.xtend import A, Script
from lucide_fasthtml import Lucide
from shad4fast import Button, Separator

layout_script = Script(
    """
    document.addEventListener("DOMContentLoaded", function () {
          const main = document.querySelector('main');
          const aside = document.querySelector('aside');
          const body = document.body;
        
          if (main && aside && main.nextElementSibling === aside) {
            // If we have both main and aside, adjust the layout for larger screens
            body.classList.remove('grid-cols-1'); // Remove single-column layout
            body.classList.add('md:grid-cols-[minmax(0,_45fr)_minmax(0,_15fr)]'); // Two-column layout on larger screens
          } else if (main) {
            // If only main, keep it full width
            body.classList.add('grid-cols-1');
          }
    });
    """
)

overlay_scrollbars_manager = Script(
    """
    (function () {
        const { OverlayScrollbars } = OverlayScrollbarsGlobal;

        function getPreferredTheme() {
            return localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
                ? 'dark'
                : 'light';
        }

        function applyOverlayScrollbars(element, scrollbarTheme) {
            // Destroy existing OverlayScrollbars instance if it exists
            const instance = OverlayScrollbars(element);
            if (instance) {
                instance.destroy();
            }

            // Reinitialize OverlayScrollbars with the correct theme and settings
            OverlayScrollbars(element, {
                overflow: {
                    x: 'hidden',
                    y: 'scroll'
                },
                scrollbars: {
                    theme: scrollbarTheme,
                    visibility: 'auto',
                    autoHide: 'leave',
                    autoHideDelay: 800
                }
            });
        }

        // Function to get the current scrollbar theme (light or dark)
        function getScrollbarTheme() {
            const isDarkMode = getPreferredTheme() === 'dark';
            return isDarkMode ? 'os-theme-light' : 'os-theme-dark';  // Light theme in dark mode, dark theme in light mode
        }

        // Expose the common functions globally for reuse
        window.OverlayScrollbarsManager = {
            applyOverlayScrollbars: applyOverlayScrollbars,
            getScrollbarTheme: getScrollbarTheme
        };
    })();
    """
)

static_elements_scrollbars = Script(
    """
    (function () {
        const { applyOverlayScrollbars, getScrollbarTheme } = OverlayScrollbarsManager;

        function applyScrollbarsToStaticElements() {
            const mainElement = document.querySelector('main');
            const chatMessagesElement = document.querySelector('#chat-messages');

            const scrollbarTheme = getScrollbarTheme();

            if (mainElement) {
                applyOverlayScrollbars(mainElement, scrollbarTheme);
            }

            if (chatMessagesElement) {
                applyOverlayScrollbars(chatMessagesElement, scrollbarTheme);
            }
        }

        // Apply the scrollbars on page load
        applyScrollbarsToStaticElements();

        // Observe changes in the 'dark' class on the <html> element to adjust the theme dynamically
        const observer = new MutationObserver(applyScrollbarsToStaticElements);
        observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
    })();
    """
)


def Logo():
    return Div(
        Img(
            src="https://assets.vespa.ai/logos/vespa-logo-black.svg",
            alt="Vespa Logo",
            cls="h-full dark:hidden",
        ),
        Img(
            src="https://assets.vespa.ai/logos/vespa-logo-white.svg",
            alt="Vespa Logo Dark Mode",
            cls="h-full hidden dark:block",
        ),
        cls="h-[27px]",
    )


def ThemeToggle(variant="ghost", cls=None, **kwargs):
    return Button(
        Lucide("sun", cls="dark:flex hidden"),
        Lucide("moon", cls="dark:hidden"),
        variant=variant,
        size="icon",
        cls=f"theme-toggle {cls}",
        **kwargs,
    )


def Links():
    return Nav(
        A(
            Button("About this demo?", variant="link"),
            href="/about-this-demo",
        ),
        Separator(orientation="vertical"),
        A(
            Button(Lucide(icon="github"), size="icon", variant="ghost"),
            href="https://github.com/vespa-engine/vespa",
            target="_blank",
        ),
        A(
            Button(Lucide(icon="slack"), size="icon", variant="ghost"),
            href="https://slack.vespa.ai",
            target="_blank",
        ),
        Separator(orientation="vertical"),
        ThemeToggle(),
        cls="flex items-center space-x-2",
    )


def Layout(*c, is_home=False, **kwargs):
    return (
        Title("Visual Retrieval ColPali"),
        Body(
            Header(
                A(Logo(), href="/"),
                Links(),
                cls="min-h-[55px] h-[55px] w-full flex items-center justify-between px-4",
            ),
            *c,
            **kwargs,
            data_is_home=str(is_home).lower(),
            cls="grid grid-rows-[minmax(0,55px)_minmax(0,1fr)] min-h-0",
        ),
        layout_script,
        overlay_scrollbars_manager,
        static_elements_scrollbars,
    )