Configure Docker publishing with correct GitHub username

This commit is contained in:
Tomas Dvorak
2026-02-27 17:34:20 +01:00
parent 4c812e376d
commit 0a80ecd9f7
138 changed files with 12130 additions and 7831 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ on:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
IMAGE_NAME: Dvorinka/trackeep
jobs:
test:
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,18 @@
- generic [ref=e26]:
- generic [ref=e27]:
- img "Trackeep Logo" [ref=e29]
- heading "Trackeep" [level=1] [ref=e30]
- paragraph [ref=e31]: Welcome back
- generic [ref=e32]:
- generic [ref=e35]: Registration Disabled
- paragraph [ref=e36]: Accounts can only be created by the administrator. Please contact your admin to get an account.
- generic [ref=e37]:
- generic [ref=e38]:
- generic [ref=e39]: Email
- textbox "Email" [ref=e40]:
- /placeholder: your@email.com
- generic [ref=e41]:
- generic [ref=e42]: Password
- textbox "Password" [ref=e43]:
- /placeholder: ••••••••
- button "Sign In" [ref=e44] [cursor=pointer]
@@ -0,0 +1,21 @@
- generic [ref=e26]:
- generic [ref=e27]:
- img "Trackeep Logo" [ref=e29]
- heading "Trackeep" [level=1] [ref=e30]
- paragraph [ref=e31]: Welcome back
- generic [ref=e32]:
- generic [ref=e35]: Registration Disabled
- paragraph [ref=e36]: Accounts can only be created by the administrator. Please contact your admin to get an account.
- generic [ref=e37]:
- generic [ref=e45]: Invalid credentials
- generic [ref=e38]:
- generic [ref=e39]: Email
- textbox "Email" [ref=e40]:
- /placeholder: your@email.com
- text: demo@trackeep.com
- generic [ref=e41]:
- generic [ref=e42]: Password
- textbox "Password" [ref=e43]:
- /placeholder: ••••••••
- text: password
- button "Sign In" [ref=e44] [cursor=pointer]
@@ -0,0 +1 @@
- paragraph [ref=e6]: Checking authentication...
@@ -0,0 +1 @@
- paragraph [ref=e6]: Checking authentication...
@@ -0,0 +1 @@
- paragraph [ref=e6]: Checking authentication...
@@ -0,0 +1 @@
- paragraph [ref=e6]: Checking authentication...
@@ -0,0 +1 @@
- paragraph [ref=e6]: Checking authentication...
@@ -0,0 +1 @@
- paragraph [ref=e6]: Checking authentication...
@@ -0,0 +1 @@
- paragraph [ref=e6]: Checking authentication...
@@ -0,0 +1 @@
- paragraph [ref=e6]: Checking authentication...
@@ -0,0 +1 @@
- paragraph [ref=e6]: Checking authentication...
@@ -0,0 +1 @@
- paragraph [ref=e6]: Checking authentication...
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,18 @@
- generic [ref=e4]:
- generic [ref=e5]:
- img "Trackeep Logo" [ref=e7]
- heading "Trackeep" [level=1] [ref=e8]
- paragraph [ref=e9]: Welcome back
- generic [ref=e10]:
- generic [ref=e13]: Registration Disabled
- paragraph [ref=e14]: Accounts can only be created by the administrator. Please contact your admin to get an account.
- generic [ref=e15]:
- generic [ref=e16]:
- generic [ref=e17]: Email
- textbox "Email" [ref=e18]:
- /placeholder: your@email.com
- generic [ref=e19]:
- generic [ref=e20]: Password
- textbox "Password" [ref=e21]:
- /placeholder: ••••••••
- button "Sign In" [ref=e22] [cursor=pointer]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,18 @@
- generic [ref=e4]:
- generic [ref=e5]:
- img "Trackeep Logo" [ref=e7]
- heading "Trackeep" [level=1] [ref=e8]
- paragraph [ref=e9]: Welcome back
- generic [ref=e10]:
- generic [ref=e13]: Registration Disabled
- paragraph [ref=e14]: Accounts can only be created by the administrator. Please contact your admin to get an account.
- generic [ref=e15]:
- generic [ref=e16]:
- generic [ref=e17]: Email
- textbox "Email" [ref=e18]:
- /placeholder: your@email.com
- generic [ref=e19]:
- generic [ref=e20]: Password
- textbox "Password" [ref=e21]:
- /placeholder: ••••••••
- button "Sign In" [ref=e22] [cursor=pointer]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,237 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:21 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,237 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:21 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,237 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:22 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,257 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:22 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
- generic [ref=e279]:
- generic [ref=e280]:
- heading "Create Workspace" [level=3] [ref=e281]
- paragraph [ref=e282]: Add a new workspace for your team or projects.
- generic [ref=e283]:
- generic [ref=e284]:
- text: Name
- textbox "Workspace name" [ref=e285]
- generic [ref=e286]:
- text: Description
- textbox "Description" [ref=e287]:
- /placeholder: Optional description
- generic [ref=e288]:
- generic [ref=e289]:
- paragraph [ref=e290]: Public workspace
- paragraph [ref=e291]: Allow all members to discover this workspace.
- switch [ref=e292] [cursor=pointer]
- generic [ref=e293]:
- button "Cancel" [ref=e294] [cursor=pointer]
- button "Create Workspace" [ref=e295] [cursor=pointer]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,237 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:22 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,244 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [expanded] [active] [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- listbox [ref=e266]:
- option "Trackeep Workspace" [ref=e267] [cursor=pointer]:
- img [ref=e268]
- generic [ref=e271]: Trackeep Workspace
- button "Create Workspace" [ref=e274] [cursor=pointer]:
- img [ref=e275]
- generic [ref=e276]: Create Workspace
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:22 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,244 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [expanded] [active] [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- listbox [ref=e266]:
- option "Trackeep Workspace" [ref=e267] [cursor=pointer]:
- img [ref=e268]
- generic [ref=e271]: Trackeep Workspace
- button "Create Workspace" [ref=e274] [cursor=pointer]:
- img [ref=e275]
- generic [ref=e276]: Create Workspace
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:22 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,237 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:23 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,244 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [expanded] [active] [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- listbox [ref=e266]:
- option "Trackeep Workspace" [ref=e267] [cursor=pointer]:
- img [ref=e268]
- generic [ref=e271]: Trackeep Workspace
- button "Create Workspace" [ref=e274] [cursor=pointer]:
- img [ref=e275]
- generic [ref=e276]: Create Workspace
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:23 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,257 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:23 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
- generic [ref=e279]:
- generic [ref=e280]:
- heading "Create Workspace" [level=3] [ref=e281]
- paragraph [ref=e282]: Add a new workspace for your team or projects.
- generic [ref=e283]:
- generic [ref=e284]:
- text: Name
- textbox "Workspace name" [ref=e285]
- generic [ref=e286]:
- text: Description
- textbox "Description" [ref=e287]:
- /placeholder: Optional description
- generic [ref=e288]:
- generic [ref=e289]:
- paragraph [ref=e290]: Public workspace
- paragraph [ref=e291]: Allow all members to discover this workspace.
- switch [ref=e292] [cursor=pointer]
- generic [ref=e293]:
- button "Cancel" [ref=e294] [cursor=pointer]
- button "Create Workspace" [ref=e295] [cursor=pointer]
@@ -0,0 +1,257 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:23 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
- generic [ref=e279]:
- generic [ref=e280]:
- heading "Create Workspace" [level=3] [ref=e281]
- paragraph [ref=e282]: Add a new workspace for your team or projects.
- generic [ref=e283]:
- generic [ref=e284]:
- text: Name
- textbox "Workspace name" [ref=e285]
- generic [ref=e286]:
- text: Description
- textbox "Description" [ref=e287]:
- /placeholder: Optional description
- generic [ref=e288]:
- generic [ref=e289]:
- paragraph [ref=e290]: Public workspace
- paragraph [ref=e291]: Allow all members to discover this workspace.
- switch [ref=e292] [cursor=pointer]
- generic [ref=e293]:
- button "Cancel" [ref=e294] [cursor=pointer]
- button "Create Workspace" [ref=e295] [cursor=pointer]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,237 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:23 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,263 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- generic [ref=e178]:
- button "AU" [active] [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- generic [ref=e267]:
- generic [ref=e269]:
- generic [ref=e270]: AU
- generic [ref=e271]:
- paragraph [ref=e272]: Admin User
- paragraph [ref=e273]: admin@trackeep.com
- generic [ref=e275]:
- generic [ref=e276]:
- paragraph [ref=e277]: "0"
- paragraph [ref=e278]: Bookmarks
- generic [ref=e279]:
- paragraph [ref=e280]: "0"
- paragraph [ref=e281]: Tasks
- button "Profile" [ref=e282] [cursor=pointer]:
- img [ref=e283]
- text: Profile
- button "Statistics" [ref=e286] [cursor=pointer]:
- img [ref=e287]
- text: Statistics
- button "Settings" [ref=e289] [cursor=pointer]:
- img [ref=e290]
- text: Settings
- button "Logout" [ref=e294] [cursor=pointer]:
- img [ref=e295]
- text: Logout
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:23 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,263 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- generic [ref=e178]:
- button "AU" [active] [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- generic [ref=e267]:
- generic [ref=e269]:
- generic [ref=e270]: AU
- generic [ref=e271]:
- paragraph [ref=e272]: Admin User
- paragraph [ref=e273]: admin@trackeep.com
- generic [ref=e275]:
- generic [ref=e276]:
- paragraph [ref=e277]: "0"
- paragraph [ref=e278]: Bookmarks
- generic [ref=e279]:
- paragraph [ref=e280]: "0"
- paragraph [ref=e281]: Tasks
- button "Profile" [ref=e282] [cursor=pointer]:
- img [ref=e283]
- text: Profile
- button "Statistics" [ref=e286] [cursor=pointer]:
- img [ref=e287]
- text: Statistics
- button "Settings" [ref=e289] [cursor=pointer]:
- img [ref=e290]
- text: Settings
- button "Logout" [ref=e294] [cursor=pointer]:
- img [ref=e295]
- text: Logout
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Tasks" [level=1] [ref=e188]
- button "Add Task" [ref=e189] [cursor=pointer]
- generic [ref=e190]:
- generic [ref=e191]:
- paragraph [ref=e192]: "0"
- paragraph [ref=e193]: Total Tasks
- generic [ref=e194]:
- paragraph [ref=e195]: "0"
- paragraph [ref=e196]: Active
- generic [ref=e197]:
- paragraph [ref=e198]: "0"
- paragraph [ref=e199]: Completed
- generic [ref=e201]:
- textbox "Search tasks..." [ref=e202]
- combobox [ref=e203]:
- option "All Priorities" [selected]
- option "high"
- option "medium"
- option "low"
- generic [ref=e204]:
- button "all" [ref=e205] [cursor=pointer]
- button "active" [ref=e206] [cursor=pointer]
- button "completed" [ref=e207] [cursor=pointer]
- paragraph [ref=e210]: No tasks yet. Add your first task!
- button "AI Assistant" [ref=e211] [cursor=pointer]:
- img [ref=e212]
- generic [ref=e219]:
- generic [ref=e220]:
- generic [ref=e221]:
- img [ref=e223]
- generic [ref=e230]:
- heading "AI Assistant" [level=3] [ref=e231]
- paragraph [ref=e232]: Always here to help
- button [ref=e234] [cursor=pointer]:
- img [ref=e235]
- generic [ref=e239]:
- img [ref=e241]
- generic [ref=e248]:
- paragraph [ref=e249]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e250]: 04:23 PM
- generic [ref=e251]:
- generic [ref=e252]:
- textbox "Type your message..." [ref=e253]
- button [disabled]:
- img
- generic [ref=e255]:
- button "longcat icon LongCat" [ref=e257] [cursor=pointer]:
- img "longcat icon" [ref=e258]
- generic [ref=e259]: LongCat
- img [ref=e260]
- generic [ref=e262]:
- generic [ref=e263]: longcat
- link "AI settings" [ref=e264] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- heading "Add New Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Add Task" [disabled]
- generic:
- generic:
- generic:
- heading "Edit Task" [level=3]
- button:
- img
- generic:
- textbox "Task title *"
- textbox "Description (optional)"
- generic:
- combobox:
- option "Low Priority"
- option "Medium Priority" [selected]
- option "High Priority"
- generic:
- button "Due date (optional)":
- generic: Due date (optional)
- img
- generic:
- button "Cancel"
- button "Save Changes" [disabled]
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,197 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Files" [level=1] [ref=e188]
- button "Upload File" [ref=e189] [cursor=pointer]:
- img [ref=e190]
- text: Upload File
- generic [ref=e194]:
- textbox "Search files..." [ref=e195]
- combobox [ref=e196]:
- option "All Tags" [selected]
- paragraph [ref=e198]: No files uploaded yet. Upload your first file!
- button "AI Assistant" [ref=e199] [cursor=pointer]:
- img [ref=e200]
- generic [ref=e207]:
- generic [ref=e208]:
- generic [ref=e209]:
- img [ref=e211]
- generic [ref=e218]:
- heading "AI Assistant" [level=3] [ref=e219]
- paragraph [ref=e220]: Always here to help
- button [ref=e222] [cursor=pointer]:
- img [ref=e223]
- generic [ref=e227]:
- img [ref=e229]
- generic [ref=e236]:
- paragraph [ref=e237]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e238]: 04:24 PM
- generic [ref=e239]:
- generic [ref=e240]:
- textbox "Type your message..." [ref=e241]
- button [disabled]:
- img
- generic [ref=e243]:
- button "longcat icon LongCat" [ref=e245] [cursor=pointer]:
- img "longcat icon" [ref=e246]
- generic [ref=e247]: LongCat
- img [ref=e248]
- generic [ref=e250]:
- generic [ref=e251]: longcat
- link "AI settings" [ref=e252] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- generic:
- heading [level=3]
- generic: Unknown size
- button:
- img
- generic:
- generic: Unknown file type
- generic:
- button "Download":
- img
- text: Download
- button "Open":
- img
- text: Open
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,197 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Files" [level=1] [ref=e188]
- button "Upload File" [ref=e189] [cursor=pointer]:
- img [ref=e190]
- text: Upload File
- generic [ref=e194]:
- textbox "Search files..." [ref=e195]
- combobox [ref=e196]:
- option "All Tags" [selected]
- paragraph [ref=e198]: No files uploaded yet. Upload your first file!
- button "AI Assistant" [ref=e199] [cursor=pointer]:
- img [ref=e200]
- generic [ref=e207]:
- generic [ref=e208]:
- generic [ref=e209]:
- img [ref=e211]
- generic [ref=e218]:
- heading "AI Assistant" [level=3] [ref=e219]
- paragraph [ref=e220]: Always here to help
- button [ref=e222] [cursor=pointer]:
- img [ref=e223]
- generic [ref=e227]:
- img [ref=e229]
- generic [ref=e236]:
- paragraph [ref=e237]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e238]: 04:24 PM
- generic [ref=e239]:
- generic [ref=e240]:
- textbox "Type your message..." [ref=e241]
- button [disabled]:
- img
- generic [ref=e243]:
- button "longcat icon LongCat" [ref=e245] [cursor=pointer]:
- img "longcat icon" [ref=e246]
- generic [ref=e247]: LongCat
- img [ref=e248]
- generic [ref=e250]:
- generic [ref=e251]: longcat
- link "AI settings" [ref=e252] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- generic:
- heading [level=3]
- generic: Unknown size
- button:
- img
- generic:
- generic: Unknown file type
- generic:
- button "Download":
- img
- text: Download
- button "Open":
- img
- text: Open
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,197 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Files" [level=1] [ref=e188]
- button "Upload File" [ref=e189] [cursor=pointer]:
- img [ref=e190]
- text: Upload File
- generic [ref=e194]:
- textbox "Search files..." [ref=e195]
- combobox [ref=e196]:
- option "All Tags" [selected]
- paragraph [ref=e198]: No files uploaded yet. Upload your first file!
- button "AI Assistant" [ref=e199] [cursor=pointer]:
- img [ref=e200]
- generic [ref=e207]:
- generic [ref=e208]:
- generic [ref=e209]:
- img [ref=e211]
- generic [ref=e218]:
- heading "AI Assistant" [level=3] [ref=e219]
- paragraph [ref=e220]: Always here to help
- button [ref=e222] [cursor=pointer]:
- img [ref=e223]
- generic [ref=e227]:
- img [ref=e229]
- generic [ref=e236]:
- paragraph [ref=e237]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e238]: 04:24 PM
- generic [ref=e239]:
- generic [ref=e240]:
- textbox "Type your message..." [ref=e241]
- button [disabled]:
- img
- generic [ref=e243]:
- button "longcat icon LongCat" [ref=e245] [cursor=pointer]:
- img "longcat icon" [ref=e246]
- generic [ref=e247]: LongCat
- img [ref=e248]
- generic [ref=e250]:
- generic [ref=e251]: longcat
- link "AI settings" [ref=e252] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- generic:
- heading [level=3]
- generic: Unknown size
- button:
- img
- generic:
- generic: Unknown file type
- generic:
- button "Download":
- img
- text: Download
- button "Open":
- img
- text: Open
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,197 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Files" [level=1] [ref=e188]
- button "Upload File" [active] [ref=e189] [cursor=pointer]:
- img [ref=e190]
- text: Upload File
- generic [ref=e194]:
- textbox "Search files..." [ref=e195]
- combobox [ref=e196]:
- option "All Tags" [selected]
- paragraph [ref=e198]: No files uploaded yet. Upload your first file!
- button "AI Assistant" [ref=e199] [cursor=pointer]:
- img [ref=e200]
- generic [ref=e207]:
- generic [ref=e208]:
- generic [ref=e209]:
- img [ref=e211]
- generic [ref=e218]:
- heading "AI Assistant" [level=3] [ref=e219]
- paragraph [ref=e220]: Always here to help
- button [ref=e222] [cursor=pointer]:
- img [ref=e223]
- generic [ref=e227]:
- img [ref=e229]
- generic [ref=e236]:
- paragraph [ref=e237]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e238]: 04:24 PM
- generic [ref=e239]:
- generic [ref=e240]:
- textbox "Type your message..." [ref=e241]
- button [disabled]:
- img
- generic [ref=e243]:
- button "longcat icon LongCat" [ref=e245] [cursor=pointer]:
- img "longcat icon" [ref=e246]
- generic [ref=e247]: LongCat
- img [ref=e248]
- generic [ref=e250]:
- generic [ref=e251]: longcat
- link "AI settings" [ref=e252] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- generic:
- heading [level=3]
- generic: Unknown size
- button:
- img
- generic:
- generic: Unknown file type
- generic:
- button "Download":
- img
- text: Download
- button "Open":
- img
- text: Open
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,197 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Files" [level=1] [ref=e188]
- button "Upload File" [active] [ref=e189] [cursor=pointer]:
- img [ref=e190]
- text: Upload File
- generic [ref=e194]:
- textbox "Search files..." [ref=e195]
- combobox [ref=e196]:
- option "All Tags" [selected]
- paragraph [ref=e198]: No files uploaded yet. Upload your first file!
- button "AI Assistant" [ref=e199] [cursor=pointer]:
- img [ref=e200]
- generic [ref=e207]:
- generic [ref=e208]:
- generic [ref=e209]:
- img [ref=e211]
- generic [ref=e218]:
- heading "AI Assistant" [level=3] [ref=e219]
- paragraph [ref=e220]: Always here to help
- button [ref=e222] [cursor=pointer]:
- img [ref=e223]
- generic [ref=e227]:
- img [ref=e229]
- generic [ref=e236]:
- paragraph [ref=e237]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e238]: 04:24 PM
- generic [ref=e239]:
- generic [ref=e240]:
- textbox "Type your message..." [ref=e241]
- button [disabled]:
- img
- generic [ref=e243]:
- button "longcat icon LongCat" [ref=e245] [cursor=pointer]:
- img "longcat icon" [ref=e246]
- generic [ref=e247]: LongCat
- img [ref=e248]
- generic [ref=e250]:
- generic [ref=e251]: longcat
- link "AI settings" [ref=e252] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- generic:
- heading [level=3]
- generic: Unknown size
- button:
- img
- generic:
- generic: Unknown file type
- generic:
- button "Download":
- img
- text: Download
- button "Open":
- img
- text: Open
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,197 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Files" [level=1] [ref=e188]
- button "Upload File" [ref=e189] [cursor=pointer]:
- img [ref=e190]
- text: Upload File
- generic [ref=e194]:
- textbox "Search files..." [ref=e195]
- combobox [ref=e196]:
- option "All Tags" [selected]
- paragraph [ref=e198]: No files uploaded yet. Upload your first file!
- button "AI Assistant" [ref=e199] [cursor=pointer]:
- img [ref=e200]
- generic [ref=e207]:
- generic [ref=e208]:
- generic [ref=e209]:
- img [ref=e211]
- generic [ref=e218]:
- heading "AI Assistant" [level=3] [ref=e219]
- paragraph [ref=e220]: Always here to help
- button [ref=e222] [cursor=pointer]:
- img [ref=e223]
- generic [ref=e227]:
- img [ref=e229]
- generic [ref=e236]:
- paragraph [ref=e237]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e238]: 04:25 PM
- generic [ref=e239]:
- generic [ref=e240]:
- textbox "Type your message..." [ref=e241]
- button [disabled]:
- img
- generic [ref=e243]:
- button "longcat icon LongCat" [ref=e245] [cursor=pointer]:
- img "longcat icon" [ref=e246]
- generic [ref=e247]: LongCat
- img [ref=e248]
- generic [ref=e250]:
- generic [ref=e251]: longcat
- link "AI settings" [ref=e252] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- generic:
- heading [level=3]
- generic: Unknown size
- button:
- img
- generic:
- generic: Unknown file type
- generic:
- button "Download":
- img
- text: Download
- button "Open":
- img
- text: Open
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,197 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Files" [level=1] [ref=e188]
- button "Upload File" [active] [ref=e189] [cursor=pointer]:
- img [ref=e190]
- text: Upload File
- generic [ref=e194]:
- textbox "Search files..." [ref=e195]
- combobox [ref=e196]:
- option "All Tags" [selected]
- paragraph [ref=e198]: No files uploaded yet. Upload your first file!
- button "AI Assistant" [ref=e199] [cursor=pointer]:
- img [ref=e200]
- generic [ref=e207]:
- generic [ref=e208]:
- generic [ref=e209]:
- img [ref=e211]
- generic [ref=e218]:
- heading "AI Assistant" [level=3] [ref=e219]
- paragraph [ref=e220]: Always here to help
- button [ref=e222] [cursor=pointer]:
- img [ref=e223]
- generic [ref=e227]:
- img [ref=e229]
- generic [ref=e236]:
- paragraph [ref=e237]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e238]: 04:25 PM
- generic [ref=e239]:
- generic [ref=e240]:
- textbox "Type your message..." [ref=e241]
- button [disabled]:
- img
- generic [ref=e243]:
- button "longcat icon LongCat" [ref=e245] [cursor=pointer]:
- img "longcat icon" [ref=e246]
- generic [ref=e247]: LongCat
- img [ref=e248]
- generic [ref=e250]:
- generic [ref=e251]: longcat
- link "AI settings" [ref=e252] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- generic:
- heading [level=3]
- generic: Unknown size
- button:
- img
- generic:
- generic: Unknown file type
- generic:
- button "Download":
- img
- text: Download
- button "Open":
- img
- text: Open
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,197 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Files" [level=1] [ref=e188]
- button "Upload File" [active] [ref=e189] [cursor=pointer]:
- img [ref=e190]
- text: Upload File
- generic [ref=e194]:
- textbox "Search files..." [ref=e195]
- combobox [ref=e196]:
- option "All Tags" [selected]
- paragraph [ref=e198]: No files uploaded yet. Upload your first file!
- button "AI Assistant" [ref=e199] [cursor=pointer]:
- img [ref=e200]
- generic [ref=e207]:
- generic [ref=e208]:
- generic [ref=e209]:
- img [ref=e211]
- generic [ref=e218]:
- heading "AI Assistant" [level=3] [ref=e219]
- paragraph [ref=e220]: Always here to help
- button [ref=e222] [cursor=pointer]:
- img [ref=e223]
- generic [ref=e227]:
- img [ref=e229]
- generic [ref=e236]:
- paragraph [ref=e237]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e238]: 04:25 PM
- generic [ref=e239]:
- generic [ref=e240]:
- textbox "Type your message..." [ref=e241]
- button [disabled]:
- img
- generic [ref=e243]:
- button "longcat icon LongCat" [ref=e245] [cursor=pointer]:
- img "longcat icon" [ref=e246]
- generic [ref=e247]: LongCat
- img [ref=e248]
- generic [ref=e250]:
- generic [ref=e251]: longcat
- link "AI settings" [ref=e252] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- generic:
- heading [level=3]
- generic: Unknown size
- button:
- img
- generic:
- generic: Unknown file type
- generic:
- button "Download":
- img
- text: Download
- button "Open":
- img
- text: Open
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,14 @@
- generic [ref=e5]:
- generic [ref=e7]:
- img "Trackeep Logo" [ref=e10]
- heading "Authentication Required" [level=1] [ref=e11]
- paragraph [ref=e12]: Please sign in to access Trackeep
- generic [ref=e13]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e19]:
- heading "Authentication Required" [level=3] [ref=e20]
- paragraph [ref=e21]: You need to be authenticated to access this page. Please sign in or create an account to continue.
- generic [ref=e22]:
- button "Sign In" [ref=e23] [cursor=pointer]
- button "Create Account" [ref=e24] [cursor=pointer]
@@ -0,0 +1,197 @@
- generic [active] [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Files" [level=1] [ref=e188]
- button "Upload File" [ref=e189] [cursor=pointer]:
- img [ref=e190]
- text: Upload File
- generic [ref=e194]:
- textbox "Search files..." [ref=e195]
- combobox [ref=e196]:
- option "All Tags" [selected]
- paragraph [ref=e198]: No files uploaded yet. Upload your first file!
- button "AI Assistant" [ref=e199] [cursor=pointer]:
- img [ref=e200]
- generic [ref=e207]:
- generic [ref=e208]:
- generic [ref=e209]:
- img [ref=e211]
- generic [ref=e218]:
- heading "AI Assistant" [level=3] [ref=e219]
- paragraph [ref=e220]: Always here to help
- button [ref=e222] [cursor=pointer]:
- img [ref=e223]
- generic [ref=e227]:
- img [ref=e229]
- generic [ref=e236]:
- paragraph [ref=e237]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e238]: 04:26 PM
- generic [ref=e239]:
- generic [ref=e240]:
- textbox "Type your message..." [ref=e241]
- button [disabled]:
- img
- generic [ref=e243]:
- button "longcat icon LongCat" [ref=e245] [cursor=pointer]:
- img "longcat icon" [ref=e246]
- generic [ref=e247]: LongCat
- img [ref=e248]
- generic [ref=e250]:
- generic [ref=e251]: longcat
- link "AI settings" [ref=e252] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- generic:
- heading [level=3]
- generic: Unknown size
- button:
- img
- generic:
- generic: Unknown file type
- generic:
- button "Download":
- img
- text: Download
- button "Open":
- img
- text: Open
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
@@ -0,0 +1,197 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e7]:
- link "Trackeep Logo Trackeep" [ref=e9] [cursor=pointer]:
- /url: /app
- img "Trackeep Logo" [ref=e10]
- generic [ref=e11]: Trackeep
- group [ref=e13]:
- button "Trackeep Workspace" [ref=e14] [cursor=pointer]:
- generic [ref=e15]:
- img [ref=e17]
- generic [ref=e20]: Trackeep Workspace
- img [ref=e22]
- navigation [ref=e24]:
- link "Home" [ref=e25] [cursor=pointer]:
- /url: /app
- generic [ref=e26]:
- img [ref=e27]
- generic [ref=e31]: Home
- link "Bookmarks" [ref=e33] [cursor=pointer]:
- /url: /app/bookmarks
- generic [ref=e34]:
- img [ref=e35]
- generic [ref=e37]: Bookmarks
- link "Tasks" [ref=e39] [cursor=pointer]:
- /url: /app/tasks
- generic [ref=e40]:
- img [ref=e41]
- generic [ref=e44]: Tasks
- link "Time Tracking" [ref=e46] [cursor=pointer]:
- /url: /app/time-tracking
- generic [ref=e47]:
- img [ref=e48]
- generic [ref=e51]: Time Tracking
- link "Calendar" [ref=e53] [cursor=pointer]:
- /url: /app/calendar
- generic [ref=e54]:
- img [ref=e55]
- generic [ref=e57]: Calendar
- link "Files" [ref=e59] [cursor=pointer]:
- /url: /app/files
- generic [ref=e60]:
- img [ref=e61]
- generic [ref=e63]: Files
- link "Notes" [ref=e65] [cursor=pointer]:
- /url: /app/notes
- generic [ref=e66]:
- img [ref=e67]
- generic [ref=e69]: Notes
- link "Messages" [ref=e71] [cursor=pointer]:
- /url: /app/messages
- generic [ref=e72]:
- img [ref=e73]
- generic [ref=e75]: Messages
- link "YouTube" [ref=e77] [cursor=pointer]:
- /url: /app/youtube
- generic [ref=e78]:
- img [ref=e79]
- generic [ref=e82]: YouTube
- link "Members" [ref=e84] [cursor=pointer]:
- /url: /app/members
- generic [ref=e85]:
- img [ref=e86]
- generic [ref=e91]: Members
- link "Learning" [ref=e93] [cursor=pointer]:
- /url: /app/learning-paths
- generic [ref=e94]:
- img [ref=e95]
- generic [ref=e98]: Learning
- link "Stats" [ref=e100] [cursor=pointer]:
- /url: /app/stats
- generic [ref=e101]:
- img [ref=e102]
- generic [ref=e104]: Stats
- link "GitHub" [ref=e106] [cursor=pointer]:
- /url: /app/github
- generic [ref=e107]:
- img [ref=e108]
- generic [ref=e110]: GitHub
- link "AI Assistant" [ref=e112] [cursor=pointer]:
- /url: /app/chat
- generic [ref=e113]:
- img [ref=e114]
- generic [ref=e121]: AI Assistant
- generic [ref=e124]:
- generic [ref=e125]: Version 1.0.0
- button "Update Failed" [ref=e126] [cursor=pointer]:
- generic [ref=e127]:
- img [ref=e128]
- generic [ref=e130]: Update Failed
- navigation [ref=e132]:
- link "Removed stuff" [ref=e133] [cursor=pointer]:
- /url: /app/removed-stuff
- generic [ref=e134]:
- img [ref=e135]
- generic [ref=e138]: Removed stuff
- link "Settings" [ref=e140] [cursor=pointer]:
- /url: /app/settings
- generic [ref=e141]:
- img [ref=e142]
- generic [ref=e145]: Settings
- button "Logout" [ref=e147] [cursor=pointer]:
- generic [ref=e148]:
- img [ref=e149]
- generic [ref=e153]: Logout
- generic [ref=e155]:
- generic [ref=e156]:
- generic [ref=e157]:
- button [ref=e158] [cursor=pointer]:
- img [ref=e159]
- button "Quick search" [ref=e160] [cursor=pointer]:
- img [ref=e161]
- text: Quick search
- generic [ref=e164]:
- button "Import a document" [ref=e165] [cursor=pointer]:
- img [ref=e166]
- text: Import a document
- button [ref=e170] [cursor=pointer]:
- img [ref=e171]
- img [ref=e176]
- button "AU" [ref=e180] [cursor=pointer]:
- generic [ref=e181]: AU
- img [ref=e182]
- main [ref=e184]:
- generic [ref=e186]:
- generic [ref=e187]:
- heading "Files" [level=1] [ref=e188]
- button "Upload File" [active] [ref=e189] [cursor=pointer]:
- img [ref=e190]
- text: Upload File
- generic [ref=e194]:
- textbox "Search files..." [ref=e195]
- combobox [ref=e196]:
- option "All Tags" [selected]
- paragraph [ref=e198]: No files uploaded yet. Upload your first file!
- button "AI Assistant" [ref=e199] [cursor=pointer]:
- img [ref=e200]
- generic [ref=e207]:
- generic [ref=e208]:
- generic [ref=e209]:
- img [ref=e211]
- generic [ref=e218]:
- heading "AI Assistant" [level=3] [ref=e219]
- paragraph [ref=e220]: Always here to help
- button [ref=e222] [cursor=pointer]:
- img [ref=e223]
- generic [ref=e227]:
- img [ref=e229]
- generic [ref=e236]:
- paragraph [ref=e237]: Hello! I'm your AI assistant. How can I help you today?
- paragraph [ref=e238]: 04:26 PM
- generic [ref=e239]:
- generic [ref=e240]:
- textbox "Type your message..." [ref=e241]
- button [disabled]:
- img
- generic [ref=e243]:
- button "longcat icon LongCat" [ref=e245] [cursor=pointer]:
- img "longcat icon" [ref=e246]
- generic [ref=e247]: LongCat
- img [ref=e248]
- generic [ref=e250]:
- generic [ref=e251]: longcat
- link "AI settings" [ref=e252] [cursor=pointer]:
- /url: /app/settings#ai
- generic:
- generic:
- generic:
- generic:
- heading [level=3]
- generic: Unknown size
- button:
- img
- generic:
- generic: Unknown file type
- generic:
- button "Download":
- img
- text: Download
- button "Open":
- img
- text: Open
- generic:
- generic:
- generic:
- heading "Import Documents" [level=3]
- button:
- img
- generic:
- generic:
- img
- heading "Drop files here" [level=4]
- paragraph: or click to browse
- button "Browse Files"
- generic:
- button "Cancel"
- button "Upload 0 Files" [disabled]
-45
View File
@@ -1,45 +0,0 @@
# Build stage for YouTube search service
FROM golang:1.21-alpine AS builder
# Install git and other build dependencies
RUN apk add --no-cache git
# Set working directory
WORKDIR /app
# Copy go mod files
COPY search.go ./
# Build the search service
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o youtube-search search.go
# Final stage
FROM alpine:latest
# Install ca-certificates for HTTPS requests
RUN apk --no-cache add ca-certificates wget
# Create non-root user
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup
WORKDIR /app
# Copy the binary from builder stage
COPY --from=builder /app/youtube-search .
# Change ownership to non-root user
RUN chown appuser:appgroup youtube-search
# Switch to non-root user
USER appuser
# Expose port
EXPOSE 8090
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8090/youtube?q=test || exit 1
# Run the binary
CMD ["./youtube-search"]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 346 B

