PageSourceSearch

https://app.ricordi.ai/assets/controllers/document_upload_controller-eeae8eed.js

js ricordi.ai collected 2026-10-05 15:48:28 UTC 2,832 bytes, 86 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3const MEDIA_EXTENSIONS = /\.(mp4|mov|webm|mkv|avi|mpeg|mp3|m4a|aac|ogg|wav|flac)$/i
4
5export default class extends Controller {
6  static targets = ["input", "filePlaceholderTemplate", "chatPlaceholderTemplate"]
7  static values = { url: String, mediaUrl: String, directUploadUrl: String }
8
9  #abortControllers = new Set()
10
11  disconnect() {
12    for (const ac of this.#abortControllers) ac.abort()
13    this.#abortControllers.clear()
14  }
15
16  upload() {
17    const file = this.inputTarget.files[0]
18    if (!file) return
19    this.inputTarget.value = ""
20
21    const isMedia = file.type.startsWith("audio/") || file.type.startsWith("video/") || MEDIA_EXTENSIONS.test(file.name)
22    const id = crypto.randomUUID()
23    const placeholder = this.#insertPlaceholder(id, file.name)
24    const ac = new AbortController()
25    this.#abortControllers.add(ac)
26
27    const cleanup = () => {
28      ac.abort()
29      this.#abortControllers.delete(ac)
30      placeholder.remove()
31    }
32
33    document.addEventListener("upload-manager:progress", (e) => {
34      if (e.detail.id === id) this.#updateProgress(placeholder, e.detail.percent)
35    }, { signal: ac.signal })
36
37    document.addEventListener("upload-manager:complete", (e) => {
38      if (e.detail.id === id) cleanup()
39    }, { signal: ac.signal })
40
41    document.addEventListener("upload-manager:error", (e) => {
42      if (e.detail.id === id) cleanup()
43    }, { signal: ac.signal })
44
45    document.dispatchEvent(new CustomEvent("upload-manager:request", {
46      detail: {
47        id,
48        file,
49        directUploadUrl: this.directUploadUrlValue,
50        destinationUrl: isMedia && this.hasMediaUrlValue ? this.mediaUrlValue : this.urlValue,
51        body: (signedId) => isMedia
52          ? { signed_id: signedId }
53          : { document: { signed_id: signedId, filename: file.name } },
54        fileName: file.name,
55      },
56    }))
57  }
58
59  get #container() {
60    return document.getElementById("files") || document.getElementById("recordings")
61  }
62
63  get #isFilesPage() {
64    return !!document.getElementById("files")
65  }
66
67  #insertPlaceholder(id, filename) {
68    const template = this.#isFilesPage ? this.filePlaceholderTemplateTarget : this.chatPlaceholderTemplateTarget
69    const el = template.content.firstElementChild.cloneNode(true)
70    el.dataset.uploadId = id
71    el.querySelector("[data-filename]").textContent = filename
72    this.#container?.prepend(el)
73    return el
74  }
75
76  #updateProgress(el, percent) {
77    const radial = el.querySelector(".radial-progress")
78    if (!radial) return
79    radial.style.setProperty("--value", percent)
80    radial.querySelector("span").textContent = `${percent}%`
81    if (percent >= 100) {
82      const statusEl = el.querySelector("[data-status]")
83      if (statusEl) statusEl.textContent = "Processing..."
84    }
85  }
86}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.