file_browser_templ.go 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. // Code generated by templ - DO NOT EDIT.
  2. // templ: version: v0.3.906
  3. package app
  4. //lint:file-ignore SA4006 This context is only used if a nested component is present.
  5. import "github.com/a-h/templ"
  6. import templruntime "github.com/a-h/templ/runtime"
  7. import (
  8. "fmt"
  9. "github.com/seaweedfs/seaweedfs/weed/admin/dash"
  10. "path/filepath"
  11. "strings"
  12. )
  13. func FileBrowser(data dash.FileBrowserData) templ.Component {
  14. return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
  15. templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
  16. if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
  17. return templ_7745c5c3_CtxErr
  18. }
  19. templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
  20. if !templ_7745c5c3_IsBuffer {
  21. defer func() {
  22. templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
  23. if templ_7745c5c3_Err == nil {
  24. templ_7745c5c3_Err = templ_7745c5c3_BufErr
  25. }
  26. }()
  27. }
  28. ctx = templ.InitializeContext(ctx)
  29. templ_7745c5c3_Var1 := templ.GetChildren(ctx)
  30. if templ_7745c5c3_Var1 == nil {
  31. templ_7745c5c3_Var1 = templ.NopComponent
  32. }
  33. ctx = templ.ClearChildren(ctx)
  34. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom\"><h1 class=\"h2\">")
  35. if templ_7745c5c3_Err != nil {
  36. return templ_7745c5c3_Err
  37. }
  38. if data.IsBucketPath && data.BucketName != "" {
  39. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<i class=\"fas fa-cube me-2\"></i>S3 Bucket: ")
  40. if templ_7745c5c3_Err != nil {
  41. return templ_7745c5c3_Err
  42. }
  43. var templ_7745c5c3_Var2 string
  44. templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.BucketName)
  45. if templ_7745c5c3_Err != nil {
  46. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 14, Col: 63}
  47. }
  48. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
  49. if templ_7745c5c3_Err != nil {
  50. return templ_7745c5c3_Err
  51. }
  52. } else {
  53. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<i class=\"fas fa-folder-open me-2\"></i>File Browser")
  54. if templ_7745c5c3_Err != nil {
  55. return templ_7745c5c3_Err
  56. }
  57. }
  58. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</h1><div class=\"btn-toolbar mb-2 mb-md-0\"><div class=\"btn-group me-2\">")
  59. if templ_7745c5c3_Err != nil {
  60. return templ_7745c5c3_Err
  61. }
  62. if data.IsBucketPath && data.BucketName != "" {
  63. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<a href=\"/object-store/buckets\" class=\"btn btn-sm btn-outline-secondary\"><i class=\"fas fa-arrow-left me-1\"></i>Back to Buckets</a> ")
  64. if templ_7745c5c3_Err != nil {
  65. return templ_7745c5c3_Err
  66. }
  67. }
  68. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<button type=\"button\" class=\"btn btn-sm btn-outline-primary\" onclick=\"createFolder()\"><i class=\"fas fa-folder-plus me-1\"></i>New Folder</button> <button type=\"button\" class=\"btn btn-sm btn-outline-secondary\" onclick=\"uploadFile()\"><i class=\"fas fa-upload me-1\"></i>Upload</button> <button type=\"button\" class=\"btn btn-sm btn-outline-danger\" id=\"deleteSelectedBtn\" onclick=\"confirmDeleteSelected()\" style=\"display: none;\"><i class=\"fas fa-trash me-1\"></i>Delete Selected</button> <button type=\"button\" class=\"btn btn-sm btn-outline-info\" onclick=\"exportFileList()\"><i class=\"fas fa-download me-1\"></i>Export</button></div></div></div><!-- Breadcrumb Navigation --><nav aria-label=\"breadcrumb\" class=\"mb-3\"><ol class=\"breadcrumb\">")
  69. if templ_7745c5c3_Err != nil {
  70. return templ_7745c5c3_Err
  71. }
  72. for i, crumb := range data.Breadcrumbs {
  73. if i == len(data.Breadcrumbs)-1 {
  74. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<li class=\"breadcrumb-item active\" aria-current=\"page\"><i class=\"fas fa-folder me-1\"></i>")
  75. if templ_7745c5c3_Err != nil {
  76. return templ_7745c5c3_Err
  77. }
  78. var templ_7745c5c3_Var3 string
  79. templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(crumb.Name)
  80. if templ_7745c5c3_Err != nil {
  81. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 48, Col: 52}
  82. }
  83. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
  84. if templ_7745c5c3_Err != nil {
  85. return templ_7745c5c3_Err
  86. }
  87. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</li>")
  88. if templ_7745c5c3_Err != nil {
  89. return templ_7745c5c3_Err
  90. }
  91. } else {
  92. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<li class=\"breadcrumb-item\"><a href=\"")
  93. if templ_7745c5c3_Err != nil {
  94. return templ_7745c5c3_Err
  95. }
  96. var templ_7745c5c3_Var4 templ.SafeURL
  97. templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/files?path=%s", crumb.Path)))
  98. if templ_7745c5c3_Err != nil {
  99. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 52, Col: 72}
  100. }
  101. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
  102. if templ_7745c5c3_Err != nil {
  103. return templ_7745c5c3_Err
  104. }
  105. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\" class=\"text-decoration-none\">")
  106. if templ_7745c5c3_Err != nil {
  107. return templ_7745c5c3_Err
  108. }
  109. if crumb.Name == "Root" {
  110. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<i class=\"fas fa-home me-1\"></i> ")
  111. if templ_7745c5c3_Err != nil {
  112. return templ_7745c5c3_Err
  113. }
  114. } else {
  115. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<i class=\"fas fa-folder me-1\"></i> ")
  116. if templ_7745c5c3_Err != nil {
  117. return templ_7745c5c3_Err
  118. }
  119. }
  120. var templ_7745c5c3_Var5 string
  121. templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(crumb.Name)
  122. if templ_7745c5c3_Err != nil {
  123. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 58, Col: 19}
  124. }
  125. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
  126. if templ_7745c5c3_Err != nil {
  127. return templ_7745c5c3_Err
  128. }
  129. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</a></li>")
  130. if templ_7745c5c3_Err != nil {
  131. return templ_7745c5c3_Err
  132. }
  133. }
  134. }
  135. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</ol></nav><!-- Summary Cards --><div class=\"row mb-4\"><div class=\"col-xl-3 col-md-6 mb-4\"><div class=\"card border-left-primary shadow h-100 py-2\"><div class=\"card-body\"><div class=\"row no-gutters align-items-center\"><div class=\"col mr-2\"><div class=\"text-xs font-weight-bold text-primary text-uppercase mb-1\">Total Entries</div><div class=\"h5 mb-0 font-weight-bold text-gray-800\">")
  136. if templ_7745c5c3_Err != nil {
  137. return templ_7745c5c3_Err
  138. }
  139. var templ_7745c5c3_Var6 string
  140. templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.TotalEntries))
  141. if templ_7745c5c3_Err != nil {
  142. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 77, Col: 46}
  143. }
  144. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
  145. if templ_7745c5c3_Err != nil {
  146. return templ_7745c5c3_Err
  147. }
  148. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div></div><div class=\"col-auto\"><i class=\"fas fa-list fa-2x text-gray-300\"></i></div></div></div></div></div><div class=\"col-xl-3 col-md-6 mb-4\"><div class=\"card border-left-success shadow h-100 py-2\"><div class=\"card-body\"><div class=\"row no-gutters align-items-center\"><div class=\"col mr-2\"><div class=\"text-xs font-weight-bold text-success text-uppercase mb-1\">Directories</div><div class=\"h5 mb-0 font-weight-bold text-gray-800\">")
  149. if templ_7745c5c3_Err != nil {
  150. return templ_7745c5c3_Err
  151. }
  152. var templ_7745c5c3_Var7 string
  153. templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", countDirectories(data.Entries)))
  154. if templ_7745c5c3_Err != nil {
  155. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 97, Col: 59}
  156. }
  157. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
  158. if templ_7745c5c3_Err != nil {
  159. return templ_7745c5c3_Err
  160. }
  161. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div></div><div class=\"col-auto\"><i class=\"fas fa-folder fa-2x text-gray-300\"></i></div></div></div></div></div><div class=\"col-xl-3 col-md-6 mb-4\"><div class=\"card border-left-info shadow h-100 py-2\"><div class=\"card-body\"><div class=\"row no-gutters align-items-center\"><div class=\"col mr-2\"><div class=\"text-xs font-weight-bold text-info text-uppercase mb-1\">Files</div><div class=\"h5 mb-0 font-weight-bold text-gray-800\">")
  162. if templ_7745c5c3_Err != nil {
  163. return templ_7745c5c3_Err
  164. }
  165. var templ_7745c5c3_Var8 string
  166. templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", countFiles(data.Entries)))
  167. if templ_7745c5c3_Err != nil {
  168. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 117, Col: 53}
  169. }
  170. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
  171. if templ_7745c5c3_Err != nil {
  172. return templ_7745c5c3_Err
  173. }
  174. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</div></div><div class=\"col-auto\"><i class=\"fas fa-file fa-2x text-gray-300\"></i></div></div></div></div></div><div class=\"col-xl-3 col-md-6 mb-4\"><div class=\"card border-left-warning shadow h-100 py-2\"><div class=\"card-body\"><div class=\"row no-gutters align-items-center\"><div class=\"col mr-2\"><div class=\"text-xs font-weight-bold text-warning text-uppercase mb-1\">Total Size</div><div class=\"h5 mb-0 font-weight-bold text-gray-800\">")
  175. if templ_7745c5c3_Err != nil {
  176. return templ_7745c5c3_Err
  177. }
  178. var templ_7745c5c3_Var9 string
  179. templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(formatBytes(data.TotalSize))
  180. if templ_7745c5c3_Err != nil {
  181. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 137, Col: 37}
  182. }
  183. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
  184. if templ_7745c5c3_Err != nil {
  185. return templ_7745c5c3_Err
  186. }
  187. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</div></div><div class=\"col-auto\"><i class=\"fas fa-hdd fa-2x text-gray-300\"></i></div></div></div></div></div></div><!-- File Listing --><div class=\"card shadow mb-4\"><div class=\"card-header py-3 d-flex justify-content-between align-items-center\"><h6 class=\"m-0 font-weight-bold text-primary\"><i class=\"fas fa-folder-open me-2\"></i> ")
  188. if templ_7745c5c3_Err != nil {
  189. return templ_7745c5c3_Err
  190. }
  191. if data.CurrentPath == "/" {
  192. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "Root Directory")
  193. if templ_7745c5c3_Err != nil {
  194. return templ_7745c5c3_Err
  195. }
  196. } else if data.CurrentPath == "/buckets" {
  197. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "Object Store Buckets Directory <a href=\"/object-store/buckets\" class=\"btn btn-sm btn-outline-primary ms-2\"><i class=\"fas fa-cube me-1\"></i>Manage Buckets</a>")
  198. if templ_7745c5c3_Err != nil {
  199. return templ_7745c5c3_Err
  200. }
  201. } else {
  202. var templ_7745c5c3_Var10 string
  203. templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(filepath.Base(data.CurrentPath))
  204. if templ_7745c5c3_Err != nil {
  205. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 162, Col: 37}
  206. }
  207. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
  208. if templ_7745c5c3_Err != nil {
  209. return templ_7745c5c3_Err
  210. }
  211. }
  212. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</h6>")
  213. if templ_7745c5c3_Err != nil {
  214. return templ_7745c5c3_Err
  215. }
  216. if data.ParentPath != data.CurrentPath {
  217. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<a href=\"")
  218. if templ_7745c5c3_Err != nil {
  219. return templ_7745c5c3_Err
  220. }
  221. var templ_7745c5c3_Var11 templ.SafeURL
  222. templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/files?path=%s", data.ParentPath)))
  223. if templ_7745c5c3_Err != nil {
  224. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 166, Col: 75}
  225. }
  226. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
  227. if templ_7745c5c3_Err != nil {
  228. return templ_7745c5c3_Err
  229. }
  230. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" class=\"btn btn-sm btn-outline-secondary\"><i class=\"fas fa-arrow-up me-1\"></i>Up</a>")
  231. if templ_7745c5c3_Err != nil {
  232. return templ_7745c5c3_Err
  233. }
  234. }
  235. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</div><div class=\"card-body\">")
  236. if templ_7745c5c3_Err != nil {
  237. return templ_7745c5c3_Err
  238. }
  239. if len(data.Entries) > 0 {
  240. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div class=\"table-responsive\"><table class=\"table table-hover\" id=\"fileTable\"><thead><tr><th width=\"40px\"><input type=\"checkbox\" id=\"selectAll\" onchange=\"toggleSelectAll()\"></th><th>Name</th><th>Size</th><th>Type</th><th>Modified</th><th>Permissions</th><th>Actions</th></tr></thead> <tbody>")
  241. if templ_7745c5c3_Err != nil {
  242. return templ_7745c5c3_Err
  243. }
  244. for _, entry := range data.Entries {
  245. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<tr><td><input type=\"checkbox\" class=\"file-checkbox\" value=\"")
  246. if templ_7745c5c3_Err != nil {
  247. return templ_7745c5c3_Err
  248. }
  249. var templ_7745c5c3_Var12 string
  250. templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(entry.FullPath)
  251. if templ_7745c5c3_Err != nil {
  252. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 192, Col: 77}
  253. }
  254. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
  255. if templ_7745c5c3_Err != nil {
  256. return templ_7745c5c3_Err
  257. }
  258. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\"></td><td><div class=\"d-flex align-items-center\">")
  259. if templ_7745c5c3_Err != nil {
  260. return templ_7745c5c3_Err
  261. }
  262. if entry.IsDirectory {
  263. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<i class=\"fas fa-folder text-warning me-2\"></i> <a href=\"")
  264. if templ_7745c5c3_Err != nil {
  265. return templ_7745c5c3_Err
  266. }
  267. var templ_7745c5c3_Var13 templ.SafeURL
  268. templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/files?path=%s", entry.FullPath)))
  269. if templ_7745c5c3_Err != nil {
  270. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 198, Col: 82}
  271. }
  272. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
  273. if templ_7745c5c3_Err != nil {
  274. return templ_7745c5c3_Err
  275. }
  276. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" class=\"text-decoration-none\">")
  277. if templ_7745c5c3_Err != nil {
  278. return templ_7745c5c3_Err
  279. }
  280. var templ_7745c5c3_Var14 string
  281. templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Name)
  282. if templ_7745c5c3_Err != nil {
  283. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 199, Col: 25}
  284. }
  285. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
  286. if templ_7745c5c3_Err != nil {
  287. return templ_7745c5c3_Err
  288. }
  289. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</a>")
  290. if templ_7745c5c3_Err != nil {
  291. return templ_7745c5c3_Err
  292. }
  293. } else {
  294. var templ_7745c5c3_Var15 = []any{fmt.Sprintf("fas %s text-muted me-2", getFileIcon(entry.Mime))}
  295. templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var15...)
  296. if templ_7745c5c3_Err != nil {
  297. return templ_7745c5c3_Err
  298. }
  299. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<i class=\"")
  300. if templ_7745c5c3_Err != nil {
  301. return templ_7745c5c3_Err
  302. }
  303. var templ_7745c5c3_Var16 string
  304. templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var15).String())
  305. if templ_7745c5c3_Err != nil {
  306. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 1, Col: 0}
  307. }
  308. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
  309. if templ_7745c5c3_Err != nil {
  310. return templ_7745c5c3_Err
  311. }
  312. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\"></i> <span>")
  313. if templ_7745c5c3_Err != nil {
  314. return templ_7745c5c3_Err
  315. }
  316. var templ_7745c5c3_Var17 string
  317. templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Name)
  318. if templ_7745c5c3_Err != nil {
  319. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 203, Col: 30}
  320. }
  321. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
  322. if templ_7745c5c3_Err != nil {
  323. return templ_7745c5c3_Err
  324. }
  325. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</span>")
  326. if templ_7745c5c3_Err != nil {
  327. return templ_7745c5c3_Err
  328. }
  329. }
  330. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</div></td><td>")
  331. if templ_7745c5c3_Err != nil {
  332. return templ_7745c5c3_Err
  333. }
  334. if entry.IsDirectory {
  335. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<span class=\"text-muted\">—</span>")
  336. if templ_7745c5c3_Err != nil {
  337. return templ_7745c5c3_Err
  338. }
  339. } else {
  340. var templ_7745c5c3_Var18 string
  341. templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(formatBytes(entry.Size))
  342. if templ_7745c5c3_Err != nil {
  343. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 211, Col: 36}
  344. }
  345. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
  346. if templ_7745c5c3_Err != nil {
  347. return templ_7745c5c3_Err
  348. }
  349. }
  350. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</td><td><span class=\"badge bg-light text-dark\">")
  351. if templ_7745c5c3_Err != nil {
  352. return templ_7745c5c3_Err
  353. }
  354. if entry.IsDirectory {
  355. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "Directory")
  356. if templ_7745c5c3_Err != nil {
  357. return templ_7745c5c3_Err
  358. }
  359. } else {
  360. var templ_7745c5c3_Var19 string
  361. templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(getMimeDisplayName(entry.Mime))
  362. if templ_7745c5c3_Err != nil {
  363. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 219, Col: 44}
  364. }
  365. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
  366. if templ_7745c5c3_Err != nil {
  367. return templ_7745c5c3_Err
  368. }
  369. }
  370. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</span></td><td>")
  371. if templ_7745c5c3_Err != nil {
  372. return templ_7745c5c3_Err
  373. }
  374. if !entry.ModTime.IsZero() {
  375. var templ_7745c5c3_Var20 string
  376. templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(entry.ModTime.Format("2006-01-02 15:04"))
  377. if templ_7745c5c3_Err != nil {
  378. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 225, Col: 53}
  379. }
  380. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
  381. if templ_7745c5c3_Err != nil {
  382. return templ_7745c5c3_Err
  383. }
  384. } else {
  385. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<span class=\"text-muted\">—</span>")
  386. if templ_7745c5c3_Err != nil {
  387. return templ_7745c5c3_Err
  388. }
  389. }
  390. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</td><td><code class=\"small permissions-display\" data-mode=\"")
  391. if templ_7745c5c3_Err != nil {
  392. return templ_7745c5c3_Err
  393. }
  394. var templ_7745c5c3_Var21 string
  395. templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Mode)
  396. if templ_7745c5c3_Err != nil {
  397. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 231, Col: 72}
  398. }
  399. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
  400. if templ_7745c5c3_Err != nil {
  401. return templ_7745c5c3_Err
  402. }
  403. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\" data-is-directory=\"")
  404. if templ_7745c5c3_Err != nil {
  405. return templ_7745c5c3_Err
  406. }
  407. var templ_7745c5c3_Var22 string
  408. templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%t", entry.IsDirectory))
  409. if templ_7745c5c3_Err != nil {
  410. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 231, Col: 131}
  411. }
  412. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
  413. if templ_7745c5c3_Err != nil {
  414. return templ_7745c5c3_Err
  415. }
  416. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\">")
  417. if templ_7745c5c3_Err != nil {
  418. return templ_7745c5c3_Err
  419. }
  420. var templ_7745c5c3_Var23 string
  421. templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Mode)
  422. if templ_7745c5c3_Err != nil {
  423. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 231, Col: 146}
  424. }
  425. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
  426. if templ_7745c5c3_Err != nil {
  427. return templ_7745c5c3_Err
  428. }
  429. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</code></td><td><div class=\"btn-group btn-group-sm\" role=\"group\">")
  430. if templ_7745c5c3_Err != nil {
  431. return templ_7745c5c3_Err
  432. }
  433. if !entry.IsDirectory {
  434. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<button type=\"button\" class=\"btn btn-outline-primary btn-sm\" title=\"Download\" data-action=\"download\" data-path=\"")
  435. if templ_7745c5c3_Err != nil {
  436. return templ_7745c5c3_Err
  437. }
  438. var templ_7745c5c3_Var24 string
  439. templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(entry.FullPath)
  440. if templ_7745c5c3_Err != nil {
  441. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 236, Col: 139}
  442. }
  443. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
  444. if templ_7745c5c3_Err != nil {
  445. return templ_7745c5c3_Err
  446. }
  447. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\"><i class=\"fas fa-download\"></i></button> <button type=\"button\" class=\"btn btn-outline-info btn-sm\" title=\"View\" data-action=\"view\" data-path=\"")
  448. if templ_7745c5c3_Err != nil {
  449. return templ_7745c5c3_Err
  450. }
  451. var templ_7745c5c3_Var25 string
  452. templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(entry.FullPath)
  453. if templ_7745c5c3_Err != nil {
  454. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 239, Col: 128}
  455. }
  456. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
  457. if templ_7745c5c3_Err != nil {
  458. return templ_7745c5c3_Err
  459. }
  460. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\"><i class=\"fas fa-eye\"></i></button> ")
  461. if templ_7745c5c3_Err != nil {
  462. return templ_7745c5c3_Err
  463. }
  464. }
  465. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<button type=\"button\" class=\"btn btn-outline-secondary btn-sm\" title=\"Properties\" data-action=\"properties\" data-path=\"")
  466. if templ_7745c5c3_Err != nil {
  467. return templ_7745c5c3_Err
  468. }
  469. var templ_7745c5c3_Var26 string
  470. templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(entry.FullPath)
  471. if templ_7745c5c3_Err != nil {
  472. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 243, Col: 144}
  473. }
  474. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
  475. if templ_7745c5c3_Err != nil {
  476. return templ_7745c5c3_Err
  477. }
  478. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "\"><i class=\"fas fa-info-circle\"></i></button> <button type=\"button\" class=\"btn btn-outline-danger btn-sm\" title=\"Delete\" data-action=\"delete\" data-path=\"")
  479. if templ_7745c5c3_Err != nil {
  480. return templ_7745c5c3_Err
  481. }
  482. var templ_7745c5c3_Var27 string
  483. templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(entry.FullPath)
  484. if templ_7745c5c3_Err != nil {
  485. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 246, Col: 133}
  486. }
  487. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
  488. if templ_7745c5c3_Err != nil {
  489. return templ_7745c5c3_Err
  490. }
  491. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "\"><i class=\"fas fa-trash\"></i></button></div></td></tr>")
  492. if templ_7745c5c3_Err != nil {
  493. return templ_7745c5c3_Err
  494. }
  495. }
  496. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</tbody></table></div>")
  497. if templ_7745c5c3_Err != nil {
  498. return templ_7745c5c3_Err
  499. }
  500. } else {
  501. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<div class=\"text-center py-5\"><i class=\"fas fa-folder-open fa-3x text-muted mb-3\"></i><h5 class=\"text-muted\">Empty Directory</h5><p class=\"text-muted\">This directory contains no files or subdirectories.</p></div>")
  502. if templ_7745c5c3_Err != nil {
  503. return templ_7745c5c3_Err
  504. }
  505. }
  506. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</div></div><!-- Last Updated --><div class=\"row\"><div class=\"col-12\"><small class=\"text-muted\"><i class=\"fas fa-clock me-1\"></i> Last updated: ")
  507. if templ_7745c5c3_Err != nil {
  508. return templ_7745c5c3_Err
  509. }
  510. var templ_7745c5c3_Var28 string
  511. templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(data.LastUpdated.Format("2006-01-02 15:04:05"))
  512. if templ_7745c5c3_Err != nil {
  513. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 271, Col: 66}
  514. }
  515. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
  516. if templ_7745c5c3_Err != nil {
  517. return templ_7745c5c3_Err
  518. }
  519. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</small></div></div><!-- Create Folder Modal --><div class=\"modal fade\" id=\"createFolderModal\" tabindex=\"-1\" aria-labelledby=\"createFolderModalLabel\" aria-hidden=\"true\"><div class=\"modal-dialog\"><div class=\"modal-content\"><div class=\"modal-header\"><h5 class=\"modal-title\" id=\"createFolderModalLabel\"><i class=\"fas fa-folder-plus me-2\"></i>Create New Folder</h5><button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button></div><div class=\"modal-body\"><form id=\"createFolderForm\"><div class=\"mb-3\"><label for=\"folderName\" class=\"form-label\">Folder Name</label> <input type=\"text\" class=\"form-control\" id=\"folderName\" name=\"folderName\" required placeholder=\"Enter folder name\" maxlength=\"255\"><div class=\"form-text\">Folder names cannot contain / or \\ characters.</div></div><input type=\"hidden\" id=\"currentPath\" name=\"currentPath\" value=\"")
  520. if templ_7745c5c3_Err != nil {
  521. return templ_7745c5c3_Err
  522. }
  523. var templ_7745c5c3_Var29 string
  524. templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(data.CurrentPath)
  525. if templ_7745c5c3_Err != nil {
  526. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 296, Col: 87}
  527. }
  528. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
  529. if templ_7745c5c3_Err != nil {
  530. return templ_7745c5c3_Err
  531. }
  532. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\"></form></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button> <button type=\"button\" class=\"btn btn-primary\" onclick=\"submitCreateFolder()\"><i class=\"fas fa-folder-plus me-1\"></i>Create Folder</button></div></div></div></div><!-- Upload File Modal --><div class=\"modal fade\" id=\"uploadFileModal\" tabindex=\"-1\" aria-labelledby=\"uploadFileModalLabel\" aria-hidden=\"true\"><div class=\"modal-dialog modal-lg\"><div class=\"modal-content\"><div class=\"modal-header\"><h5 class=\"modal-title\" id=\"uploadFileModalLabel\"><i class=\"fas fa-upload me-2\"></i>Upload Files</h5><button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button></div><div class=\"modal-body\"><form id=\"uploadFileForm\" enctype=\"multipart/form-data\"><div class=\"mb-3\"><label for=\"fileInput\" class=\"form-label\">Select Files</label> <input type=\"file\" class=\"form-control\" id=\"fileInput\" name=\"files\" multiple required><div class=\"form-text\">Choose one or more files to upload to the current directory. You can select multiple files by holding Ctrl (Cmd on Mac) while clicking.</div></div><input type=\"hidden\" id=\"uploadPath\" name=\"path\" value=\"")
  533. if templ_7745c5c3_Err != nil {
  534. return templ_7745c5c3_Err
  535. }
  536. var templ_7745c5c3_Var30 string
  537. templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(data.CurrentPath)
  538. if templ_7745c5c3_Err != nil {
  539. return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 328, Col: 79}
  540. }
  541. _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
  542. if templ_7745c5c3_Err != nil {
  543. return templ_7745c5c3_Err
  544. }
  545. templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\"><!-- File List Preview --><div id=\"fileListPreview\" class=\"mb-3\" style=\"display: none;\"><label class=\"form-label\">Selected Files:</label><div id=\"selectedFilesList\" class=\"border rounded p-2 bg-light\"><!-- Files will be listed here --></div></div><!-- Upload Progress --><div class=\"mb-3\" id=\"uploadProgress\" style=\"display: none;\"><label class=\"form-label\">Upload Progress:</label><div class=\"progress mb-2\"><div class=\"progress-bar progress-bar-striped progress-bar-animated\" role=\"progressbar\" style=\"width: 0%\" aria-valuenow=\"0\" aria-valuemin=\"0\" aria-valuemax=\"100\">0%</div></div><div id=\"uploadStatus\" class=\"small text-muted\">Preparing upload...</div></div></form></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button> <button type=\"button\" class=\"btn btn-primary\" onclick=\"submitUploadFile()\"><i class=\"fas fa-upload me-1\"></i>Upload Files</button></div></div></div></div><!-- JavaScript for file browser functionality --><script>\n\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t// Format permissions in the main table\n\t\tdocument.querySelectorAll('.permissions-display').forEach(element => {\n\t\t\tconst mode = element.getAttribute('data-mode');\n\t\t\tconst isDirectory = element.getAttribute('data-is-directory') === 'true';\n\t\t\tif (mode) {\n\t\t\t\telement.textContent = formatPermissions(mode, isDirectory);\n\t\t\t}\n\t\t});\n\t\t\n\t\t// Handle file browser action buttons (download, view, properties, delete)\n\t\tdocument.addEventListener('click', function(e) {\n\t\t\tconst button = e.target.closest('[data-action]');\n\t\t\tif (!button) return;\n\t\t\t\n\t\t\tconst action = button.getAttribute('data-action');\n\t\t\tconst path = button.getAttribute('data-path');\n\t\t\t\n\t\t\tif (!path) return;\n\t\t\t\n\t\t\tswitch(action) {\n\t\t\t\tcase 'download':\n\t\t\t\t\tdownloadFile(path);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'view':\n\t\t\t\t\tviewFile(path);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'properties':\n\t\t\t\t\tshowFileProperties(path);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'delete':\n\t\t\t\t\tif (confirm('Are you sure you want to delete \"' + path + '\"?')) {\n\t\t\t\t\t\tdeleteFile(path);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t\t\n\t\t// Initialize file manager event handlers from admin.js\n\t\tif (typeof setupFileManagerEventHandlers === 'function') {\n\t\t\tsetupFileManagerEventHandlers();\n\t\t}\n\t});\n\t\n\t// File browser specific functions\n\tfunction downloadFile(path) {\n\t\t// Open download URL in new tab\n\t\twindow.open('/api/files/download?path=' + encodeURIComponent(path), '_blank');\n\t}\n\t\n\tfunction viewFile(path) {\n\t\t// Open file viewer in new tab\n\t\twindow.open('/api/files/view?path=' + encodeURIComponent(path), '_blank');\n\t}\n\t\n\tfunction showFileProperties(path) {\n\t\t// Fetch file properties and show in modal\n\t\tfetch('/api/files/properties?path=' + encodeURIComponent(path))\n\t\t\t.then(response => response.json())\n\t\t\t.then(data => {\n\t\t\t\tif (data.error) {\n\t\t\t\t\talert('Error loading file properties: ' + data.error);\n\t\t\t\t} else {\n\t\t\t\t\tdisplayFileProperties(data);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch(error => {\n\t\t\t\tconsole.error('Error fetching file properties:', error);\n\t\t\t\talert('Error loading file properties: ' + error.message);\n\t\t\t});\n\t}\n\t\n\tfunction displayFileProperties(data) {\n\t\t// Create a comprehensive modal for file properties\n\t\tconst modalHtml = '<div class=\"modal fade\" id=\"filePropertiesModal\" tabindex=\"-1\">' +\n\t\t\t'<div class=\"modal-dialog modal-lg\">' +\n\t\t\t'<div class=\"modal-content\">' +\n\t\t\t'<div class=\"modal-header\">' +\n\t\t\t'<h5 class=\"modal-title\"><i class=\"fas fa-info-circle me-2\"></i>Properties: ' + (data.name || 'Unknown') + '</h5>' +\n\t\t\t'<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"></button>' +\n\t\t\t'</div>' +\n\t\t\t'<div class=\"modal-body\">' +\n\t\t\tcreateFilePropertiesContent(data) +\n\t\t\t'</div>' +\n\t\t\t'<div class=\"modal-footer\">' +\n\t\t\t'<button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Close</button>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'</div>';\n\t\t\n\t\t// Remove existing modal if present\n\t\tconst existingModal = document.getElementById('filePropertiesModal');\n\t\tif (existingModal) {\n\t\t\texistingModal.remove();\n\t\t}\n\t\t\n\t\t// Add modal to body and show\n\t\tdocument.body.insertAdjacentHTML('beforeend', modalHtml);\n\t\tconst modal = new bootstrap.Modal(document.getElementById('filePropertiesModal'));\n\t\tmodal.show();\n\t\t\n\t\t// Remove modal when hidden\n\t\tdocument.getElementById('filePropertiesModal').addEventListener('hidden.bs.modal', function() {\n\t\t\tthis.remove();\n\t\t});\n\t}\n\t\n\tfunction createFilePropertiesContent(data) {\n\t\tlet html = '<div class=\"row\">' +\n\t\t\t'<div class=\"col-12\">' +\n\t\t\t'<h6 class=\"text-primary\"><i class=\"fas fa-file me-1\"></i>Basic Information</h6>' +\n\t\t\t'<table class=\"table table-sm\">' +\n\t\t\t'<tr><td style=\"width: 120px;\"><strong>Name:</strong></td><td>' + (data.name || 'N/A') + '</td></tr>' +\n\t\t\t'<tr><td><strong>Full Path:</strong></td><td><code class=\"text-break\">' + (data.full_path || 'N/A') + '</code></td></tr>' +\n\t\t\t'<tr><td><strong>Type:</strong></td><td>' + (data.is_directory ? 'Directory' : 'File') + '</td></tr>';\n\t\t\n\t\tif (!data.is_directory) {\n\t\t\thtml += '<tr><td><strong>Size:</strong></td><td>' + (data.size_formatted || (data.size ? formatBytes(data.size) : 'N/A')) + '</td></tr>' +\n\t\t\t\t'<tr><td><strong>MIME Type:</strong></td><td>' + (data.mime_type || 'N/A') + '</td></tr>';\n\t\t}\n\t\t\n\t\thtml += '</table>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'<div class=\"row\">' +\n\t\t\t'<div class=\"col-md-6\">' +\n\t\t\t'<h6 class=\"text-primary\"><i class=\"fas fa-clock me-1\"></i>Timestamps</h6>' +\n\t\t\t'<table class=\"table table-sm\">';\n\t\t\n\t\tif (data.modified_time) {\n\t\t\thtml += '<tr><td><strong>Modified:</strong></td><td>' + data.modified_time + '</td></tr>';\n\t\t}\n\t\tif (data.created_time) {\n\t\t\thtml += '<tr><td><strong>Created:</strong></td><td>' + data.created_time + '</td></tr>';\n\t\t}\n\t\t\n\t\thtml += '</table>' +\n\t\t\t'</div>' +\n\t\t\t'<div class=\"col-md-6\">' +\n\t\t\t'<h6 class=\"text-primary\"><i class=\"fas fa-shield-alt me-1\"></i>Permissions</h6>' +\n\t\t\t'<table class=\"table table-sm\">';\n\t\t\n\t\tif (data.file_mode) {\n\t\t\tconst rwxPermissions = formatPermissions(data.file_mode, data.is_directory);\n\t\t\thtml += '<tr><td><strong>Permissions:</strong></td><td><code>' + rwxPermissions + '</code></td></tr>';\n\t\t}\n\t\tif (data.uid !== undefined) {\n\t\t\thtml += '<tr><td><strong>User ID:</strong></td><td>' + data.uid + '</td></tr>';\n\t\t}\n\t\tif (data.gid !== undefined) {\n\t\t\thtml += '<tr><td><strong>Group ID:</strong></td><td>' + data.gid + '</td></tr>';\n\t\t}\n\t\t\n\t\thtml += '</table>' +\n\t\t\t'</div>' +\n\t\t\t'</div>';\n\t\t\n\t\t// Add advanced info\n\t\thtml += '<div class=\"row\">' +\n\t\t\t'<div class=\"col-12\">' +\n\t\t\t'<h6 class=\"text-primary\"><i class=\"fas fa-cog me-1\"></i>Advanced</h6>' +\n\t\t\t'<table class=\"table table-sm\">';\n\t\t\n\t\tif (data.chunk_count) {\n\t\t\thtml += '<tr><td style=\"width: 120px;\"><strong>Chunks:</strong></td><td>' + data.chunk_count + '</td></tr>';\n\t\t}\n\t\tif (data.ttl_formatted) {\n\t\t\thtml += '<tr><td><strong>TTL:</strong></td><td>' + data.ttl_formatted + '</td></tr>';\n\t\t}\n\t\t\n\t\thtml += '</table>' +\n\t\t\t'</div>' +\n\t\t\t'</div>';\n\t\t\n\t\t// Add chunk details if available (show top 5)\n\t\tif (data.chunks && data.chunks.length > 0) {\n\t\t\tconst chunksToShow = data.chunks.slice(0, 5);\n\t\t\thtml += '<div class=\"row mt-3\">' +\n\t\t\t\t'<div class=\"col-12\">' +\n\t\t\t\t'<h6 class=\"text-primary\"><i class=\"fas fa-puzzle-piece me-1\"></i>Chunk Details' +\n\t\t\t\t(data.chunk_count > 5 ? ' (Top 5 of ' + data.chunk_count + ')' : ' (' + data.chunk_count + ')') +\n\t\t\t\t'</h6>' +\n\t\t\t\t'<div class=\"table-responsive\" style=\"max-height: 200px; overflow-y: auto;\">' +\n\t\t\t\t'<table class=\"table table-sm table-striped\">' +\n\t\t\t\t'<thead>' +\n\t\t\t\t'<tr>' +\n\t\t\t\t'<th>File ID</th>' +\n\t\t\t\t'<th>Offset</th>' +\n\t\t\t\t'<th>Size</th>' +\n\t\t\t\t'<th>ETag</th>' +\n\t\t\t\t'</tr>' +\n\t\t\t\t'</thead>' +\n\t\t\t\t'<tbody>';\n\t\t\t\n\t\t\tchunksToShow.forEach(chunk => {\n\t\t\t\thtml += '<tr>' +\n\t\t\t\t\t'<td><code class=\"small\">' + (chunk.file_id || 'N/A') + '</code></td>' +\n\t\t\t\t\t'<td>' + formatBytes(chunk.offset || 0) + '</td>' +\n\t\t\t\t\t'<td>' + formatBytes(chunk.size || 0) + '</td>' +\n\t\t\t\t\t'<td><code class=\"small\">' + (chunk.e_tag || 'N/A') + '</code></td>' +\n\t\t\t\t\t'</tr>';\n\t\t\t});\n\t\t\t\n\t\t\thtml += '</tbody>' +\n\t\t\t\t'</table>' +\n\t\t\t\t'</div>' +\n\t\t\t\t'</div>' +\n\t\t\t\t'</div>';\n\t\t}\n\t\t\n\t\t// Add extended attributes if present\n\t\tif (data.extended && Object.keys(data.extended).length > 0) {\n\t\t\thtml += '<div class=\"row\">' +\n\t\t\t\t'<div class=\"col-12\">' +\n\t\t\t\t'<h6 class=\"text-primary\"><i class=\"fas fa-tags me-1\"></i>Extended Attributes</h6>' +\n\t\t\t\t'<table class=\"table table-sm\">';\n\t\t\t\n\t\t\tfor (const [key, value] of Object.entries(data.extended)) {\n\t\t\t\thtml += '<tr><td><strong>' + key + ':</strong></td><td>' + value + '</td></tr>';\n\t\t\t}\n\t\t\t\n\t\t\thtml += '</table>' +\n\t\t\t\t'</div>' +\n\t\t\t\t'</div>';\n\t\t}\n\t\t\n\t\treturn html;\n\t}\n\t\n\tfunction uploadFile() {\n\t\tconst modal = new bootstrap.Modal(document.getElementById('uploadFileModal'));\n\t\tmodal.show();\n\t}\n\t\n\tfunction toggleSelectAll() {\n\t\tconst selectAllCheckbox = document.getElementById('selectAll');\n\t\tconst checkboxes = document.querySelectorAll('.file-checkbox');\n\t\t\n\t\tcheckboxes.forEach(checkbox => {\n\t\t\tcheckbox.checked = selectAllCheckbox.checked;\n\t\t});\n\t\t\n\t\tupdateDeleteSelectedButton();\n\t}\n\t\n\tfunction updateDeleteSelectedButton() {\n\t\tconst checkboxes = document.querySelectorAll('.file-checkbox:checked');\n\t\tconst deleteBtn = document.getElementById('deleteSelectedBtn');\n\t\t\n\t\tif (checkboxes.length > 0) {\n\t\t\tdeleteBtn.style.display = 'inline-block';\n\t\t} else {\n\t\t\tdeleteBtn.style.display = 'none';\n\t\t}\n\t}\n\t\n\t// Helper function to format bytes\n\tfunction formatBytes(bytes) {\n\t\tif (bytes === 0) return '0 Bytes';\n\t\tconst k = 1024;\n\t\tconst sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];\n\t\tconst i = Math.floor(Math.log(bytes) / Math.log(k));\n\t\treturn parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n\t}\n\t\n\t// Helper function to format permissions in rwxrwxrwx format\n\tfunction formatPermissions(mode, isDirectory) {\n\t\t// Check if mode is already in rwxrwxrwx format (e.g., \"drwxr-xr-x\" or \"-rw-r--r--\")\n\t\tif (mode && (mode.startsWith('d') || mode.startsWith('-') || mode.startsWith('l')) && mode.length === 10) {\n\t\t\treturn mode; // Already formatted\n\t\t}\n\t\t\n\t\t// Convert to number - could be octal string or decimal\n\t\tlet permissions;\n\t\tif (typeof mode === 'string') {\n\t\t\t// Try parsing as octal first, then decimal\n\t\t\tif (mode.startsWith('0') && mode.length <= 4) {\n\t\t\t\tpermissions = parseInt(mode, 8);\n\t\t\t} else {\n\t\t\t\tpermissions = parseInt(mode, 10);\n\t\t\t}\n\t\t} else {\n\t\t\tpermissions = parseInt(mode, 10);\n\t\t}\n\t\t\n\t\tif (isNaN(permissions)) {\n\t\t\treturn isDirectory ? 'drwxr-xr-x' : '-rw-r--r--'; // Default fallback\n\t\t}\n\t\t\n\t\t// Handle Go's os.ModeDir conversion\n\t\t// Go's os.ModeDir is 0x80000000 (2147483648), but Unix S_IFDIR is 0o40000 (16384)\n\t\tlet fileType = '-';\n\t\t\n\t\t// Check for Go's os.ModeDir flag\n\t\tif (permissions & 0x80000000) {\n\t\t\tfileType = 'd';\n\t\t}\n\t\t// Check for standard Unix file type bits\n\t\telse if ((permissions & 0xF000) === 0x4000) { // S_IFDIR (0o40000)\n\t\t\tfileType = 'd';\n\t\t} else if ((permissions & 0xF000) === 0x8000) { // S_IFREG (0o100000)\n\t\t\tfileType = '-';\n\t\t} else if ((permissions & 0xF000) === 0xA000) { // S_IFLNK (0o120000)\n\t\t\tfileType = 'l';\n\t\t} else if ((permissions & 0xF000) === 0x2000) { // S_IFCHR (0o020000)\n\t\t\tfileType = 'c';\n\t\t} else if ((permissions & 0xF000) === 0x6000) { // S_IFBLK (0o060000)\n\t\t\tfileType = 'b';\n\t\t} else if ((permissions & 0xF000) === 0x1000) { // S_IFIFO (0o010000)\n\t\t\tfileType = 'p';\n\t\t} else if ((permissions & 0xF000) === 0xC000) { // S_IFSOCK (0o140000)\n\t\t\tfileType = 's';\n\t\t}\n\t\t// Fallback to isDirectory parameter if file type detection fails\n\t\telse if (isDirectory) {\n\t\t\tfileType = 'd';\n\t\t}\n\t\t\n\t\t// Permission bits (always use the lower 12 bits for permissions)\n\t\tconst owner = (permissions >> 6) & 7;\n\t\tconst group = (permissions >> 3) & 7;\n\t\tconst others = permissions & 7;\n\t\t\n\t\t// Convert number to rwx format\n\t\tfunction numToRwx(num) {\n\t\t\tconst r = (num & 4) ? 'r' : '-';\n\t\t\tconst w = (num & 2) ? 'w' : '-';\n\t\t\tconst x = (num & 1) ? 'x' : '-';\n\t\t\treturn r + w + x;\n\t\t}\n\t\t\n\t\treturn fileType + numToRwx(owner) + numToRwx(group) + numToRwx(others);\n\t}\n\t\n\tfunction exportFileList() {\n\t\t// Simple CSV export of file list\n\t\tconst rows = Array.from(document.querySelectorAll('#fileTable tbody tr')).map(row => {\n\t\t\tconst cells = row.querySelectorAll('td');\n\t\t\tif (cells.length > 1) {\n\t\t\t\treturn {\n\t\t\t\t\tname: cells[1].textContent.trim(),\n\t\t\t\t\tsize: cells[2].textContent.trim(),\n\t\t\t\t\ttype: cells[3].textContent.trim(),\n\t\t\t\t\tmodified: cells[4].textContent.trim(),\n\t\t\t\t\tpermissions: cells[5].textContent.trim()\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn null;\n\t\t}).filter(row => row !== null);\n\t\t\n\t\tconst csvContent = \"data:text/csv;charset=utf-8,\" + \n\t\t\t\"Name,Size,Type,Modified,Permissions\\n\" +\n\t\t\trows.map(r => '\"' + r.name + '\",\"' + r.size + '\",\"' + r.type + '\",\"' + r.modified + '\",\"' + r.permissions + '\"').join(\"\\n\");\n\t\t\n\t\tconst encodedUri = encodeURI(csvContent);\n\t\tconst link = document.createElement(\"a\");\n\t\tlink.setAttribute(\"href\", encodedUri);\n\t\tlink.setAttribute(\"download\", \"files.csv\");\n\t\tdocument.body.appendChild(link);\n\t\tlink.click();\n\t\tdocument.body.removeChild(link);\n\t}\n\t\n\t// Handle file checkbox changes\n\tdocument.addEventListener('change', function(e) {\n\t\tif (e.target.classList.contains('file-checkbox')) {\n\t\t\tupdateDeleteSelectedButton();\n\t\t}\n\t});\n\t</script>")
  546. if templ_7745c5c3_Err != nil {
  547. return templ_7745c5c3_Err
  548. }
  549. return nil
  550. })
  551. }
  552. func countDirectories(entries []dash.FileEntry) int {
  553. count := 0
  554. for _, entry := range entries {
  555. if entry.IsDirectory {
  556. count++
  557. }
  558. }
  559. return count
  560. }
  561. func countFiles(entries []dash.FileEntry) int {
  562. count := 0
  563. for _, entry := range entries {
  564. if !entry.IsDirectory {
  565. count++
  566. }
  567. }
  568. return count
  569. }
  570. func getFileIcon(mime string) string {
  571. switch {
  572. case strings.HasPrefix(mime, "image/"):
  573. return "fa-image"
  574. case strings.HasPrefix(mime, "video/"):
  575. return "fa-video"
  576. case strings.HasPrefix(mime, "audio/"):
  577. return "fa-music"
  578. case strings.HasPrefix(mime, "text/"):
  579. return "fa-file-text"
  580. case mime == "application/pdf":
  581. return "fa-file-pdf"
  582. case mime == "application/zip" || strings.Contains(mime, "archive"):
  583. return "fa-file-archive"
  584. case mime == "application/json":
  585. return "fa-file-code"
  586. case strings.Contains(mime, "script") || strings.Contains(mime, "javascript"):
  587. return "fa-file-code"
  588. default:
  589. return "fa-file"
  590. }
  591. }
  592. func getMimeDisplayName(mime string) string {
  593. switch mime {
  594. case "text/plain":
  595. return "Text"
  596. case "text/html":
  597. return "HTML"
  598. case "application/json":
  599. return "JSON"
  600. case "application/pdf":
  601. return "PDF"
  602. case "image/jpeg":
  603. return "JPEG"
  604. case "image/png":
  605. return "PNG"
  606. case "image/gif":
  607. return "GIF"
  608. case "video/mp4":
  609. return "MP4"
  610. case "audio/mpeg":
  611. return "MP3"
  612. case "application/zip":
  613. return "ZIP"
  614. default:
  615. if strings.HasPrefix(mime, "image/") {
  616. return "Image"
  617. } else if strings.HasPrefix(mime, "video/") {
  618. return "Video"
  619. } else if strings.HasPrefix(mime, "audio/") {
  620. return "Audio"
  621. } else if strings.HasPrefix(mime, "text/") {
  622. return "Text"
  623. }
  624. return "File"
  625. }
  626. }
  627. var _ = templruntime.GeneratedTemplate