+448 -181
View File
@@ -4,260 +4,527 @@
<meta charset="UTF-8" />
<title>Trackeep Saver Options</title>
<style>
/* Complete Inter Font Faces - Exact Papra */
@font-face {
font-family: Inter;
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(https://fonts.bunny.net/inter/files/inter-latin-400-normal.woff2) format("woff2"),url(https://fonts.bunny.net/inter/files/inter-latin-400-normal.woff) format("woff");
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD
}
@font-face {
font-family: Inter;
font-style: normal;
font-weight: 500;
font-stretch: 100%;
font-display: swap;
src: url(https://fonts.bunny.net/inter/files/inter-latin-500-normal.woff2) format("woff2"),url(https://fonts.bunny.net/inter/files/inter-latin-500-normal.woff) format("woff");
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD
}
@font-face {
font-family: Inter;
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(https://fonts.bunny.net/inter/files/inter-latin-600-normal.woff2) format("woff2"),url(https://fonts.bunny.net/inter/files/inter-latin-600-normal.woff) format("woff");
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD
}
@font-face {
font-family: Inter;
font-style: normal;
font-weight: 700;
font-stretch: 100%;
font-display: swap;
src: url(https://fonts.bunny.net/inter/files/inter-latin-700-normal.woff2) format("woff2"),url(https://fonts.bunny.net/inter/files/inter-latin-700-normal.woff) format("woff");
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD
/* Modern Inter Font */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
/* Modern CSS Variables - Proton Pass Inspired */
:root {
--bg-primary: #0f0f0f;
--bg-secondary: #1a1a1a;
--bg-tertiary: #262626;
--bg-hover: #2a2a2a;
--bg-active: #333333;
--border-primary: #2a2a2a;
--border-secondary: #333333;
--text-primary: #ffffff;
--text-secondary: #a3a3a3;
--text-tertiary: #737373;
--accent-primary: #3b82f6;
--accent-hover: #2563eb;
--success: #10b981;
--warning: #f59e0b;
--error: #ef4444;
--gradient-primary: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
--gradient-secondary: linear-gradient(135deg, #1a1a1a 0%, #262626 100%);
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
--radius-sm: 6px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
}
/* Exact Papra CSS variables and dark theme (hex fallbacks for clarity) */
:root {
--background: 26 26 26;
--foreground: 250 250 250;
--card: 32 32 32;
--card-foreground: 250 250 250;
--popover: 32 32 32;
--popover-foreground: 250 250 250;
--primary: 217 70.2% 91.2%;
--primary-foreground: 250 250 250;
--secondary: 39 39 42;
--secondary-foreground: 250 250 250;
--muted: 39 39 42;
--muted-foreground: 163 163 163;
--accent: 39 39 42;
--accent-foreground: 250 250 250;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 250 250 250;
--border: 39 39 42;
--input: 39 39 42;
--ring: 217 70.2% 91.2%;
--radius: 0.5rem;
/* Hex fallbacks for readability */
--bg-hex: #1a1a1a;
--card-hex: #202020;
--input-hex: #27272a;
--border-hex: #27272a;
--muted-hex: #27272a;
--text-hex: #fafafa;
--muted-text-hex: #a3a3a3;
--primary-hex: #60a5fa;
* {
box-sizing: border-box;
}
body {
font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
margin: 0;
padding: 20px;
max-width: 640px;
background: var(--bg-hex);
color: var(--text-hex);
padding: 0;
min-height: 100vh;
background: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
font-size: 14px;
color-scheme: dark;
}
h1 {
font-size: 24px;
font-weight: 600;
margin: 0 0 8px 0;
/* Header */
.header {
background: var(--gradient-secondary);
padding: 32px 20px 20px;
border-bottom: 1px solid var(--border-primary);
position: relative;
overflow: hidden;
}
.header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: var(--gradient-primary);
}
.header-content {
max-width: 640px;
margin: 0 auto;
display: flex;
align-items: center;
gap: 10px;
gap: 16px;
}
.logo-container {
display: flex;
align-items: center;
gap: 16px;
}
.logo {
width: 32px;
height: 32px;
border-radius: calc(var(--radius) * 0.5);
background: var(--primary-hex);
width: 48px;
height: 48px;
border-radius: var(--radius-lg);
background: var(--gradient-primary);
display: flex;
align-items: center;
justify-content: center;
color: var(--text-hex);
font-weight: bold;
font-size: 16px;
color: white;
font-weight: 700;
font-size: 20px;
box-shadow: var(--shadow-lg);
position: relative;
overflow: hidden;
}
p {
.logo::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg, transparent, rgba(255,255,255,0.1), transparent);
transform: rotate(45deg);
animation: shimmer 3s infinite;
}
@keyframes shimmer {
0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
100% { transform: translateX(100%) translateY(100%) rotate(45deg); }
}
.title-section {
flex: 1;
}
.title {
font-size: 28px;
font-weight: 700;
color: var(--text-primary);
margin: 0 0 4px 0;
}
.subtitle {
font-size: 14px;
color: var(--muted-text-hex);
margin: 0 0 24px 0;
color: var(--text-secondary);
margin: 0;
}
/* Main Content */
.container {
max-width: 640px;
margin: 0 auto;
padding: 32px 20px;
}
/* Sections */
.section {
background: var(--card-hex);
border-radius: var(--radius);
padding: 20px;
border: 1px solid var(--border-hex);
background: var(--bg-secondary);
border-radius: var(--radius-lg);
padding: 24px;
border: 1px solid var(--border-primary);
margin-bottom: 24px;
transition: all 0.2s ease;
}
.section:hover {
border-color: var(--border-secondary);
box-shadow: var(--shadow-md);
}
.section-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
.section-title {
.section-icon {
width: 32px;
height: 32px;
border-radius: var(--radius-md);
background: var(--gradient-primary);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 16px;
}
.section-title {
font-size: 18px;
font-weight: 600;
margin: 0 0 16px 0;
color: var(--text-hex);
color: var(--text-primary);
margin: 0;
}
/* Form Elements */
.form-group {
margin-bottom: 20px;
}
.form-group:last-child {
margin-bottom: 0;
}
label {
display: block;
font-size: 14px;
font-size: 13px;
font-weight: 500;
margin: 0 0 6px 0;
color: var(--muted-text-hex);
margin-bottom: 8px;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
input[type="text"],
input[type="url"],
input[type="password"] {
width: 100%;
box-sizing: border-box;
padding: 10px 14px;
border-radius: var(--radius);
border: 1px solid var(--border-hex);
background: var(--input-hex);
color: var(--text-hex);
padding: 14px 16px;
border-radius: var(--radius-md);
border: 1px solid var(--border-primary);
background: var(--bg-tertiary);
color: var(--text-primary);
font-size: 14px;
font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-family: 'Inter', sans-serif;
font-weight: 400;
transition: border-color 0.15s, background 0.15s;
}
input:focus {
transition: all 0.2s ease;
outline: none;
border-color: var(--primary-hex);
background: var(--card-hex);
}
button {
cursor: pointer;
border-radius: var(--radius);
input[type="text"]:focus,
input[type="url"]:focus,
input[type="password"]:focus {
border-color: var(--accent-primary);
background: var(--bg-hover);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
/* Instructions */
.instructions {
background: var(--bg-tertiary);
border-radius: var(--radius-md);
padding: 16px;
border: 1px solid var(--border-primary);
margin-top: 16px;
}
.instructions-title {
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
margin: 0 0 8px 0;
display: flex;
align-items: center;
gap: 6px;
}
.instructions-list {
font-size: 13px;
color: var(--text-secondary);
margin: 0;
padding-left: 16px;
line-height: 1.6;
}
.instructions-list li {
margin-bottom: 4px;
}
.instructions-list li:last-child {
margin-bottom: 0;
}
/* Buttons */
.btn {
padding: 14px 24px;
border-radius: var(--radius-md);
border: none;
padding: 10px 18px;
font-size: 14px;
font-weight: 500;
font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: var(--primary-hex);
color: var(--text-hex);
transition: all 0.2s;
font-family: 'Inter', sans-serif;
cursor: pointer;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
outline: none;
position: relative;
overflow: hidden;
}
button:hover {
opacity: 0.9;
transform: translateY(-1px);
.btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
transition: left 0.5s;
}
button:disabled {
.btn:hover::before {
left: 100%;
}
.btn-primary {
background: var(--gradient-primary);
color: white;
box-shadow: var(--shadow-sm);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.btn-primary:active {
transform: translateY(0);
}
.btn:disabled {
opacity: 0.5;
cursor: default;
transform: none;
cursor: not-allowed;
transform: none !important;
}
.status {
margin-top: 12px;
/* Status Messages */
.status-message {
padding: 16px 20px;
border-radius: var(--radius-md);
font-size: 13px;
padding: 8px 12px;
border-radius: calc(var(--radius) * 0.5);
background: var(--muted-hex);
border: 1px solid var(--border-hex);
font-weight: 500;
margin-top: 20px;
display: flex;
align-items: center;
gap: 10px;
animation: slideUp 0.3s ease;
}
.status.success {
color: var(--primary-hex);
border-color: var(--primary-hex);
background: color-mix(in srgb, var(--primary-hex) 10%, transparent);
@keyframes slideUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.status.error {
color: #ef4444;
border-color: #ef4444;
background: color-mix(in srgb, #ef4444 10%, transparent);
.status-message.success {
background: rgba(16, 185, 129, 0.1);
color: var(--success);
border: 1px solid rgba(16, 185, 129, 0.2);
}
.status-message.error {
background: rgba(239, 68, 68, 0.1);
color: var(--error);
border: 1px solid rgba(239, 68, 68, 0.2);
}
.status-message.info {
background: rgba(59, 130, 246, 0.1);
color: var(--accent-primary);
border: 1px solid rgba(59, 130, 246, 0.2);
}
/* Code styling */
code {
background: var(--input-hex);
padding: 2px 6px;
border-radius: calc(var(--radius) * 0.5);
font-size: 13px;
color: var(--text-hex);
border: 1px solid var(--border-hex);
background: var(--bg-tertiary);
padding: 3px 8px;
border-radius: var(--radius-sm);
font-size: 12px;
color: var(--text-primary);
border: 1px solid var(--border-primary);
font-family: 'Fira Code', 'Monaco', 'Consolas', monospace;
}
.instructions {
font-size: 13px;
color: var(--muted-text-hex);
margin-top: 6px;
line-height: 1.5;
/* Icon System */
.icon {
width: 16px;
height: 16px;
display: inline-block;
vertical-align: middle;
transition: all 0.2s ease;
}
.instructions strong {
color: var(--text-hex);
.icon-sm {
width: 12px;
height: 12px;
}
.icon-lg {
width: 20px;
height: 20px;
}
.icon-xl {
width: 24px;
height: 24px;
}
/* Icon animations */
.icon-spin {
animation: spin 1s linear infinite;
}
.icon-pulse {
animation: pulse 2s ease-in-out infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.8; transform: scale(1.05); }
}
/* Enhanced button icons */
.btn .icon {
transition: transform 0.2s ease;
}
.btn:hover .icon {
transform: scale(1.1);
}
.btn:active .icon {
transform: scale(0.95);
}
/* Section icon enhancements */
.section-icon {
transition: all 0.3s ease;
}
.section:hover .section-icon {
transform: scale(1.05) rotate(5deg);
box-shadow: var(--shadow-md);
}
/* Responsive */
@media (max-width: 640px) {
.container {
padding: 20px 16px;
}
.section {
padding: 20px;
}
.header {
padding: 24px 16px 16px;
}
.title {
font-size: 24px;
}
}
</style>
</head>
<body>
<h1>
<div class="logo">T</div>
Trackeep Saver Options
</h1>
<p>Configure how the extension connects to your Trackeep backend.</p>
<div class="section">
<div class="section-title">API Configuration</div>
<label for="apiBaseUrl">Trackeep API base URL (must include <code>/api/v1</code>)</label>
<input
id="apiBaseUrl"
type="url"
placeholder="https://your-domain.example.com/api/v1 or http://localhost:8080/api/v1"
/>
<label for="authToken">Auth token (JWT)</label>
<input
id="authToken"
type="password"
placeholder="Paste your Trackeep token (trackeep_token) here"
/>
<div class="instructions">
<strong>How to get your token:</strong><br>
1. Log into Trackeep in your browser.<br>
2. Open DevTools → Application → Local Storage.<br>
3. Find the key <code>trackeep_token</code> and copy its value.<br>
4. Paste it above. Never share this token publicly.
<!-- Header -->
<header class="header">
<div class="header-content">
<div class="logo-container">
<div class="logo">T</div>
<div class="title-section">
<h1 class="title">Trackeep Saver</h1>
<p class="subtitle">Configure your extension settings</p>
</div>
</div>
</div>
</header>
<button id="saveBtn" style="margin-top:20px;">💾 Save settings</button>
<div id="status" class="status"></div>
</div>
<!-- Main Content -->
<main class="container">
<div class="section">
<div class="section-header">
<div class="section-icon">
<svg class="icon-xl" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"/>
<path d="M12 1v6m0 6v6m4.22-13.22l4.24 4.24M1.54 1.54l4.24 4.24M1 12h6m6 0h6"/>
</svg>
</div>
<h2 class="section-title">API Configuration</h2>
</div>
<div class="form-group">
<label for="apiBaseUrl">Trackeep API Base URL</label>
<input
id="apiBaseUrl"
type="url"
placeholder="https://your-domain.example.com/api/v1 or http://localhost:8080/api/v1"
/>
</div>
<div class="form-group">
<label for="authToken">Authentication Token (JWT)</label>
<input
id="authToken"
type="password"
placeholder="Paste your Trackeep authentication token here"
/>
</div>
<div class="instructions">
<div class="instructions-title">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
<polyline points="14,2 14,8 20,8"/>
<line x1="16" y1="13" x2="8" y2="13"/>
<line x1="16" y1="17" x2="8" y2="17"/>
<polyline points="10,9 9,9 8,9"/>
</svg>
<span>How to get your authentication token:</span>
</div>
<ol class="instructions-list">
<li>Log into your Trackeep account in your browser</li>
<li>Open Developer Tools (F12) → Application → Local Storage</li>
<li>Find key <code>trackeep_token</code> and copy its value</li>
<li>Paste token in field above</li>
<li><strong>Never share this token publicly</strong> - it provides full access to your account</li>
</ol>
</div>
<button class="btn btn-primary" id="saveBtn" style="margin-top: 24px;">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
<polyline points="17,21 17,13 7,13 7,21"/>
<polyline points="7,3 7,8 15,8"/>
</svg>
<span>Save Settings</span>
</button>
<div id="statusMessage" class="status-message" style="display: none;"></div>
</div>
</main>
<script src="options.js"></script>
</body>
+47 -15
View File
@@ -3,13 +3,41 @@
const apiBaseUrlInput = document.getElementById('apiBaseUrl');
const authTokenInput = document.getElementById('authToken');
const saveBtn = document.getElementById('saveBtn');
const statusEl = document.getElementById('status');
const statusMessageEl = document.getElementById('statusMessage');
function setStatus(message, type) {
statusEl.textContent = message || '';
statusEl.classList.remove('success', 'error');
if (type) {
statusEl.classList.add(type);
function showMessage(message, type = 'info', duration = 5000) {
statusMessageEl.textContent = message;
statusMessageEl.className = `status-message ${type}`;
statusMessageEl.style.display = 'flex';
if (duration > 0) {
setTimeout(() => {
statusMessageEl.style.display = 'none';
}, duration);
}
}
function hideMessage() {
statusMessageEl.style.display = 'none';
}
function setButtonLoading(button, loading = true) {
if (loading) {
button.disabled = true;
const originalContent = button.innerHTML;
button.dataset.originalContent = originalContent;
button.innerHTML = `
<svg class="icon icon-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
</svg>
<span>Saving...</span>
`;
} else {
button.disabled = false;
if (button.dataset.originalContent) {
button.innerHTML = button.dataset.originalContent;
delete button.dataset.originalContent;
}
}
}
@@ -63,17 +91,17 @@ function saveSettings() {
const authToken = authTokenInput.value.trim();
if (!apiBaseUrl) {
setStatus('API base URL is required.', 'error');
showMessage('API base URL is required.', 'error');
return;
}
if (!authToken) {
setStatus('Auth token is required.', 'error');
showMessage('Authentication token is required.', 'error');
return;
}
saveBtn.disabled = true;
setStatus('Saving…', null);
setButtonLoading(saveBtn, true);
hideMessage();
chrome.storage.sync.set(
{
@@ -81,18 +109,22 @@ function saveSettings() {
trackeepAuthToken: authToken
},
() => {
saveBtn.disabled = false;
setButtonLoading(saveBtn, false);
if (chrome.runtime.lastError) {
setStatus(`Failed to save: ${chrome.runtime.lastError.message}`, 'error');
showMessage(`Failed to save: ${chrome.runtime.lastError.message}`, 'error');
} else {
setStatus('Settings saved. You can now use the popup to save bookmarks and files.', 'success');
showMessage(`
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20,6 9,17 4,12"/>
</svg>
Settings saved successfully! You can now use the extension to save bookmarks and files.
`, 'success');
}
}
);
}
// Init
// Initialize everything when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
detectAndPrefillApiBaseUrl(() => {
loadSettings();
File diff suppressed because it is too large Load Diff
+137 -39
View File
@@ -1,9 +1,16 @@
/* global chrome */
const statusEl = document.getElementById('status');
const configHintEl = document.getElementById('configHint');
// DOM Elements
const statusIndicatorEl = document.getElementById('statusIndicator');
const statusTextEl = document.getElementById('statusText');
const statusMessageEl = document.getElementById('statusMessage');
const openOptionsBtn = document.getElementById('openOptions');
// Tab elements
const tabBtns = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab-content');
// Bookmark elements
const bookmarkTitleInput = document.getElementById('bookmarkTitle');
const bookmarkUrlInput = document.getElementById('bookmarkUrl');
const bookmarkDescriptionInput = document.getElementById('bookmarkDescription');
@@ -11,6 +18,7 @@ const bookmarkTagsInput = document.getElementById('bookmarkTags');
const bookmarkPublicInput = document.getElementById('bookmarkPublic');
const saveBookmarkBtn = document.getElementById('saveBookmarkBtn');
// File elements
const fileInput = document.getElementById('fileInput');
const fileDescriptionInput = document.getElementById('fileDescription');
const uploadFileBtn = document.getElementById('uploadFileBtn');
@@ -20,19 +28,86 @@ let trackeepConfig = {
authToken: ''
};
function setStatus(message, type) {
statusEl.textContent = message || '';
statusEl.classList.remove('error', 'success');
if (type) {
statusEl.classList.add(type);
// Tab switching functionality
function initTabs() {
tabBtns.forEach(btn => {
btn.addEventListener('click', () => {
const targetTab = btn.dataset.tab;
// Update button states
tabBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
// Update content visibility
tabContents.forEach(content => {
content.classList.remove('active');
if (content.id === `${targetTab}-tab`) {
content.classList.add('active');
}
});
});
});
}
// Status management
function updateStatus(text, type = 'info') {
statusTextEl.textContent = text;
statusIndicatorEl.className = 'status-indicator';
if (type === 'success') {
statusIndicatorEl.classList.add('connected');
} else if (type === 'error') {
statusIndicatorEl.classList.add('error');
}
}
function showMessage(message, type = 'info', duration = 5000) {
statusMessageEl.textContent = message;
statusMessageEl.className = `status-message ${type}`;
statusMessageEl.style.display = 'flex';
// Auto-hide after duration
if (duration > 0) {
setTimeout(() => {
statusMessageEl.style.display = 'none';
}, duration);
}
}
function hideMessage() {
statusMessageEl.style.display = 'none';
}
// Loading states
function setButtonLoading(button, loading = true) {
if (loading) {
button.disabled = true;
const originalContent = button.innerHTML;
button.dataset.originalContent = originalContent;
button.innerHTML = `
<svg class="icon icon-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
</svg>
<span>Processing...</span>
`;
} else {
button.disabled = false;
if (button.dataset.originalContent) {
button.innerHTML = button.dataset.originalContent;
delete button.dataset.originalContent;
}
}
}
function disableForms(disabled) {
[bookmarkTitleInput, bookmarkUrlInput, bookmarkDescriptionInput, bookmarkTagsInput, bookmarkPublicInput, saveBookmarkBtn,
fileInput, fileDescriptionInput, uploadFileBtn].forEach((el) => {
if (!el) return;
el.disabled = disabled;
const elements = [
bookmarkTitleInput, bookmarkUrlInput, bookmarkDescriptionInput,
bookmarkTagsInput, bookmarkPublicInput, saveBookmarkBtn,
fileInput, fileDescriptionInput, uploadFileBtn
];
elements.forEach(el => {
if (el) el.disabled = disabled;
});
}
@@ -44,10 +119,12 @@ function loadConfig(callback) {
trackeepConfig = { apiBaseUrl, authToken };
if (!apiBaseUrl || !authToken) {
configHintEl.textContent = 'Configure API URL and token in Options to enable saving.';
updateStatus('Configuration required', 'error');
showMessage('Configure API URL and token in Options to enable saving.', 'error');
disableForms(true);
} else {
configHintEl.textContent = `Using API: ${apiBaseUrl}`;
updateStatus(`Connected to ${apiBaseUrl}`, 'success');
hideMessage();
disableForms(false);
}
@@ -67,11 +144,9 @@ function detectTrackeepDomain(callback) {
try {
const url = new URL(tab.url);
// Common Trackeep domains: localhost, trackeep.*, etc.
const isTrackeepDomain = url.hostname.includes('trackeep') || url.hostname === 'localhost';
if (isTrackeepDomain && url.protocol === 'https:') {
const candidate = `${url.origin}/api/v1`;
// Only pre-fill if not already set
chrome.storage.sync.get(['trackeepApiBaseUrl'], (items) => {
if (!items.trackeepApiBaseUrl) {
chrome.storage.sync.set({ trackeepApiBaseUrl: candidate }, () => {
@@ -96,11 +171,9 @@ function initActiveTab() {
const tab = tabs && tabs[0];
if (!tab) return;
// Check for context menu data first
chrome.storage.local.get(['contextMenuData'], (items) => {
const ctx = items.contextMenuData;
if (ctx && ctx.timestamp && Date.now() - ctx.timestamp < 5000) {
// Use context menu data if recent
if (ctx.url && !bookmarkUrlInput.value) {
bookmarkUrlInput.value = ctx.url;
}
@@ -110,10 +183,8 @@ function initActiveTab() {
if (ctx.selection && !bookmarkDescriptionInput.value) {
bookmarkDescriptionInput.value = ctx.selection;
}
// Clear after using
chrome.storage.local.remove(['contextMenuData']);
} else {
// Fallback to active tab
if (tab.title && !bookmarkTitleInput.value) {
bookmarkTitleInput.value = tab.title;
}
@@ -127,17 +198,17 @@ function initActiveTab() {
async function saveBookmark(event) {
event.preventDefault();
setStatus('', null);
hideMessage();
const { apiBaseUrl, authToken } = trackeepConfig;
if (!apiBaseUrl || !authToken) {
setStatus('Missing API URL or auth token. Open options first.', 'error');
showMessage('Missing API URL or auth token. Open options first.', 'error');
return;
}
const url = bookmarkUrlInput.value.trim();
if (!url) {
setStatus('URL is required.', 'error');
showMessage('URL is required.', 'error');
return;
}
@@ -158,8 +229,8 @@ async function saveBookmark(event) {
is_public: isPublic
};
saveBookmarkBtn.disabled = true;
setStatus('Saving bookmark', null);
setButtonLoading(saveBookmarkBtn, true);
showMessage('Saving bookmark...', 'info', 0);
try {
const base = apiBaseUrl.replace(/\/$/, '');
@@ -185,28 +256,41 @@ async function saveBookmark(event) {
throw new Error(errorMessage);
}
setStatus('Bookmark saved to Trackeep.', 'success');
showMessage(`
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20,6 9,17 4,12"/>
</svg>
Bookmark saved successfully!
`, 'success');
// Clear form after successful save
setTimeout(() => {
bookmarkDescriptionInput.value = '';
bookmarkTagsInput.value = '';
bookmarkPublicInput.checked = false;
}, 2000);
} catch (err) {
console.error('Error saving bookmark', err);
setStatus(err && err.message ? err.message : 'Failed to save bookmark.', 'error');
showMessage(err && err.message ? err.message : 'Failed to save bookmark.', 'error');
} finally {
saveBookmarkBtn.disabled = false;
setButtonLoading(saveBookmarkBtn, false);
}
}
async function uploadFile(event) {
event.preventDefault();
setStatus('', null);
hideMessage();
const { apiBaseUrl, authToken } = trackeepConfig;
if (!apiBaseUrl || !authToken) {
setStatus('Missing API URL or auth token. Open options first.', 'error');
showMessage('Missing API URL or auth token. Open options first.', 'error');
return;
}
const file = fileInput.files && fileInput.files[0];
if (!file) {
setStatus('Please choose a file to upload.', 'error');
showMessage('Please choose a file to upload.', 'error');
return;
}
@@ -218,8 +302,8 @@ async function uploadFile(event) {
formData.append('description', description);
}
uploadFileBtn.disabled = true;
setStatus('Uploading file', null);
setButtonLoading(uploadFileBtn, true);
showMessage('Uploading file...', 'info', 0);
try {
const base = apiBaseUrl.replace(/\/$/, '');
@@ -244,14 +328,24 @@ async function uploadFile(event) {
throw new Error(errorMessage);
}
setStatus('File uploaded to Trackeep.', 'success');
fileInput.value = '';
fileDescriptionInput.value = '';
showMessage(`
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20,6 9,17 4,12"/>
</svg>
File uploaded successfully!
`, 'success');
// Clear form after successful upload
setTimeout(() => {
fileInput.value = '';
fileDescriptionInput.value = '';
}, 2000);
} catch (err) {
console.error('Error uploading file', err);
setStatus(err && err.message ? err.message : 'Failed to upload file.', 'error');
showMessage(err && err.message ? err.message : 'Failed to upload file.', 'error');
} finally {
uploadFileBtn.disabled = false;
setButtonLoading(uploadFileBtn, false);
}
}
@@ -263,9 +357,12 @@ function openOptions() {
}
}
// Init
// Initialize everything when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
// Initialize tabs
initTabs();
// Event listeners
openOptionsBtn.addEventListener('click', openOptions);
saveBookmarkBtn.addEventListener('click', (e) => {
e.preventDefault();
@@ -276,6 +373,7 @@ document.addEventListener('DOMContentLoaded', () => {
uploadFile(e);
});
// Initialize configuration and active tab
detectTrackeepDomain(() => {
loadConfig(() => {
initActiveTab();
+66
View File
@@ -8,6 +8,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/trackeep/backend/config"
"github.com/trackeep/backend/models"
"golang.org/x/crypto/bcrypt"
)
// AdminMiddleware checks if user is admin
@@ -212,6 +213,71 @@ func AdminGetUsers(c *gin.Context) {
})
}
// AdminCreateUser handles POST /api/v1/admin/users
func AdminCreateUser(c *gin.Context) {
db := config.GetDB()
var req struct {
Email string `json:"email" binding:"required,email"`
Username string `json:"username" binding:"required,min=3,max=50"`
Password string `json:"password" binding:"required,min=6"`
FullName string `json:"fullName" binding:"required,min=1,max=100"`
Role string `json:"role"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
role := req.Role
if role == "" {
role = "user"
}
if role != "user" && role != "admin" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid role. Must be 'user' or 'admin'"})
return
}
var existing models.User
if err := db.Where("email = ?", req.Email).First(&existing).Error; err == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "User with this email already exists"})
return
}
if err := db.Where("username = ?", req.Username).First(&existing).Error; err == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Username already taken"})
return
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"})
return
}
user := models.User{
Email: req.Email,
Username: req.Username,
Password: string(hashedPassword),
FullName: req.FullName,
Role: role,
Theme: "dark",
}
if err := db.Create(&user).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user"})
return
}
_ = ensureMessagingDefaults(db, user.ID)
user.Password = ""
c.JSON(http.StatusCreated, gin.H{
"message": "User created successfully",
"user": user,
})
}
// AdminUpdateUserRole handles PUT /api/v1/admin/users/:id/role
func AdminUpdateUserRole(c *gin.Context) {
db := config.GetDB()
+1 -1
View File
@@ -588,7 +588,7 @@ Provide a JSON array of task objects with:
- context_data: Additional context
- deadline: Suggested deadline (ISO date or null)
- estimated_time: Estimated time in minutes
- confidence: Confidence score 0-1`, contextData, limit)
- confidence: Confidence score 0-1`, limit, contextData)
messages := []services.Message{
{Role: "system", Content: "You are a productivity assistant. Always respond with valid JSON array."},
+51
View File
@@ -95,6 +95,33 @@ func ValidateJWT(tokenString string) (*Claims, error) {
return nil, errors.New("invalid token")
}
func getAuthenticatedUserFromHeader(c *gin.Context, db *gorm.DB) (*models.User, error) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
return nil, errors.New("authorization header required")
}
tokenString := authHeader
if strings.HasPrefix(authHeader, "Bearer ") {
tokenString = strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer "))
}
if tokenString == "" {
return nil, errors.New("invalid authorization header")
}
claims, err := ValidateJWT(tokenString)
if err != nil {
return nil, err
}
var user models.User
if err := db.First(&user, claims.UserID).Error; err != nil {
return nil, err
}
return &user, nil
}
// AuthMiddleware validates JWT tokens
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
@@ -202,6 +229,24 @@ func Register(c *gin.Context) {
db := config.GetDB()
// Registration rules:
// - First user can self-register and becomes admin.
// - After that, only authenticated admins can create users.
var userCount int64
if err := db.Model(&models.User{}).Count(&userCount).Error; err != nil {
c.JSON(500, gin.H{"error": "Failed to check existing users"})
return
}
isFirstUser := userCount == 0
if !isFirstUser {
requester, err := getAuthenticatedUserFromHeader(c, db)
if err != nil || requester.Role != "admin" {
c.JSON(403, gin.H{"error": "Registration is disabled. Only an administrator can create users."})
return
}
}
// Check if user already exists
var existingUser models.User
if err := db.Where("email = ?", req.Email).First(&existingUser).Error; err == nil {
@@ -222,11 +267,17 @@ func Register(c *gin.Context) {
}
// Create user
role := "user"
if isFirstUser {
role = "admin"
}
user := models.User{
Email: req.Email,
Username: req.Username,
Password: string(hashedPassword),
FullName: req.FullName,
Role: role,
Theme: "dark",
}
+34 -3
View File
@@ -6,6 +6,7 @@ import (
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
@@ -18,10 +19,40 @@ import (
func GetFiles(c *gin.Context) {
var files []models.File
// TODO: Get user ID from authentication context
userID := uint(1) // Placeholder
userID := c.GetUint("user_id")
if userID == 0 {
userID = c.GetUint("userID")
}
if userID == 0 {
c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
return
}
if err := models.DB.Where("user_id = ?", userID).Find(&files).Error; err != nil {
query := models.DB.Where("user_id = ?", userID)
if rawQuery := strings.TrimSpace(c.Query("q")); rawQuery != "" {
needle := "%" + strings.ToLower(rawQuery) + "%"
query = query.Where("LOWER(original_name) LIKE ? OR LOWER(description) LIKE ?", needle, needle)
}
limitApplied := false
if limitRaw := strings.TrimSpace(c.Query("limit")); limitRaw != "" {
limit, err := strconv.Atoi(limitRaw)
if err != nil || limit <= 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid limit"})
return
}
if limit > 100 {
limit = 100
}
query = query.Limit(limit)
limitApplied = true
}
if !limitApplied && strings.TrimSpace(c.Query("q")) != "" {
query = query.Limit(20)
}
if err := query.Order("created_at DESC").Find(&files).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve files"})
return
}
+151 -28
View File
@@ -640,41 +640,23 @@ func CreateConversationMessage(c *gin.Context) {
return
}
if strings.TrimSpace(req.Body) == "" && len(req.Attachments) == 0 {
trimmedBody := strings.TrimSpace(req.Body)
if trimmedBody == "" && len(req.Attachments) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Message body or attachments are required"})
return
}
metadataJSON := "{}"
if req.Metadata != nil {
if raw, err := json.Marshal(req.Metadata); err == nil {
metadataJSON = string(raw)
}
}
message := models.Message{
ConversationID: conversationID,
SenderID: userID,
Body: strings.TrimSpace(req.Body),
MetadataJSON: metadataJSON,
}
if err := models.DB.Create(&message).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create message"})
return
}
attachmentRows := make([]models.MessageAttachment, 0, len(req.Attachments))
for _, a := range req.Attachments {
attachmentRows = append(attachmentRows, models.MessageAttachment{
MessageID: message.ID,
Kind: normalizeAttachmentKind(a.Kind),
FileID: a.FileID,
URL: a.URL,
Title: a.Title,
Kind: normalizeAttachmentKind(a.Kind),
FileID: a.FileID,
URL: a.URL,
Title: a.Title,
})
}
suggestions, inferredAttachments, isSensitive := services.DetectMessageContent(message.Body)
suggestions, inferredAttachments, isSensitive := services.DetectMessageContent(trimmedBody)
for _, inferred := range inferredAttachments {
if hasAttachment(attachmentRows, inferred.Kind, inferred.URL) {
continue
@@ -684,13 +666,55 @@ func CreateConversationMessage(c *gin.Context) {
previewJSON = string(raw)
}
attachmentRows = append(attachmentRows, models.MessageAttachment{
MessageID: message.ID,
Kind: normalizeAttachmentKind(inferred.Kind),
URL: inferred.URL,
Title: inferred.Title,
PreviewJSON: previewJSON,
})
}
metadataMap := map[string]interface{}{}
for k, v := range req.Metadata {
metadataMap[k] = v
}
storedBody := trimmedBody
if isSensitive && (conv.Type == models.ConversationTypeDM || conv.Type == models.ConversationTypeSelf) && trimmedBody != "" {
ciphertext, err := utils.Encrypt(trimmedBody)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to encrypt sensitive message"})
return
}
storedBody = maskSensitiveBody(trimmedBody)
metadataMap["sensitive_payload"] = map[string]interface{}{
"version": "v1",
"ciphertext": ciphertext,
"masked_body": storedBody,
"scope": string(conv.Type),
}
}
metadataJSON := "{}"
if len(metadataMap) > 0 {
if raw, err := json.Marshal(metadataMap); err == nil {
metadataJSON = string(raw)
}
}
message := models.Message{
ConversationID: conversationID,
SenderID: userID,
Body: storedBody,
MetadataJSON: metadataJSON,
}
if err := models.DB.Create(&message).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create message"})
return
}
for i := range attachmentRows {
attachmentRows[i].MessageID = message.ID
}
if len(attachmentRows) > 0 {
models.DB.Create(&attachmentRows)
}
@@ -1187,6 +1211,37 @@ func DismissMessageSuggestion(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"suggestion": suggestion})
}
// RevealSensitiveMessage decrypts and returns sensitive message plaintext for authorized members.
func RevealSensitiveMessage(c *gin.Context) {
userID := getAuthUserID(c)
messageID, err := parseUintParam(c, "id")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid message id"})
return
}
var msg models.Message
if err := models.DB.First(&msg, messageID).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Message not found"})
return
}
if _, _, err := getConversationWithMembership(models.DB, msg.ConversationID, userID); err != nil {
c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
return
}
plaintext, ok := extractSensitivePlaintext(msg.MetadataJSON)
if !ok {
c.JSON(http.StatusNotFound, gin.H{"error": "Sensitive payload not found"})
return
}
c.JSON(http.StatusOK, gin.H{
"message_id": msg.ID,
"plaintext": plaintext,
})
}
// GetPasswordVaultItems returns owned and explicitly shared vault items.
func GetPasswordVaultItems(c *gin.Context) {
userID := getAuthUserID(c)
@@ -1760,11 +1815,15 @@ func applySuggestionAction(db *gorm.DB, userID uint, message *models.Message, su
return gin.H{"deep_link": ref.DeepLink}, nil
case "move_to_password_vault":
secretSource := message.Body
if sensitivePlaintext, ok := extractSensitivePlaintext(message.MetadataJSON); ok {
secretSource = sensitivePlaintext
}
label := "Imported from chat"
if compact := compactMessageTitle(message.Body, 50); compact != "" {
if compact := compactMessageTitle(secretSource, 50); compact != "" {
label = compact
}
encryptedSecret, err := utils.Encrypt(message.Body)
encryptedSecret, err := utils.Encrypt(secretSource)
if err != nil {
return nil, err
}
@@ -2026,6 +2085,70 @@ func hasAttachment(rows []models.MessageAttachment, kind, url string) bool {
return false
}
func maskSensitiveBody(text string) string {
trimmed := strings.TrimSpace(text)
if trimmed == "" {
return "[sensitive content hidden]"
}
parts := strings.Fields(trimmed)
if len(parts) == 0 {
return "[sensitive content hidden]"
}
maskedParts := make([]string, 0, len(parts))
for _, part := range parts {
runes := []rune(part)
if len(runes) <= 2 {
maskedParts = append(maskedParts, "**")
continue
}
maskedParts = append(maskedParts, strings.Repeat("*", len(runes)))
}
return strings.Join(maskedParts, " ")
}
func extractSensitivePlaintext(metadataJSON string) (string, bool) {
payload := extractSensitivePayload(metadataJSON)
if payload == nil {
return "", false
}
ciphertext := asString(payload["ciphertext"])
if ciphertext == "" {
return "", false
}
plaintext, err := utils.Decrypt(ciphertext)
if err != nil {
return "", false
}
return plaintext, true
}
func extractSensitivePayload(metadataJSON string) map[string]interface{} {
trimmed := strings.TrimSpace(metadataJSON)
if trimmed == "" || trimmed == "{}" {
return nil
}
metadata := map[string]interface{}{}
if err := json.Unmarshal([]byte(trimmed), &metadata); err != nil {
return nil
}
rawPayload, ok := metadata["sensitive_payload"]
if !ok || rawPayload == nil {
return nil
}
payload, ok := rawPayload.(map[string]interface{})
if !ok {
return nil
}
return payload
}
func normalizeAttachmentKind(kind string) string {
k := strings.ToLower(strings.TrimSpace(kind))
switch k {
+1
View File
@@ -369,6 +369,7 @@ func main() {
messages.GET("/messages/:id/suggestions", handlers.GetMessageSuggestions)
messages.POST("/messages/:id/suggestions/:suggestionId/accept", handlers.AcceptMessageSuggestion)
messages.POST("/messages/:id/suggestions/:suggestionId/dismiss", handlers.DismissMessageSuggestion)
messages.POST("/messages/:id/reveal-sensitive", handlers.RevealSensitiveMessage)
messages.GET("/ws", handlers.MessagesWebSocket)
messages.GET("/password-vault/items", handlers.GetPasswordVaultItems)
+8 -15
View File
@@ -196,6 +196,10 @@ func (ff *FaviconFetcher) makeAbsoluteURL(href string, baseURL *url.URL) string
if idx := strings.Index(href, "#"); idx != -1 {
href = href[:idx]
}
href = strings.TrimSpace(href)
if href == "" {
return ""
}
// Handle different URL types
if strings.HasPrefix(href, "http://") || strings.HasPrefix(href, "https://") {
@@ -206,22 +210,11 @@ func (ff *FaviconFetcher) makeAbsoluteURL(href string, baseURL *url.URL) string
return baseURL.Scheme + ":" + href
}
if strings.HasPrefix(href, "/") {
return baseURL.Scheme + "://" + baseURL.Host + href
ref, err := url.Parse(href)
if err != nil {
return href
}
// Relative path - construct proper URL
if baseURL.Path == "" || baseURL.Path == "/" {
return baseURL.Scheme + "://" + baseURL.Host + "/" + href
}
// Remove filename from base path
basePath := baseURL.Path
if lastSlash := strings.LastIndex(basePath, "/"); lastSlash != -1 {
basePath = basePath[:lastSlash+1]
}
return baseURL.Scheme + "://" + baseURL.Host + basePath + href
return baseURL.ResolveReference(ref).String()
}
// tryCommonLocations tries common favicon file paths
+56
View File
@@ -0,0 +1,56 @@
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: ${DB_NAME:-trackeep}
POSTGRES_USER: ${DB_USER:-trackeep}
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-trackeep} -d ${DB_NAME:-trackeep}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
trackeep-backend:
image: ghcr.io/Dvorinka/trackeep/backend:latest
ports:
- "${PORT:-8080}:8080"
env_file:
- .env
volumes:
- ./data:/data
- ./uploads:/app/uploads
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health || wget --no-verbose --tries=1 --spider http://localhost:8080/live"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
trackeep-frontend:
image: ghcr.io/Dvorinka/trackeep/frontend:latest
ports:
- "5173:80"
depends_on:
trackeep-backend:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pgrep nginx > /dev/null || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
volumes:
postgres_data:
+4 -4
View File
@@ -2,16 +2,16 @@ services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB:-trackeep}
POSTGRES_USER: ${POSTGRES_USER:-trackeep}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
POSTGRES_DB: ${DB_NAME:-trackeep}
POSTGRES_USER: ${DB_USER:-trackeep}
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-trackeep} -d ${POSTGRES_DB:-trackeep}"]
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-trackeep} -d ${DB_NAME:-trackeep}"]
interval: 10s
timeout: 5s
retries: 5
-4
View File
@@ -1,4 +0,0 @@
// Enable demo mode - run this in browser console
localStorage.setItem('demoMode', 'true');
document.title = 'Trackeep - Demo Mode';
console.log('Demo mode enabled! Refresh the page to see changes.');
@@ -27,11 +27,13 @@ export const AuthenticationWarning = () => {
<div class="text-center mb-8">
<div class="mb-6">
<div class="inline-flex items-center justify-center mb-4">
<img
src="/trackeepfavi_bg.png"
alt="Trackeep Logo"
class="w-12 h-12 rounded-xl"
/>
<div class="inline-flex items-center justify-center p-2.5 rounded-xl border border-border bg-muted/40">
<img
src="/trackeep.svg"
alt="Trackeep Logo"
class="w-9 h-9 app-logo-mono"
/>
</div>
</div>
<h1 class="text-2xl font-bold tracking-tight mb-2 text-foreground">Authentication Required</h1>
<p class="text-muted-foreground">Please sign in to access Trackeep</p>
+20 -20
View File
@@ -1,32 +1,32 @@
import { useAuth } from '@/lib/auth';
import { AuthenticationWarning } from '@/components/AuthenticationWarning';
import { isDemoMode } from '@/lib/demo-mode';
import { Show } from 'solid-js';
interface ProtectedRouteProps {
children: any;
}
export const ProtectedRoute = (props: ProtectedRouteProps) => {
// In demo mode, show UI immediately without any checks
if (isDemoMode()) {
console.log('[ProtectedRoute] Demo mode active - showing UI immediately');
return props.children;
}
const { authState } = useAuth();
console.log('[ProtectedRoute] Render:', {
isDemoMode: isDemoMode(),
isAuthenticated: authState.isAuthenticated,
isLoading: authState.isLoading
});
// If not authenticated, show authentication warning (no loading state)
if (!authState.isAuthenticated) {
console.log('[ProtectedRoute] Rendering authentication warning');
return <AuthenticationWarning />;
}
console.log('[ProtectedRoute] Rendering children');
return props.children;
return (
<Show when={!isDemoMode()} fallback={props.children}>
<Show
when={!authState.isLoading}
fallback={
<div class="min-h-screen bg-background flex items-center justify-center px-4 py-8">
<div class="text-center">
<div class="inline-block w-8 h-8 border-2 border-primary border-r-transparent rounded-full animate-spin mb-3"></div>
<p class="text-sm text-muted-foreground">Checking authentication...</p>
</div>
</div>
}
>
<Show when={authState.isAuthenticated} fallback={<AuthenticationWarning />}>
{props.children}
</Show>
</Show>
</Show>
);
};
+1 -7
View File
@@ -16,6 +16,7 @@ import {
type TimeEntry
} from '../lib/api';
import { TagPicker } from '@/components/ui/TagPicker';
import { isDemoMode } from '@/lib/demo-mode';
interface TimerProps {
onTimeEntryCreated?: (timeEntry: TimeEntry) => void;
@@ -38,13 +39,6 @@ export const Timer = (props: TimerProps) => {
const [showSettings, setShowSettings] = createSignal(false);
const [availableTags, setAvailableTags] = createSignal<string[]>([]);
// Check if we're in demo mode
const isDemoMode = () => {
return localStorage.getItem('demoMode') === 'true' ||
document.title.includes('Demo Mode') ||
window.location.search.includes('demo=true');
};
// Use appropriate API based on demo mode
const getApi = () => isDemoMode() ? demoTimeEntriesApi : timeEntriesApi;
@@ -1,6 +1,7 @@
import { createSignal, Show } from 'solid-js'
import { IconX, IconSend, IconUser, IconChevronDown } from '@tabler/icons-solidjs'
import longcatIcon from '@/assets/longcat-color.svg'
import { ModalPortal } from '@/components/ui/ModalPortal'
interface FloatingAIProps {
onToggleChat: () => void
@@ -79,8 +80,9 @@ export function FloatingAI(props: FloatingAIProps) {
{/* AI Chat Modal */}
<Show when={props.isChatOpen}>
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 mt-0 p-4">
<div class="bg-card border border-border rounded-lg shadow-xl max-w-md w-full max-h-[600px] flex flex-col" style="width: 420px;">
<ModalPortal>
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div class="bg-card border border-border rounded-lg shadow-xl max-w-md w-full max-h-[600px] flex flex-col" style="width: 420px;">
{/* Header */}
<div class="flex items-center justify-between p-4 border-b border-border bg-gradient-to-r from-primary/10 to-primary/5">
<div class="flex items-center gap-3">
@@ -177,8 +179,9 @@ export function FloatingAI(props: FloatingAIProps) {
</button>
</div>
</div>
</div>
</div>
</div>
</ModalPortal>
</Show>
</>
)
+12
View File
@@ -56,6 +56,18 @@ export function Header(props: HeaderProps) {
<div class="flex justify-between px-6 pt-4 pb-4">
{/* Left side */}
<div class="flex items-center">
<a
href="/app"
class="hidden sm:inline-flex items-center gap-2 rounded-md px-2 py-1.5 mr-2 hover:bg-accent/40 transition-colors"
>
<img
src="/trackeep.svg"
alt="Trackeep Logo"
class="w-6 h-6 app-logo-mono"
/>
<span class="text-sm font-semibold tracking-tight text-foreground">Trackeep</span>
</a>
{/* Menu button */}
<button
type="button"
+13 -3
View File
@@ -14,9 +14,16 @@ export interface LayoutProps {
export function Layout(props: LayoutProps) {
const resolved = children(() => props.children)
const [isChatOpen, setIsChatOpen] = createSignal(false)
const [isSidebarOpen, setIsSidebarOpen] = createSignal(false)
const [isSidebarOpen, setIsSidebarOpen] = createSignal(true)
onMount(() => {
const savedSidebarState = localStorage.getItem('trackeep_sidebar_open')
if (savedSidebarState !== null) {
setIsSidebarOpen(savedSidebarState === 'true')
} else {
setIsSidebarOpen(window.innerWidth >= 768)
}
// Initialize dark mode from localStorage or system preference
const savedTheme = localStorage.getItem('theme')
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
@@ -143,11 +150,14 @@ export function Layout(props: LayoutProps) {
}
const toggleSidebar = () => {
setIsSidebarOpen(!isSidebarOpen())
const nextValue = !isSidebarOpen()
setIsSidebarOpen(nextValue)
localStorage.setItem('trackeep_sidebar_open', String(nextValue))
}
const closeSidebar = () => {
setIsSidebarOpen(false)
localStorage.setItem('trackeep_sidebar_open', 'false')
}
return (
@@ -157,7 +167,7 @@ export function Layout(props: LayoutProps) {
{/* Mobile Sidebar Overlay */}
{isSidebarOpen() && (
<div
class="fixed inset-0 bg-black/50 z-40"
class="fixed inset-0 bg-black/50 z-40 md:hidden"
onClick={closeSidebar}
/>
)}
+351 -31
View File
@@ -1,4 +1,4 @@
import { For, createSignal, onMount, Show } from 'solid-js'
import { For, createSignal, onCleanup, onMount, Show } from 'solid-js'
import { A, useLocation } from '@solidjs/router'
import {
IconBookmark,
@@ -21,10 +21,15 @@ import {
IconMessageCircle,
IconLogout,
IconBuilding,
IconPlus,
IconX
IconPlus
} from '@tabler/icons-solidjs'
import { UpdateChecker } from '../ui/UpdateChecker'
import { Input } from '../ui/Input'
import { Button } from '../ui/Button'
import { Switch } from '../ui/Switch'
import { ModalPortal } from '../ui/ModalPortal'
import { useAuth } from '@/lib/auth'
import { getApiV1BaseUrl } from '@/lib/api-url'
const navigation = [
{ name: 'Home', href: '/app', icon: IconHome },
@@ -43,11 +48,23 @@ const navigation = [
{ name: 'AI Assistant', href: '/app/chat', icon: IconBrain },
]
const mockWorkspaces = [
{ id: '1', name: 'Trackeep Workspace', icon: IconFileText },
{ id: '2', name: 'Personal Projects', icon: IconBuilding },
{ id: '3', name: 'Team Collaboration', icon: IconUsers },
]
const API_BASE_URL = getApiV1BaseUrl()
const DEFAULT_WORKSPACE_NAME = 'Trackeep Workspace'
interface WorkspaceOption {
id: string
name: string
icon: typeof IconFileText
}
const getWorkspaceIcon = (name: string) => {
const lower = name.toLowerCase()
if (lower.includes('team')) return IconUsers
if (lower.includes('personal')) return IconBuilding
return IconFileText
}
const getAuthToken = () => localStorage.getItem('trackeep_token') || localStorage.getItem('token') || ''
export interface SidebarProps {
class?: string
@@ -57,8 +74,35 @@ export interface SidebarProps {
export function Sidebar(props: SidebarProps) {
const location = useLocation()
const { logout } = useAuth()
const [isWorkspaceDropdownOpen, setIsWorkspaceDropdownOpen] = createSignal(false)
const [selectedWorkspace, setSelectedWorkspace] = createSignal(mockWorkspaces[0])
const [workspaces, setWorkspaces] = createSignal<WorkspaceOption[]>([])
const [selectedWorkspaceId, setSelectedWorkspaceId] = createSignal<string>('')
const [isCreateWorkspaceModalOpen, setIsCreateWorkspaceModalOpen] = createSignal(false)
const [workspaceName, setWorkspaceName] = createSignal('')
const [workspaceDescription, setWorkspaceDescription] = createSignal('')
const [workspaceIsPublic, setWorkspaceIsPublic] = createSignal(false)
const [isCreatingWorkspace, setIsCreatingWorkspace] = createSignal(false)
const [createWorkspaceError, setCreateWorkspaceError] = createSignal('')
const selectedWorkspace = () => {
const list = workspaces()
const found = list.find((workspace) => workspace.id === selectedWorkspaceId())
return found || list[0] || { id: 'default', name: DEFAULT_WORKSPACE_NAME, icon: IconFileText }
}
const persistSelectedWorkspace = (workspace: WorkspaceOption) => {
localStorage.setItem('trackeep_workspace_id', workspace.id)
localStorage.setItem('trackeep_workspace_name', workspace.name)
window.dispatchEvent(
new CustomEvent('trackeep:workspace-changed', {
detail: {
id: workspace.id,
name: workspace.name,
},
}),
)
}
const isActive = (href: string) => {
const currentPath = location.pathname
@@ -66,17 +110,206 @@ export function Sidebar(props: SidebarProps) {
return currentPath === href
}
const handleWorkspaceSelect = (workspace: typeof mockWorkspaces[0]) => {
setSelectedWorkspace(workspace)
const handleWorkspaceSelect = (workspace: WorkspaceOption) => {
setSelectedWorkspaceId(workspace.id)
persistSelectedWorkspace(workspace)
setIsWorkspaceDropdownOpen(false)
}
const resetCreateWorkspaceForm = () => {
setWorkspaceName('')
setWorkspaceDescription('')
setWorkspaceIsPublic(false)
setCreateWorkspaceError('')
}
const openCreateWorkspaceModal = () => {
setIsWorkspaceDropdownOpen(false)
resetCreateWorkspaceForm()
setIsCreateWorkspaceModalOpen(true)
}
const closeCreateWorkspaceModal = () => {
if (isCreatingWorkspace()) return
setIsCreateWorkspaceModalOpen(false)
resetCreateWorkspaceForm()
}
const toggleWorkspaceDropdown = () => {
setIsWorkspaceDropdownOpen(!isWorkspaceDropdownOpen())
}
const normalizeWorkspace = (team: { id?: number | string; name?: string }): WorkspaceOption => {
const name = team.name?.trim() || DEFAULT_WORKSPACE_NAME
return {
id: String(team.id ?? `workspace-${Date.now()}`),
name,
icon: getWorkspaceIcon(name),
}
}
const createDefaultWorkspace = async (token: string): Promise<WorkspaceOption | null> => {
const response = await fetch(`${API_BASE_URL}/teams`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
name: DEFAULT_WORKSPACE_NAME,
description: 'Default workspace',
is_public: false,
}),
})
if (!response.ok) {
return null
}
const data = await response.json()
if (!data?.team) {
return null
}
return normalizeWorkspace(data.team)
}
const loadWorkspaces = async () => {
const token = getAuthToken()
if (!token) {
const fallbackWorkspace = {
id: 'local-default',
name: DEFAULT_WORKSPACE_NAME,
icon: IconFileText,
}
setWorkspaces([fallbackWorkspace])
setSelectedWorkspaceId(fallbackWorkspace.id)
persistSelectedWorkspace(fallbackWorkspace)
return
}
try {
const response = await fetch(`${API_BASE_URL}/teams`, {
headers: {
Authorization: `Bearer ${token}`,
},
})
let mappedWorkspaces: WorkspaceOption[] = []
if (response.ok) {
const data = await response.json()
const teams = Array.isArray(data?.teams) ? data.teams : []
mappedWorkspaces = teams.map(normalizeWorkspace)
}
if (mappedWorkspaces.length === 0) {
const created = await createDefaultWorkspace(token)
if (created) {
mappedWorkspaces = [created]
}
}
if (mappedWorkspaces.length === 0) {
mappedWorkspaces = [
{
id: 'local-default',
name: DEFAULT_WORKSPACE_NAME,
icon: IconFileText,
},
]
}
setWorkspaces(mappedWorkspaces)
const persistedWorkspaceId = localStorage.getItem('trackeep_workspace_id') || ''
const initialSelection =
mappedWorkspaces.find((workspace) => workspace.id === persistedWorkspaceId) || mappedWorkspaces[0]
setSelectedWorkspaceId(initialSelection.id)
persistSelectedWorkspace(initialSelection)
} catch (error) {
console.error('Failed to load workspaces:', error)
const fallbackWorkspace = {
id: 'local-default',
name: DEFAULT_WORKSPACE_NAME,
icon: IconFileText,
}
setWorkspaces([fallbackWorkspace])
setSelectedWorkspaceId(fallbackWorkspace.id)
persistSelectedWorkspace(fallbackWorkspace)
}
}
const handleCreateWorkspace = async () => {
const trimmed = workspaceName().trim()
const description = workspaceDescription().trim()
const isPublic = workspaceIsPublic()
if (!trimmed) {
setCreateWorkspaceError('Workspace name is required.')
return
}
setCreateWorkspaceError('')
setIsCreatingWorkspace(true)
const token = getAuthToken()
if (!token) {
const localWorkspace = {
id: `local-${Date.now()}`,
name: trimmed,
icon: getWorkspaceIcon(trimmed),
}
setWorkspaces((prev) => [localWorkspace, ...prev])
handleWorkspaceSelect(localWorkspace)
setIsCreateWorkspaceModalOpen(false)
resetCreateWorkspaceForm()
setIsCreatingWorkspace(false)
return
}
try {
const response = await fetch(`${API_BASE_URL}/teams`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
name: trimmed,
description,
is_public: isPublic,
}),
})
if (!response.ok) {
let message = `Failed to create workspace: ${response.status}`
try {
const data = await response.json()
message = data?.error || data?.message || message
} catch {
// Keep fallback message
}
throw new Error(message)
}
const data = await response.json()
const createdWorkspace = normalizeWorkspace(data.team)
setWorkspaces((prev) => [createdWorkspace, ...prev])
handleWorkspaceSelect(createdWorkspace)
setIsCreateWorkspaceModalOpen(false)
resetCreateWorkspaceForm()
} catch (error) {
console.error('Failed to create workspace:', error)
setCreateWorkspaceError(error instanceof Error ? error.message : 'Failed to create workspace.')
} finally {
setIsCreatingWorkspace(false)
}
}
// Close dropdown when clicking outside
onMount(() => {
void loadWorkspaces()
const handleClickOutside = (event: MouseEvent) => {
const target = event.target
if (!(target instanceof HTMLElement)) return
@@ -85,28 +318,34 @@ export function Sidebar(props: SidebarProps) {
}
}
document.addEventListener('click', handleClickOutside)
return () => document.removeEventListener('click', handleClickOutside)
onCleanup(() => document.removeEventListener('click', handleClickOutside))
})
return (
<>
{/* Mobile Close Button - Above sidebar */}
<Show when={props.isOpen}>
<button
onClick={props.onClose}
class="fixed top-4 right-4 z-50 md:hidden inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</Show>
<div class={`fixed inset-y-0 left-0 z-50 w-280px border-r border-r-border flex-shrink-0 bg-card transform transition-transform duration-300 ease-in-out md:relative md:translate-x-0 ${
props.isOpen ? 'translate-x-0' : '-translate-x-full'
}`}>
<div class="flex h-full">
<div
class={`fixed inset-y-0 left-0 z-50 border-r border-r-border bg-card transition-all duration-300 ease-in-out overflow-hidden md:relative md:inset-y-auto md:left-auto md:transform-none ${
props.isOpen ? 'w-[280px] translate-x-0' : 'w-[280px] -translate-x-full md:w-0 md:translate-x-0 md:pointer-events-none'
}`}
>
<div class="w-[280px] h-full flex">
<div class="h-full flex flex-col pb-6 flex-1 min-w-0">
<div class="px-4 pt-4">
<A
href="/app"
class="flex items-center gap-3 rounded-lg px-2 py-2 hover:bg-accent/40 transition-colors"
>
<img
src="/trackeep.svg"
alt="Trackeep Logo"
class="w-7 h-7 app-logo-mono"
/>
<span class="font-semibold tracking-tight text-foreground">Trackeep</span>
</A>
</div>
{/* Organization Selector */}
<div class="p-4 pb-0 min-w-0 max-w-full" id="workspace-selector">
<div class="p-4 pb-0 pt-3 min-w-0 max-w-full" id="workspace-selector">
<div role="group" class="w-full relative">
<button
type="button"
@@ -133,7 +372,7 @@ export function Sidebar(props: SidebarProps) {
<Show when={isWorkspaceDropdownOpen()}>
<div class="absolute top-full left-0 right-0 mt-1 bg-popover border border-border rounded-md shadow-lg z-50 max-h-60 overflow-auto">
<div class="p-1" role="listbox">
<For each={mockWorkspaces}>
<For each={workspaces()}>
{(workspace) => (
<button
type="button"
@@ -156,6 +395,7 @@ export function Sidebar(props: SidebarProps) {
<div class="border-t border-border mt-1 pt-1">
<button
type="button"
onClick={openCreateWorkspaceModal}
class="flex w-full items-center gap-2 px-3 py-2 text-sm rounded-sm hover:bg-accent/50 transition-colors focus:bg-accent/50 focus:outline-none text-muted-foreground"
>
<IconPlus class="size-4" />
@@ -262,9 +502,8 @@ export function Sidebar(props: SidebarProps) {
}}></div>
</A>
<button
onClick={() => {
// Handle logout logic here
localStorage.removeItem('auth_token')
onClick={async () => {
await logout()
window.location.href = '/login'
}}
class="group inline-flex rounded-md text-sm font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 h-9 px-4 py-2 justify-start items-center gap-2 truncate w-full relative overflow-hidden hover:bg-destructive/10 hover:text-destructive dark:text-muted-foreground"
@@ -279,6 +518,87 @@ export function Sidebar(props: SidebarProps) {
</div>
</div>
</div>
<Show when={isCreateWorkspaceModalOpen()}>
<ModalPortal>
<>
<div
class="fixed inset-0 z-[90] bg-black/50"
onClick={closeCreateWorkspaceModal}
/>
<div class="fixed top-1/2 left-1/2 z-[100] w-full max-w-md -translate-x-1/2 -translate-y-1/2 px-4">
<div class="rounded-lg border border-border bg-card shadow-xl">
<div class="border-b border-border p-5">
<h3 class="text-lg font-semibold text-foreground">Create Workspace</h3>
<p class="mt-1 text-sm text-muted-foreground">Add a new workspace for your team or projects.</p>
</div>
<div
class="space-y-4 p-5"
>
<div class="space-y-1.5">
<label class="text-sm font-medium text-foreground">
Name
</label>
<Input
type="text"
placeholder="Workspace name"
value={workspaceName()}
onInput={(event) => setWorkspaceName((event.currentTarget as HTMLInputElement).value)}
required
disabled={isCreatingWorkspace()}
/>
</div>
<div class="space-y-1.5">
<label class="text-sm font-medium text-foreground" for="workspace-description">
Description
</label>
<textarea
id="workspace-description"
rows={3}
class="flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
placeholder="Optional description"
value={workspaceDescription()}
onInput={(event) => setWorkspaceDescription((event.currentTarget as HTMLTextAreaElement).value)}
disabled={isCreatingWorkspace()}
/>
</div>
<div class="flex items-center justify-between rounded-md border border-border bg-background px-3 py-2">
<div>
<p class="text-sm font-medium text-foreground">Public workspace</p>
<p class="text-xs text-muted-foreground">Allow all members to discover this workspace.</p>
</div>
<Switch
checked={workspaceIsPublic()}
onCheckedChange={setWorkspaceIsPublic}
disabled={isCreatingWorkspace()}
/>
</div>
<Show when={createWorkspaceError()}>
<p class="text-sm text-destructive">{createWorkspaceError()}</p>
</Show>
<div class="flex justify-end gap-2 pt-2">
<Button
variant="outline"
onClick={closeCreateWorkspaceModal}
disabled={isCreatingWorkspace()}
>
Cancel
</Button>
<Button onClick={() => void handleCreateWorkspace()} disabled={isCreatingWorkspace()}>
{isCreatingWorkspace() ? 'Creating...' : 'Create Workspace'}
</Button>
</div>
</div>
</div>
</div>
</>
</ModalPortal>
</Show>
</>
)
}
+112 -123
View File
@@ -11,6 +11,9 @@ import {
IconClock,
IconExternalLink
} from '@tabler/icons-solidjs';
import { getApiV1BaseUrl } from '@/lib/api-url';
const API_BASE_URL = getApiV1BaseUrl();
interface ActivityItem {
id: string;
@@ -27,6 +30,7 @@ interface ActivityItem {
language?: string;
tags?: string[];
};
displayTimestamp?: string;
}
interface ActivityFeedProps {
@@ -40,6 +44,21 @@ export const ActivityFeed = (props: ActivityFeedProps) => {
const [filter, setFilter] = createSignal<'all' | 'trackeep' | 'github'>('all');
const [loading, setLoading] = createSignal(true);
const normalizeActivityType = (type: string): ActivityItem['type'] => {
if (type === 'bookmark' || type === 'task' || type === 'note' || type === 'file') {
return type;
}
return 'task';
};
const formatTimestamp = (value: string): string => {
const parsed = new Date(value);
if (Number.isNaN(parsed.getTime())) {
return value;
}
return parsed.toISOString().split('T')[0];
};
const getActivityIcon = (type: string) => {
switch (type) {
case 'bookmark': return IconBookmark;
@@ -58,78 +77,36 @@ export const ActivityFeed = (props: ActivityFeedProps) => {
try {
setLoading(true);
// Import mock data for demo mode
const { getMockActivities } = await import('@/lib/mockData');
// Combine and format activities
const combinedActivities: ActivityItem[] = [];
// Add Trackeep activities from mock data
const mockActivities = getMockActivities();
const now = new Date();
mockActivities.forEach((activity, index) => {
// Create realistic timestamps
const timestamp = new Date(now.getTime() - (index * 3600000)); // Each activity 1 hour apart
combinedActivities.push({
id: activity.id,
type: activity.type as any,
title: activity.title,
description: `${activity.action} ${activity.type}`,
timestamp: timestamp.toISOString(),
source: 'trackeep' as const,
metadata: {
tags: activity.details?.tags ? Object.keys(activity.details.tags) : undefined
}
});
const token = localStorage.getItem('trackeep_token') || localStorage.getItem('token');
const response = await fetch(`${API_BASE_URL}/dashboard/stats`, {
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
});
// Add some GitHub-style activities
const githubActivities = [
{
id: 'github_1',
type: 'github_commit' as const,
title: 'Fixed responsive design issues',
description: 'Resolved mobile layout problems on dashboard',
timestamp: new Date(now.getTime() - 2 * 3600000).toISOString(),
source: 'github' as const,
metadata: {
repo: 'tdvorak/trackeep',
url: 'https://github.com/tdvorak/trackeep/commit/abc123',
branch: 'main',
language: 'Go'
}
},
{
id: 'github_2',
type: 'github_pr' as const,
title: 'Add AI chat integration',
description: 'Implement LongCat AI provider with model switching',
timestamp: new Date(now.getTime() - 5 * 3600000).toISOString(),
source: 'github' as const,
metadata: {
repo: 'tdvorak/trackeep',
url: 'https://github.com/tdvorak/trackeep/pull/42',
branch: 'feature/ai-chat',
language: 'TypeScript'
}
},
{
id: 'github_3',
type: 'github_star' as const,
title: 'trackeep gained new stars',
description: 'Repository reached 245 stars',
timestamp: new Date(now.getTime() - 8 * 3600000).toISOString(),
source: 'github' as const,
metadata: {
repo: 'tdvorak/trackeep',
url: 'https://github.com/tdvorak/trackeep'
}
}
];
if (!response.ok) {
throw new Error(`Failed to fetch activities: ${response.status}`);
}
combinedActivities.push(...githubActivities);
const data = await response.json();
const recentActivities: Array<{ id?: number; type?: string; title?: string; timestamp?: string }> = Array.isArray(data.recentActivity)
? data.recentActivity
: [];
recentActivities.forEach((activity, index) => {
combinedActivities.push({
id: String(activity.id ?? `activity-${index}`),
type: normalizeActivityType(activity.type || ''),
title: activity.title || 'Activity',
description: activity.type || 'trackeep',
timestamp: new Date().toISOString(),
displayTimestamp: activity.timestamp || '',
source: 'trackeep',
});
});
// Sort by timestamp (most recent first)
combinedActivities.sort((a, b) =>
@@ -149,6 +126,7 @@ export const ActivityFeed = (props: ActivityFeedProps) => {
setActivities(limitedActivities);
} catch (error) {
console.error('Failed to fetch activities:', error);
setActivities([]);
} finally {
setLoading(false);
}
@@ -179,7 +157,10 @@ export const ActivityFeed = (props: ActivityFeedProps) => {
{props.showFilter && (
<div class="flex gap-2">
<button
onClick={() => setFilter('all')}
onClick={() => {
setFilter('all');
fetchActivities();
}}
class={`px-3 py-1 rounded-lg text-sm transition-colors ${
filter() === 'all'
? 'bg-[#262626] text-[#fafafa]'
@@ -189,7 +170,10 @@ export const ActivityFeed = (props: ActivityFeedProps) => {
All
</button>
<button
onClick={() => setFilter('trackeep')}
onClick={() => {
setFilter('trackeep');
fetchActivities();
}}
class={`px-3 py-1 rounded-lg text-sm transition-colors ${
filter() === 'trackeep'
? 'bg-[#262626] text-[#fafafa]'
@@ -199,7 +183,10 @@ export const ActivityFeed = (props: ActivityFeedProps) => {
Trackeep
</button>
<button
onClick={() => setFilter('github')}
onClick={() => {
setFilter('github');
fetchActivities();
}}
class={`px-3 py-1 rounded-lg text-sm transition-colors ${
filter() === 'github'
? 'bg-[#262626] text-[#fafafa]'
@@ -220,68 +207,70 @@ export const ActivityFeed = (props: ActivityFeedProps) => {
)}
{/* Activity List */}
<div class="space-y-3 flex-1 min-h-0 overflow-y-auto max-h-96 scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent">
<For each={activities()}>
{(activity) => {
const Icon = getActivityIcon(activity.type);
{activities().length > 0 && (
<div class="space-y-3 flex-1 min-h-0 overflow-y-auto max-h-96 scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent">
<For each={activities()}>
{(activity) => {
const Icon = getActivityIcon(activity.type);
return (
<div class="flex items-center justify-between p-3 bg-card rounded-lg border hover:bg-muted/50 transition-colors">
<div class="flex items-center gap-3">
<div class="bg-primary/10 p-2 rounded-lg">
<Icon class="size-4 text-primary" />
</div>
<div class="flex-1">
<p class="text-sm text-foreground font-medium">
{activity.title}
</p>
<div class="flex items-center gap-2 text-xs text-muted-foreground mt-1">
<span>{new Date(activity.timestamp).toISOString().split('T')[0]}</span>
<span></span>
<span class="text-primary">
{activity.source === 'github'
? (activity.metadata?.repo?.split('/').pop() || 'GitHub')
: 'trackeep'}
</span>
<span></span>
<span>
{activity.source === 'github'
? activity.type === 'github_commit'
? 'pushed'
: activity.type === 'github_pr'
? 'opened PR'
: activity.type === 'github_star'
? 'starred'
: activity.type === 'github_fork'
? 'forked'
: 'activity'
: activity.description || activity.type}
</span>
return (
<div class="flex items-center justify-between p-3 bg-card rounded-lg border hover:bg-muted/50 transition-colors">
<div class="flex items-center gap-3">
<div class="bg-primary/10 p-2 rounded-lg">
<Icon class="size-4 text-primary" />
</div>
<div class="flex-1">
<p class="text-sm text-foreground font-medium">
{activity.title}
</p>
<div class="flex items-center gap-2 text-xs text-muted-foreground mt-1">
<span>{activity.displayTimestamp || formatTimestamp(activity.timestamp)}</span>
<span></span>
<span class="text-primary">
{activity.source === 'github'
? (activity.metadata?.repo?.split('/').pop() || 'GitHub')
: 'trackeep'}
</span>
<span></span>
<span>
{activity.source === 'github'
? activity.type === 'github_commit'
? 'pushed'
: activity.type === 'github_pr'
? 'opened PR'
: activity.type === 'github_star'
? 'starred'
: activity.type === 'github_fork'
? 'forked'
: 'activity'
: activity.description || activity.type}
</span>
</div>
</div>
</div>
{activity.metadata?.url && (
<a
href={activity.metadata.url}
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8 ml-2"
onClick={(e) => e.stopPropagation()}
>
<IconExternalLink class="size-4 text-primary" />
</a>
)}
</div>
{activity.metadata?.url && (
<a
href={activity.metadata.url}
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8 ml-2"
onClick={(e) => e.stopPropagation()}
>
<IconExternalLink class="size-4 text-primary" />
</a>
)}
</div>
);
}}
</For>
</div>
);
}}
</For>
</div>
)}
{/* Empty State */}
{!loading() && activities().length === 0 && (
<div class="text-center py-8">
<IconClock class="size-12 text-[#a3a3a3] mx-auto mb-4" />
<p class="text-[#a3a3a3]">No recent activity found</p>
<p class="text-[#a3a3a3]">No activity yet</p>
<p class="text-sm text-[#a3a3a3] mt-1">
{filter() === 'github' ? 'Connect your GitHub account to see activity' : 'Start using Trackeep to see your activity here'}
</p>
+79 -76
View File
@@ -2,6 +2,7 @@ import { createSignal, createEffect } from 'solid-js';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { TagPicker } from '@/components/ui/TagPicker';
import { ModalPortal } from '@/components/ui/ModalPortal';
import { IconX } from '@tabler/icons-solidjs';
interface BookmarkModalProps {
@@ -52,92 +53,94 @@ export const BookmarkModal = (props: BookmarkModalProps) => {
};
return (
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-[60] mt-0" onClick={props.onClose} />
)}
<ModalPortal>
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-[60]" onClick={props.onClose} />
)}
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-[70] ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: min(500px, 90vw); max-height: min(80vh, 600px); overflow-y: auto;">
{/* Header */}
<div class="flex items-center justify-between p-4 sm:p-6 border-b border-border">
<h3 class="text-lg font-semibold">Add New Bookmark</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-[70] ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: min(500px, 90vw); max-height: min(80vh, 600px); overflow-y: auto;">
{/* Header */}
<div class="flex items-center justify-between p-4 sm:p-6 border-b border-border">
<h3 class="text-lg font-semibold">Add New Bookmark</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
{/* Content */}
<div class="p-4 sm:p-6 space-y-4">
<div class="relative">
{/* Content */}
<div class="p-4 sm:p-6 space-y-4">
<div class="relative">
<Input
type="url"
placeholder="URL *"
value={newBookmark().url}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setNewBookmark(prev => ({ ...prev, url: target.value }));
}}
required
class="pr-12"
/>
{faviconPreview() && (
<div class="absolute right-3 top-1/2 transform -translate-y-1/2 w-6 h-6 bg-muted rounded flex items-center justify-center overflow-hidden">
<img
src={faviconPreview()}
alt="Site favicon"
class="w-4 h-4 object-contain"
onError={(e) => { e.currentTarget.style.display = 'none'; }}
/>
</div>
)}
</div>
<Input
type="url"
placeholder="URL *"
value={newBookmark().url}
type="text"
placeholder="Title (optional)"
value={newBookmark().title}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setNewBookmark(prev => ({ ...prev, url: target.value }));
if (target) setNewBookmark(prev => ({ ...prev, title: target.value }));
}}
required
class="pr-12"
/>
{faviconPreview() && (
<div class="absolute right-3 top-1/2 transform -translate-y-1/2 w-6 h-6 bg-muted rounded flex items-center justify-center overflow-hidden">
<img
src={faviconPreview()}
alt="Site favicon"
class="w-4 h-4 object-contain"
onError={(e) => { e.currentTarget.style.display = 'none'; }}
/>
</div>
)}
</div>
<Input
type="text"
placeholder="Title (optional)"
value={newBookmark().title}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setNewBookmark(prev => ({ ...prev, title: target.value }));
}}
/>
<Input
type="text"
placeholder="Description (optional)"
value={newBookmark().description}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setNewBookmark(prev => ({ ...prev, description: target.value }));
}}
/>
<div class="space-y-2">
<label class="text-sm font-medium text-muted-foreground">Tags</label>
<TagPicker
availableTags={availableTags()}
selectedTags={tags()}
onTagsChange={(next) => setTags(next)}
placeholder="Add tags..."
allowNew={true}
<Input
type="text"
placeholder="Description (optional)"
value={newBookmark().description}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setNewBookmark(prev => ({ ...prev, description: target.value }));
}}
/>
<div class="space-y-2">
<label class="text-sm font-medium text-muted-foreground">Tags</label>
<TagPicker
availableTags={availableTags()}
selectedTags={tags()}
onTagsChange={(next) => setTags(next)}
placeholder="Add tags..."
allowNew={true}
/>
</div>
</div>
</div>
{/* Footer */}
<div class="flex flex-col sm:flex-row justify-end gap-2 p-4 sm:p-6 border-t border-border">
<Button variant="outline" onClick={props.onClose}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={!newBookmark().url.trim()}>
Save Bookmark
</Button>
{/* Footer */}
<div class="flex flex-col sm:flex-row justify-end gap-2 p-4 sm:p-6 border-t border-border">
<Button variant="outline" onClick={props.onClose}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={!newBookmark().url.trim()}>
Save Bookmark
</Button>
</div>
</div>
</div>
</>
</>
</ModalPortal>
);
};
@@ -386,10 +386,6 @@
inset: -5px;
}
.-translate-y-1\/2 {
transform: translateY(-50%);
}
/* Z-index utilities */
.z-50 {
z-index: 50;
+38 -35
View File
@@ -1,4 +1,5 @@
import { Button } from '@/components/ui/Button';
import { ModalPortal } from '@/components/ui/ModalPortal';
import { IconX, IconAlertTriangle } from '@tabler/icons-solidjs';
interface ConfirmModalProps {
@@ -45,45 +46,47 @@ export const ConfirmModal = (props: ConfirmModalProps) => {
};
return (
<>
{/* Backdrop */}
{isOpen && (
<div class="fixed inset-0 bg-black/50 z-40 mt-0" onClick={onClose} />
)}
<ModalPortal>
<>
{/* Backdrop */}
{isOpen && (
<div class="fixed inset-0 bg-black/50 z-40" onClick={onClose} />
)}
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 400px; max-width: 90vw;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<div class="flex items-center gap-3">
{getIcon()}
<h3 class="text-lg font-semibold">{title}</h3>
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 400px; max-width: 90vw;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<div class="flex items-center gap-3">
{getIcon()}
<h3 class="text-lg font-semibold">{title}</h3>
</div>
<button
onClick={onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
<button
onClick={onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
{/* Content */}
<div class="p-6">
<p class="text-muted-foreground">{message}</p>
</div>
{/* Content */}
<div class="p-6">
<p class="text-muted-foreground">{message}</p>
</div>
{/* Footer */}
<div class="flex justify-end gap-2 p-6 border-t border-border">
<Button variant="outline" onClick={onClose}>
{cancelText}
</Button>
<Button variant={getConfirmButtonVariant()} onClick={onConfirm}>
{confirmText}
</Button>
{/* Footer */}
<div class="flex justify-end gap-2 p-6 border-t border-border">
<Button variant="outline" onClick={onClose}>
{cancelText}
</Button>
<Button variant={getConfirmButtonVariant()} onClick={onConfirm}>
{confirmText}
</Button>
</div>
</div>
</div>
</>
</>
</ModalPortal>
);
};
@@ -2,6 +2,7 @@ import { createSignal, onMount } from 'solid-js';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { TagPicker } from '@/components/ui/TagPicker';
import { ModalPortal } from '@/components/ui/ModalPortal';
import { IconX } from '@tabler/icons-solidjs';
interface Bookmark {
@@ -71,79 +72,81 @@ export const EditBookmarkModal = (props: EditBookmarkModalProps) => {
};
return (
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-40 mt-0" onClick={props.onClose} />
)}
<ModalPortal>
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-40" onClick={props.onClose} />
)}
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 500px; max-width: 90vw;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<h3 class="text-lg font-semibold">Edit Bookmark</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 500px; max-width: 90vw;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<h3 class="text-lg font-semibold">Edit Bookmark</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
{/* Content */}
<div class="p-6 space-y-4">
<Input
type="url"
placeholder="URL *"
value={editBookmark().url}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setEditBookmark(prev => ({ ...prev, url: target.value }));
}}
required
/>
<Input
type="text"
placeholder="Title"
value={editBookmark().title}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setEditBookmark(prev => ({ ...prev, title: target.value }));
}}
/>
<Input
type="text"
placeholder="Description"
value={editBookmark().description}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setEditBookmark(prev => ({ ...prev, description: target.value }));
}}
/>
<div class="space-y-2">
<label class="text-sm font-medium text-muted-foreground">Tags</label>
<TagPicker
availableTags={availableTags()}
selectedTags={tags()}
onTagsChange={(next) => setTags(next)}
placeholder="Add tags..."
allowNew={true}
{/* Content */}
<div class="p-6 space-y-4">
<Input
type="url"
placeholder="URL *"
value={editBookmark().url}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setEditBookmark(prev => ({ ...prev, url: target.value }));
}}
required
/>
<Input
type="text"
placeholder="Title"
value={editBookmark().title}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setEditBookmark(prev => ({ ...prev, title: target.value }));
}}
/>
<Input
type="text"
placeholder="Description"
value={editBookmark().description}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setEditBookmark(prev => ({ ...prev, description: target.value }));
}}
/>
<div class="space-y-2">
<label class="text-sm font-medium text-muted-foreground">Tags</label>
<TagPicker
availableTags={availableTags()}
selectedTags={tags()}
onTagsChange={(next) => setTags(next)}
placeholder="Add tags..."
allowNew={true}
/>
</div>
</div>
{/* Footer */}
<div class="flex justify-end gap-2 p-6 border-t border-border">
<Button variant="outline" onClick={props.onClose}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={!editBookmark().url.trim()}>
Save Changes
</Button>
</div>
</div>
{/* Footer */}
<div class="flex justify-end gap-2 p-6 border-t border-border">
<Button variant="outline" onClick={props.onClose}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={!editBookmark().url.trim()}>
Save Changes
</Button>
</div>
</div>
</>
</>
</ModalPortal>
);
};
+30 -31
View File
@@ -1,6 +1,8 @@
import { createSignal } from 'solid-js';
import { Button } from '@/components/ui/Button';
import { ModalPortal } from '@/components/ui/ModalPortal';
import { IconX, IconDownload, IconExternalLink, IconEye, IconFile, IconCode, IconFileText, IconAlertTriangle, IconMusic, IconFileDescription, IconChartBar, IconChartLine } from '@tabler/icons-solidjs';
import { isDemoMode } from '@/lib/demo-mode';
interface FilePreviewModalProps {
isOpen: boolean;
@@ -168,12 +170,7 @@ export const FilePreviewModal = (props: FilePreviewModalProps) => {
};
const handleDownload = () => {
// Check if we're in demo mode
const isDemoMode = localStorage.getItem('demoMode') === 'true' ||
document.title.includes('Demo Mode') ||
window.location.search.includes('demo=true');
if (isDemoMode) {
if (isDemoMode()) {
// Simulate download in demo mode
alert(`Download simulated for: ${props.file.name}\n\nIn production, this would download the actual file.`);
return;
@@ -190,31 +187,32 @@ export const FilePreviewModal = (props: FilePreviewModalProps) => {
};
return (
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-40 mt-0" onClick={props.onClose} />
)}
<ModalPortal>
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-40" onClick={props.onClose} />
)}
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 900px; max-width: 95vw; max-height: 85vh;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<div class="flex items-center gap-3 flex-1 min-w-0">
<h3 class="text-lg font-semibold truncate">{props.file?.name}</h3>
<span class="text-sm text-muted-foreground flex-shrink-0">
{props.file?.size ? formatFileSize(props.file.size) : 'Unknown size'}
</span>
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 900px; max-width: 95vw; max-height: 85vh;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<div class="flex items-center gap-3 flex-1 min-w-0">
<h3 class="text-lg font-semibold truncate">{props.file?.name}</h3>
<span class="text-sm text-muted-foreground flex-shrink-0">
{props.file?.size ? formatFileSize(props.file.size) : 'Unknown size'}
</span>
</div>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8 flex-shrink-0"
>
<IconX class="size-4" />
</button>
</div>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8 flex-shrink-0"
>
<IconX class="size-4" />
</button>
</div>
{/* Preview Area */}
<div class="p-6" style="height: 500px;">
@@ -251,7 +249,8 @@ export const FilePreviewModal = (props: FilePreviewModalProps) => {
</Button>
</div>
</div>
</div>
</>
</div>
</>
</ModalPortal>
);
};
+24 -11
View File
@@ -1,5 +1,6 @@
import { createSignal, For, Show } from 'solid-js';
import { cn } from '@/lib/utils';
import { ModalPortal } from './ModalPortal';
import './FileUpload.css';
export interface FileUploadProps {
@@ -191,17 +192,26 @@ export const FileUpload = (props: FileUploadProps) => {
props.onClose?.();
};
if (!props.isOpen) {
return null;
}
return (
<div
class={cn(
"relative w-full rounded-20 bg-bg-white-0 focus:outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 max-w-[440px] shadow-custom-md",
props.class
)}
role="dialog"
aria-labelledby="file-upload-title"
aria-describedby="file-upload-description"
data-state={props.isOpen ? 'open' : 'closed'}
>
<ModalPortal>
<>
<div class="fixed inset-0 z-[80] bg-black/50" onClick={handleClose} />
<div class="fixed top-1/2 left-1/2 z-[90] w-[min(440px,90vw)] max-h-[85vh] -translate-x-1/2 -translate-y-1/2 overflow-y-auto">
<div
class={cn(
"relative w-full rounded-20 bg-bg-white-0 focus:outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 max-w-[440px] shadow-custom-md",
props.class
)}
role="dialog"
aria-labelledby="file-upload-title"
aria-describedby="file-upload-description"
data-state="open"
onClick={(event) => event.stopPropagation()}
>
{/* Header */}
<div class="relative flex items-start gap-3.5 py-4 pl-5 pr-14 before:absolute before:inset-x-0 before:bottom-0 before:border-b before:border-stroke-soft-200">
<div class="flex size-10 shrink-0 items-center justify-center rounded-full bg-bg-white-0 ring-1 ring-inset ring-stroke-soft-200">
@@ -366,6 +376,9 @@ export const FileUpload = (props: FileUploadProps) => {
</div>
</div>
</div>
</div>
</div>
</div>
</>
</ModalPortal>
);
};
+12 -9
View File
@@ -2,6 +2,7 @@ import { createSignal, For, Show, onMount, onCleanup } from 'solid-js';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { Card } from '@/components/ui/Card';
import { ModalPortal } from '@/components/ui/ModalPortal';
import {
IconX,
IconUpload,
@@ -153,15 +154,16 @@ export const FileUploadModal = (props: FileUploadModalProps) => {
};
return (
<Show when={props.isOpen}>
<div
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 mt-0"
onClick={props.onClose}
>
<ModalPortal>
<Show when={props.isOpen}>
<div
class="bg-card rounded-lg border border-border p-6 w-full max-w-2xl max-h-[90vh] overflow-y-auto mx-4 my-4"
onClick={(e) => e.stopPropagation()}
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
onClick={props.onClose}
>
<div
class="bg-card rounded-lg border border-border p-6 w-full max-w-2xl max-h-[90vh] overflow-y-auto mx-4 my-4"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div class="flex items-center justify-between mb-6">
<h2 class="text-xl font-semibold">Upload File</h2>
@@ -382,8 +384,9 @@ export const FileUploadModal = (props: FileUploadModalProps) => {
Upload
</Button>
</div>
</div>
</div>
</div>
</Show>
</Show>
</ModalPortal>
);
};
+123 -231
View File
@@ -51,141 +51,20 @@ export const GitHubActivity = (props: GitHubActivityProps) => {
longestStreak: 0
});
onMount(() => {
// Always show rich mock data for demonstration
generateMockData();
return;
// Original real data loading logic (commented out for demo)
/*
if (isDemoMode()) {
// In demo mode, always show rich mock data
generateMockData();
return;
}
loadRealData().catch((error) => {
console.error('Failed to load GitHub activity analytics, falling back to mock data:', error);
generateMockData();
});
*/
});
const generateMockData = () => {
const activityData: ActivityData[] = [];
const today = new Date();
const oneYearAgo = new Date(today);
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
let currentStreak = 0;
let longestStreak = 0;
let tempStreak = 0;
let totalContributions = 0;
// Generate more realistic activity patterns
for (let d = new Date(oneYearAgo); d <= today; d.setDate(d.getDate() + 1)) {
const dayOfWeek = d.getDay();
const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
const monthsAgo = Math.floor((today.getTime() - d.getTime()) / (30 * 24 * 60 * 60 * 1000));
// More realistic patterns:
// - Higher activity in recent months
// - Lower activity on weekends
// - Some bursts of activity followed by quiet periods
let baseProbability = 0.3; // 30% chance of some activity
// Increase activity for more recent months
if (monthsAgo < 3) baseProbability = 0.7; // Last 3 months: 70% chance
else if (monthsAgo < 6) baseProbability = 0.5; // 3-6 months ago: 50% chance
else baseProbability = 0.3; // 6+ months ago: 30% chance
// Reduce activity on weekends
if (isWeekend) baseProbability *= 0.6;
// Add some randomness and bursts
const hasActivity = Math.random() < baseProbability;
let count = 0;
if (hasActivity) {
// Generate contribution count with some bursts
if (Math.random() < 0.1) {
// 10% chance of high activity burst
count = Math.floor(Math.random() * 15) + 10;
} else {
// Normal activity
count = Math.floor(Math.random() * 8) + 1;
}
}
const level = count === 0 ? 0 : Math.min(5, Math.ceil(count / 2));
activityData.push({
date: new Date(d).toISOString().split('T')[0],
count,
level
});
if (count > 0) {
tempStreak++;
if (d.toDateString() === today.toDateString()) {
currentStreak = tempStreak;
}
} else {
longestStreak = Math.max(longestStreak, tempStreak);
tempStreak = 0;
}
totalContributions += count;
}
const defaultEvents: ActivityEvent[] = [
{
type: 'commit',
title: 'feat: Add advanced color scheme management',
date: '2024-01-28',
link: '/app/activity',
repo: 'trackeep',
action: 'pushed'
},
{
type: 'pull_request',
title: 'Enhance admin settings with toggle buttons',
date: '2024-01-27',
link: '/app/admin',
repo: 'trackeep',
action: 'opened'
},
{
type: 'merge',
title: 'Merge branch: feature/ai-chat-enhancements',
date: '2024-01-26',
link: '/app/chat',
repo: 'trackeep',
action: 'merged'
},
{
type: 'bookmark',
title: 'Added bookmark: Advanced React Patterns',
date: '2024-01-25',
link: '/app/bookmarks'
},
{
type: 'project',
title: 'Updated project: Trackeep Dashboard',
date: '2024-01-24',
link: '/app/projects'
}
];
setActivities(activityData);
setRecentEvents(props.customEvents || defaultEvents);
const setEmptyData = () => {
setActivities([]);
setRecentEvents(props.customEvents || []);
setStats({
totalContributions,
currentStreak,
longestStreak: Math.max(longestStreak, tempStreak)
totalContributions: 0,
currentStreak: 0,
longestStreak: 0
});
};
onMount(() => {
setEmptyData();
});
const getMonthLabels = () => {
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const today = new Date();
@@ -325,75 +204,84 @@ export const GitHubActivity = (props: GitHubActivityProps) => {
</h3>
</div>
{/* Month labels - Show all months with responsive spacing */}
<div class="flex justify-between mb-3 px-6 sm:px-8 text-xs sm:text-sm font-medium overflow-x-auto">
<div class="flex gap-2 sm:gap-3 min-w-max">
{getMonthLabels().map((month) => (
<span class="text-foreground/80 hover:text-foreground transition-colors cursor-default whitespace-nowrap">
{month}
</span>
))}
</div>
</div>
{/* Contribution grid - Responsive and prevents overflow */}
<div class="overflow-hidden w-full">
<div class="flex gap-1 min-w-0">
{/* Day labels */}
<div class="flex flex-col gap-1 pr-2 flex-shrink-0">
{['Mon', 'Wed', 'Fri'].map((day) => (
<div class="h-3 flex items-center justify-end">
<span class="text-xs text-foreground/70 hover:text-foreground transition-colors cursor-default font-medium">
{day}
</span>
</div>
<Show
when={activities().length > 0}
fallback={
<div class="h-44 border border-dashed border-border rounded-lg flex items-center justify-center">
<p class="text-sm text-muted-foreground">No GitHub contribution data yet.</p>
</div>
}
>
{/* Month labels - Show all months with responsive spacing */}
<div class="flex justify-between mb-3 px-6 sm:px-8 text-xs sm:text-sm font-medium overflow-x-auto">
<div class="flex gap-2 sm:gap-3 min-w-max">
{getMonthLabels().map((month) => (
<span class="text-foreground/80 hover:text-foreground transition-colors cursor-default whitespace-nowrap">
{month}
</span>
))}
</div>
</div>
{/* Weekly columns - Responsive with proper overflow handling */}
<div class="flex gap-1 overflow-x-auto overflow-y-hidden min-w-0 pb-2">
{Array.from({ length: 53 }, (_, weekIndex) => (
<div class="flex flex-col gap-1 flex-shrink-0">
{Array.from({ length: 7 }, (_, dayIndex) => {
const activityIndex = weekIndex * 7 + dayIndex;
const activity = activities()[activityIndex];
{/* Contribution grid - Responsive and prevents overflow */}
<div class="overflow-hidden w-full">
<div class="flex gap-1 min-w-0">
{/* Day labels */}
<div class="flex flex-col gap-1 pr-2 flex-shrink-0">
{['Mon', 'Wed', 'Fri'].map((day) => (
<div class="h-3 flex items-center justify-end">
<span class="text-xs text-foreground/70 hover:text-foreground transition-colors cursor-default font-medium">
{day}
</span>
</div>
))}
</div>
{/* Weekly columns - Responsive with proper overflow handling */}
<div class="flex gap-1 overflow-x-auto overflow-y-hidden min-w-0 pb-2">
{Array.from({ length: 53 }, (_, weekIndex) => (
<div class="flex flex-col gap-1 flex-shrink-0">
{Array.from({ length: 7 }, (_, dayIndex) => {
const activityIndex = weekIndex * 7 + dayIndex;
const activity = activities()[activityIndex];
if (!activity) {
return (
<div
class="w-2.5 h-2.5 sm:w-3 sm:h-3 rounded-sm flex-shrink-0 transition-all"
style={`background-color: ${getActivityColor(0)}`}
></div>
);
}
if (!activity) {
return (
<div
class="w-2.5 h-2.5 sm:w-3 sm:h-3 rounded-sm flex-shrink-0 transition-all"
style={`background-color: ${getActivityColor(0)}`}
class="w-2.5 h-2.5 sm:w-3 sm:h-3 rounded-sm hover:ring-1 hover:ring-primary cursor-pointer transition-all flex-shrink-0 hover:scale-110"
style={`background-color: ${getActivityColor(activity.level)}`}
title={`${activity.date}: ${activity.count} contributions`}
></div>
);
}
return (
<div
class="w-2.5 h-2.5 sm:w-3 sm:h-3 rounded-sm hover:ring-1 hover:ring-primary cursor-pointer transition-all flex-shrink-0 hover:scale-110"
style={`background-color: ${getActivityColor(activity.level)}`}
title={`${activity.date}: ${activity.count} contributions`}
></div>
);
})}
</div>
))}
})}
</div>
))}
</div>
</div>
</div>
</div>
{/* Legend */}
<div class="flex items-center justify-between mt-4">
<span class="text-xs text-muted-foreground">Less</span>
<div class="flex gap-1">
{[0, 1, 2, 3, 4].map((level) => (
<div
class="w-2.5 h-2.5 sm:w-3 sm:h-3 rounded-sm"
style={`background-color: ${getActivityColor(level)}`}
></div>
))}
{/* Legend */}
<div class="flex items-center justify-between mt-4">
<span class="text-xs text-muted-foreground">Less</span>
<div class="flex gap-1">
{[0, 1, 2, 3, 4].map((level) => (
<div
class="w-2.5 h-2.5 sm:w-3 sm:h-3 rounded-sm"
style={`background-color: ${getActivityColor(level)}`}
></div>
))}
</div>
<span class="text-xs text-muted-foreground">More</span>
</div>
<span class="text-xs text-muted-foreground">More</span>
</div>
</Show>
</Card>
</Show>
@@ -407,52 +295,56 @@ export const GitHubActivity = (props: GitHubActivityProps) => {
<span>Active</span>
</div>
</div>
<div class="space-y-3 max-h-64 overflow-y-auto">
<For each={recentEvents()}>
{(event) => (
<div class="flex items-center justify-between p-3 bg-card rounded-lg border hover:bg-muted/50 transition-colors">
<div class="flex items-center gap-3">
<div class="bg-primary/10 p-2 rounded-lg">
{getEventIcon(event.type)}
</div>
<div class="flex-1">
<p class="text-sm text-foreground font-medium">{event.title}</p>
<div class="flex items-center gap-2 text-xs text-muted-foreground mt-1">
<span>{event.date}</span>
{event.repo && (
<>
<span></span>
<span class="text-primary">{event.repo}</span>
</>
)}
{event.action && (
<>
<span></span>
<span>{event.action}</span>
</>
)}
<Show
when={recentEvents().length > 0}
fallback={<p class="text-sm text-muted-foreground">No GitHub events yet.</p>}
>
<div class="space-y-3 max-h-64 overflow-y-auto">
<For each={recentEvents()}>
{(event) => (
<div class="flex items-center justify-between p-3 bg-card rounded-lg border hover:bg-muted/50 transition-colors">
<div class="flex items-center gap-3">
<div class="bg-primary/10 p-2 rounded-lg">
{getEventIcon(event.type)}
</div>
<div class="flex-1">
<p class="text-sm text-foreground font-medium">{event.title}</p>
<div class="flex items-center gap-2 text-xs text-muted-foreground mt-1">
<span>{event.date}</span>
{event.repo && (
<>
<span></span>
<span class="text-primary">{event.repo}</span>
</>
)}
{event.action && (
<>
<span></span>
<span>{event.action}</span>
</>
)}
</div>
</div>
</div>
{event.link && (
<Button
variant="ghost"
size="sm"
onClick={() => {
if (event.link) {
window.location.href = event.link;
}
}}
class="hover:bg-primary/10 transition-colors"
>
<IconExternalLink class="size-4" />
</Button>
)}
</div>
{event.link && (
<Button
variant="ghost"
size="sm"
onClick={() => {
// Navigate to the link in the same tab
if (event.link) {
window.location.href = event.link;
}
}}
class="hover:bg-primary/10 transition-colors"
>
<IconExternalLink class="size-4" />
</Button>
)}
</div>
)}
</For>
</div>
)}
</For>
</div>
</Show>
</Card>
</Show>
</div>
@@ -1,6 +1,7 @@
import { createSignal } from 'solid-js';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { ModalPortal } from '@/components/ui/ModalPortal';
import { IconX } from '@tabler/icons-solidjs';
interface LearningPathFormData {
@@ -100,8 +101,9 @@ export const LearningPathModal = (props: LearningPathModalProps) => {
if (!props.isOpen) return null;
return (
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 mt-0">
<div class="bg-[#1a1a1a] rounded-lg w-full max-w-2xl max-h-[90vh] overflow-y-auto mx-4 my-4">
<ModalPortal>
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div class="bg-[#1a1a1a] rounded-lg w-full max-w-2xl max-h-[90vh] overflow-y-auto mx-4 my-4">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-[#404040]">
<h2 class="text-xl font-semibold text-[#fafafa]">
@@ -264,7 +266,8 @@ export const LearningPathModal = (props: LearningPathModalProps) => {
</Button>
</div>
</form>
</div>
</div>
</div>
</ModalPortal>
);
};
@@ -1,6 +1,7 @@
import { createSignal } from 'solid-js';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
import { ModalPortal } from '@/components/ui/ModalPortal';
import { IconX, IconClock, IconUsers, IconStar, IconBook, IconVideo, IconFileText, IconCode, IconCheck } from '@tabler/icons-solidjs';
interface LearningPath {
@@ -82,8 +83,9 @@ export const LearningPathPreviewModal = (props: LearningPathPreviewModalProps) =
if (!props.isOpen || !props.learningPath) return null;
return (
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 mt-0">
<div class="bg-[#1a1a1a] rounded-lg w-full max-w-4xl max-h-[90vh] overflow-y-auto mx-4 my-4">
<ModalPortal>
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div class="bg-[#1a1a1a] rounded-lg w-full max-w-4xl max-h-[90vh] overflow-y-auto mx-4 my-4">
{/* Header */}
<div class="relative">
{/* Thumbnail */}
@@ -241,7 +243,8 @@ export const LearningPathPreviewModal = (props: LearningPathPreviewModalProps) =
</div>
)}
</div>
</div>
</div>
</div>
</ModalPortal>
);
};
+68 -65
View File
@@ -1,6 +1,7 @@
import { createSignal } from 'solid-js';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { ModalPortal } from '@/components/ui/ModalPortal';
import { IconX } from '@tabler/icons-solidjs';
interface Member {
@@ -55,74 +56,76 @@ export const MemberModal = (props: MemberModalProps) => {
};
return (
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-40 mt-0" onClick={props.onClose} />
)}
<ModalPortal>
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-40" onClick={props.onClose} />
)}
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 500px; max-width: 90vw;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<h3 class="text-lg font-semibold">
{props.isEdit ? 'Edit Member' : 'Add New Member'}
</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
{/* Content */}
<div class="p-6 space-y-4">
<Input
type="text"
placeholder="Member name *"
value={memberData().name}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setMemberData(prev => ({ ...prev, name: target.value }));
}}
required
/>
<Input
type="email"
placeholder="Email address *"
value={memberData().email}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setMemberData(prev => ({ ...prev, email: target.value }));
}}
required
/>
<div class="space-y-2">
<label class="text-sm font-medium text-foreground">Role</label>
<select
value={memberData().role}
onChange={(e) => setMemberData(prev => ({ ...prev, role: e.target.value as 'Admin' | 'Member' }))}
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring"
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 500px; max-width: 90vw;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<h3 class="text-lg font-semibold">
{props.isEdit ? 'Edit Member' : 'Add New Member'}
</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<option value="Member">Member</option>
<option value="Admin">Admin</option>
</select>
<IconX class="size-4" />
</button>
</div>
{/* Content */}
<div class="p-6 space-y-4">
<Input
type="text"
placeholder="Member name *"
value={memberData().name}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setMemberData(prev => ({ ...prev, name: target.value }));
}}
required
/>
<Input
type="email"
placeholder="Email address *"
value={memberData().email}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setMemberData(prev => ({ ...prev, email: target.value }));
}}
required
/>
<div class="space-y-2">
<label class="text-sm font-medium text-foreground">Role</label>
<select
value={memberData().role}
onChange={(e) => setMemberData(prev => ({ ...prev, role: e.target.value as 'Admin' | 'Member' }))}
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring"
>
<option value="Member">Member</option>
<option value="Admin">Admin</option>
</select>
</div>
</div>
{/* Footer */}
<div class="flex justify-end gap-2 p-6 border-t border-border">
<Button variant="outline" onClick={props.onClose}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={!memberData().name.trim() || !memberData().email.trim()}>
{props.isEdit ? 'Save Changes' : 'Add Member'}
</Button>
</div>
</div>
{/* Footer */}
<div class="flex justify-end gap-2 p-6 border-t border-border">
<Button variant="outline" onClick={props.onClose}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={!memberData().name.trim() || !memberData().email.trim()}>
{props.isEdit ? 'Save Changes' : 'Add Member'}
</Button>
</div>
</div>
</>
</>
</ModalPortal>
);
};
@@ -0,0 +1,10 @@
import type { JSX } from 'solid-js'
import { Portal } from 'solid-js/web'
interface ModalPortalProps {
children: JSX.Element
}
export const ModalPortal = (props: ModalPortalProps) => {
return <Portal mount={document.body}>{props.children}</Portal>
}
+66 -63
View File
@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { RichTextEditor } from '@/components/ui/RichTextEditor';
import { TagPicker } from '@/components/ui/TagPicker';
import { ModalPortal } from '@/components/ui/ModalPortal';
import { IconX, IconTag } from '@tabler/icons-solidjs';
interface NoteModalProps {
@@ -34,74 +35,76 @@ export const NoteModal = (props: NoteModalProps) => {
};
return (
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-40 mt-0" onClick={props.onClose} />
)}
<ModalPortal>
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-40" onClick={props.onClose} />
)}
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 600px; max-width: 90vw; max-height: 80vh; overflow-y: auto;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<h3 class="text-lg font-semibold">
{props.note ? 'Edit Note' : 'Add New Note'}
</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
{/* Content */}
<div class="p-6 space-y-4">
<Input
type="text"
placeholder="Note title"
value={noteData().title}
onInput={(e: any) => setNoteData(prev => ({ ...prev, title: e.target.value }))}
required
/>
<div class="space-y-2">
<label class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<IconTag class="size-4" />
Content
</label>
<RichTextEditor
value={noteData().content}
onChange={(content) => setNoteData(prev => ({ ...prev, content }))}
placeholder="Write your note here..."
mode="markdown"
/>
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 600px; max-width: 90vw; max-height: 80vh; overflow-y: auto;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<h3 class="text-lg font-semibold">
{props.note ? 'Edit Note' : 'Add New Note'}
</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
<div class="space-y-2">
<label class="text-sm font-medium text-muted-foreground">Tags</label>
<TagPicker
availableTags={availableTags()}
selectedTags={noteData().tags}
onTagsChange={(tags) => setNoteData(prev => ({ ...prev, tags }))}
placeholder="Add tags..."
allowNew={true}
{/* Content */}
<div class="p-6 space-y-4">
<Input
type="text"
placeholder="Note title"
value={noteData().title}
onInput={(e: any) => setNoteData(prev => ({ ...prev, title: e.target.value }))}
required
/>
<div class="space-y-2">
<label class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<IconTag class="size-4" />
Content
</label>
<RichTextEditor
value={noteData().content}
onChange={(content) => setNoteData(prev => ({ ...prev, content }))}
placeholder="Write your note here..."
mode="markdown"
/>
</div>
<div class="space-y-2">
<label class="text-sm font-medium text-muted-foreground">Tags</label>
<TagPicker
availableTags={availableTags()}
selectedTags={noteData().tags}
onTagsChange={(tags) => setNoteData(prev => ({ ...prev, tags }))}
placeholder="Add tags..."
allowNew={true}
/>
</div>
</div>
{/* Footer */}
<div class="flex justify-end gap-2 p-6 border-t border-border">
<Button variant="outline" onClick={props.onClose}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={!noteData().title.trim()}>
{props.note ? 'Update Note' : 'Save Note'}
</Button>
</div>
</div>
{/* Footer */}
<div class="flex justify-end gap-2 p-6 border-t border-border">
<Button variant="outline" onClick={props.onClose}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={!noteData().title.trim()}>
{props.note ? 'Update Note' : 'Save Note'}
</Button>
</div>
</div>
</>
</>
</ModalPortal>
);
};
@@ -11,12 +11,13 @@ interface SearchTagFilterBarProps {
selectedTag: string;
onTagChange: (value: string) => void;
onReset: () => void;
allOptionLabel?: string;
}
export const SearchTagFilterBar = (props: SearchTagFilterBarProps) => {
return (
<div class="flex flex-col sm:flex-row gap-4 mb-6">
<div class="flex flex-1 gap-2">
<div class="mb-6 space-y-3">
<div class="grid grid-cols-1 sm:grid-cols-[17fr_3fr] gap-3 items-stretch sm:items-center">
<Input
type="text"
placeholder={props.searchPlaceholder}
@@ -25,14 +26,14 @@ export const SearchTagFilterBar = (props: SearchTagFilterBarProps) => {
const target = e.currentTarget as HTMLInputElement;
if (target) props.onSearchChange(target.value);
}}
class="flex-1"
class="w-full min-w-0"
/>
<select
value={props.selectedTag}
onChange={(e) => props.onTagChange(e.target.value)}
class="flex h-10 w-full sm:w-48 rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring"
class="flex h-10 w-full min-w-0 rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring"
>
<option value="">All Tags</option>
<option value="">{props.allOptionLabel || 'All Tags'}</option>
<For each={props.tagOptions}>
{(tag) => <option value={tag}>{tag}</option>}
</For>
+72 -69
View File
@@ -2,6 +2,7 @@ import { createSignal } from 'solid-js';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { DatePicker } from '@/components/ui/DatePicker';
import { ModalPortal } from '@/components/ui/ModalPortal';
import { IconX } from '@tabler/icons-solidjs';
interface Task {
@@ -78,79 +79,81 @@ export const TaskModal = (props: TaskModalProps) => {
};
return (
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-40 mt-0" onClick={props.onClose} />
)}
<ModalPortal>
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-[60]" onClick={props.onClose} />
)}
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-50 ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: 500px; max-width: 90vw;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<h3 class="text-lg font-semibold">
{props.isEdit ? 'Edit Task' : 'Add New Task'}
</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
{/* Content */}
<div class="p-6 space-y-4">
<Input
type="text"
placeholder="Task title *"
value={taskData().title}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setTaskData(prev => ({ ...prev, title: target.value }));
}}
required
/>
<Input
type="text"
placeholder="Description (optional)"
value={taskData().description}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setTaskData(prev => ({ ...prev, description: target.value }));
}}
/>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<select
value={taskData().priority}
onChange={(e) => setTaskData(prev => ({ ...prev, priority: e.target.value as any }))}
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring"
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-[70] w-full max-w-lg mx-4 ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`}>
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<h3 class="text-lg font-semibold">
{props.isEdit ? 'Edit Task' : 'Add New Task'}
</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<option value="low">Low Priority</option>
<option value="medium">Medium Priority</option>
<option value="high">High Priority</option>
</select>
<DatePicker
value={dueDate()}
onChange={(date) => setDueDate(date || undefined)}
placeholder="Due date (optional)"
class="w-full"
<IconX class="size-4" />
</button>
</div>
{/* Content */}
<div class="p-6 space-y-4">
<Input
type="text"
placeholder="Task title *"
value={taskData().title}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setTaskData(prev => ({ ...prev, title: target.value }));
}}
required
/>
<Input
type="text"
placeholder="Description (optional)"
value={taskData().description}
onInput={(e) => {
const target = e.currentTarget as HTMLInputElement;
if (target) setTaskData(prev => ({ ...prev, description: target.value }));
}}
/>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<select
value={taskData().priority}
onChange={(e) => setTaskData(prev => ({ ...prev, priority: e.target.value as any }))}
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring"
>
<option value="low">Low Priority</option>
<option value="medium">Medium Priority</option>
<option value="high">High Priority</option>
</select>
<DatePicker
value={dueDate()}
onChange={(date) => setDueDate(date || undefined)}
placeholder="Due date (optional)"
class="w-full"
/>
</div>
</div>
{/* Footer */}
<div class="flex justify-end gap-2 p-6 border-t border-border">
<Button variant="outline" onClick={props.onClose}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={!taskData().title.trim()}>
{props.isEdit ? 'Save Changes' : 'Add Task'}
</Button>
</div>
</div>
{/* Footer */}
<div class="flex justify-end gap-2 p-6 border-t border-border">
<Button variant="outline" onClick={props.onClose}>
Cancel
</Button>
<Button onClick={handleSubmit} disabled={!taskData().title.trim()}>
{props.isEdit ? 'Save Changes' : 'Add Task'}
</Button>
</div>
</div>
</>
</>
</ModalPortal>
);
};
+7 -4
View File
@@ -7,6 +7,7 @@ import {
IconLoader2
} from '@tabler/icons-solidjs';
import { updateStore } from '../../stores/updateStore';
import { ModalPortal } from './ModalPortal';
interface UpdateCheckerProps {
class?: string;
@@ -115,9 +116,10 @@ export function UpdateChecker(props: UpdateCheckerProps) {
{/* Update Modal */}
<Show when={showUpdateModal() && updateStore.updateInfo()}>
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4 mt-0">
<div class="bg-card border border-border rounded-lg shadow-lg max-w-md w-full max-h-[80vh] overflow-auto">
<div class="p-6">
<ModalPortal>
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div class="bg-card border border-border rounded-lg shadow-lg max-w-md w-full max-h-[80vh] overflow-auto">
<div class="p-6">
<div class="flex items-center gap-3 mb-4">
<IconDownload class="size-6 text-blue-500" />
<h2 class="text-lg font-semibold">Update Available</h2>
@@ -243,9 +245,10 @@ export function UpdateChecker(props: UpdateCheckerProps) {
</button>
</Show>
</div>
</div>
</div>
</div>
</div>
</ModalPortal>
</Show>
</>
);
+30 -28
View File
@@ -1,6 +1,11 @@
import { createSignal } from 'solid-js';
import { Button } from '@/components/ui/Button';
import { ModalPortal } from '@/components/ui/ModalPortal';
import { IconX, IconUpload } from '@tabler/icons-solidjs';
import { isDemoMode } from '@/lib/demo-mode';
import { getApiV1BaseUrl } from '@/lib/api-url';
const API_BASE_URL = getApiV1BaseUrl();
interface UploadModalProps {
isOpen: boolean;
@@ -39,13 +44,8 @@ export const UploadModal = (props: UploadModalProps) => {
const files = uploadedFiles();
if (files.length === 0) return;
// Check if we're in demo mode
const isDemoMode = localStorage.getItem('demoMode') === 'true' ||
document.title.includes('Demo Mode') ||
window.location.search.includes('demo=true');
try {
if (isDemoMode) {
if (isDemoMode()) {
// Simulate upload in demo mode
console.log('Demo mode: Simulating upload for files:', files.map(f => f.name));
// Simulate upload delay
@@ -62,7 +62,7 @@ export const UploadModal = (props: UploadModalProps) => {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8080'}/api/v1/files/upload`, {
const response = await fetch(`${API_BASE_URL}/files/upload`, {
method: 'POST',
body: formData
});
@@ -86,26 +86,27 @@ export const UploadModal = (props: UploadModalProps) => {
};
return (
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-[60] mt-0" onClick={props.onClose} />
)}
<ModalPortal>
<>
{/* Backdrop */}
{props.isOpen && (
<div class="fixed inset-0 bg-black/50 z-[60]" onClick={props.onClose} />
)}
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-[70] ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: min(600px, 90vw); max-height: min(80vh, 600px); overflow-y: auto;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<h3 class="text-lg font-semibold">Import Documents</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
{/* Modal */}
<div class={`fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-card border border-border rounded-lg shadow-xl transition-all duration-300 z-[70] ${
props.isOpen ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'
}`} style="width: min(600px, 90vw); max-height: min(80vh, 600px); overflow-y: auto;">
{/* Header */}
<div class="flex items-center justify-between p-6 border-b border-border">
<h3 class="text-lg font-semibold">Import Documents</h3>
<button
onClick={props.onClose}
class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-shadow focus-visible:outline-none focus-visible:ring-1.5 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-inherit hover:bg-accent/50 hover:text-accent-foreground h-8 w-8"
>
<IconX class="size-4" />
</button>
</div>
{/* Content */}
<div class="p-4 sm:p-6 space-y-4">
@@ -175,7 +176,8 @@ export const UploadModal = (props: UploadModalProps) => {
Upload {uploadedFiles().length} {uploadedFiles().length === 1 ? 'File' : 'Files'}
</Button>
</div>
</div>
</>
</div>
</>
</ModalPortal>
);
};

Some files were not shown because too many files have changed in this diff Show More