<!DOCTYPE html>
<html lang="en" data-bs-theme="auto">

<head>
      <!-- TEMPLATE_HEADER - Used by Every UI Page -->
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sunshine</title>
<link rel="icon" type="image/x-icon" href="./images/sunshine.ico">

<link href="./assets/css/sunshine.css" rel="stylesheet" />

  <script type="module" crossorigin src="./assets/apps-CPFgR2IA.js"></script>
  <link rel="modulepreload" crossorigin href="./assets/Notification-CBsCzcFI.js">
  <link rel="modulepreload" crossorigin href="./assets/Navbar-BJwSTNaf.js">
  <link rel="modulepreload" crossorigin href="./assets/Checkbox-Bu3ROfG8.js">
  <link rel="modulepreload" crossorigin href="./assets/fetch_utils-B73kExl8.js">
  <link rel="modulepreload" crossorigin href="./assets/trash-2-BLmZGN1a.js">
  <link rel="modulepreload" crossorigin href="./assets/save-6rxVew8l.js">
  <link rel="modulepreload" crossorigin href="./assets/rotate-ccw-CEeGI7M1.js">
  <link rel="modulepreload" crossorigin href="./assets/file-text-B9jaTKZU.js">
  <link rel="stylesheet" crossorigin href="./assets/Notification-BcQGACFn.css">
  <link rel="stylesheet" crossorigin href="./assets/Navbar-vHSgVmbh.css">
</head>

<body id="app" v-cloak>
  <Navbar></Navbar>
  <div class="container">
    <div class="my-4">
      <h1>{{ $t('apps.applications_title') }}</h1>
      <p>{{ $t('apps.applications_desc') }}</p>
    </div>

    <!-- Apps Grid -->
    <div class="row g-3" v-if="apps && apps.length > 0">
      <div class="col-12 col-sm-6 col-md-4 col-lg-3" v-for="(app,i) in apps" :key="i">
        <div class="card app-card h-100">
          <div class="app-poster-container">
            <img
              v-if="app['image-path']"
              :src="'/api/covers/' + i"
              class="app-poster"
              :alt="app.name"
              @error="handleImageError"
            />
            <div v-else class="app-poster-placeholder">
              <span class="app-initial">{{ app.name.charAt(0).toUpperCase() }}</span>
            </div>
          </div>
          <div class="card-body d-flex flex-column">
            <h5 class="card-title mb-2">{{ app.name }}</h5>
            <div class="app-details text-muted small mb-3">
              <div v-if="app.output" class="text-truncate">
                <file-text :size="14" class="icon me-1"></file-text>
                {{ app.output }}
              </div>
              <div v-if="app.cmd" class="text-truncate">
                <terminal :size="14" class="icon me-1"></terminal>
                {{ app.cmd }}
              </div>
            </div>
            <div class="mt-auto d-flex gap-2">
              <button class="btn btn-sm btn-primary flex-fill" @click="editApp(i)">
                <edit :size="16" class="icon"></edit>
                {{ $t('apps.edit') }}
              </button>
              <button class="btn btn-sm btn-danger" @click="showDeleteForm(i)">
                <trash-2 :size="16" class="icon"></trash-2>
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Empty State -->
    <div v-else class="card">
      <div class="card-body text-center py-5">
        <p class="text-muted">{{ $t('apps.no_applications') }}</p>
      </div>
    </div>

    <div class="edit-form card mt-4" v-if="showEditForm">
      <div class="card-body">
        <!-- Application Name -->
        <div class="mb-3">
          <label for="appName" class="form-label">{{ $t('apps.app_name') }}</label>
          <input type="text" class="form-control" id="appName" aria-describedby="appNameHelp" v-model="editForm.name" />
          <div id="appNameHelp" class="form-text">{{ $t('apps.app_name_desc') }}</div>
        </div>
        <!-- output -->
        <div class="mb-3">
          <label for="appOutput" class="form-label">{{ $t('apps.output_name') }}</label>
          <div class="input-group">
            <input type="text" class="form-control monospace" id="appOutput" aria-describedby="appOutputHelp"
              v-model="editForm.output" />
            <button class="btn btn-secondary" type="button"
              @click="browseFor('any', 'file_browser.select_file', editForm.output, v => editForm.output = v)">
              <folder-open :size="18" class="icon"></folder-open>
            </button>
          </div>
          <div id="appOutputHelp" class="form-text">{{ $t('apps.output_desc') }}</div>
        </div>
        <!-- prep-cmd -->
        <Checkbox class="mb-3"
                  id="excludeGlobalPrep"
                  label="apps.global_prep_name"
                  desc="apps.global_prep_desc"
                  v-model="editForm['exclude-global-prep-cmd']"
                  default="true"
                  inverse-values
        ></Checkbox>
        <div class="mb-3">
          <label for="appName" class="form-label">{{ $t('apps.cmd_prep_name') }}</label>
          <div class="form-text">{{ $t('apps.cmd_prep_desc') }}</div>
          <div class="d-flex justify-content-start mb-3 mt-3" v-if="editForm['prep-cmd'].length === 0">
            <button class="btn btn-success" @click="addPrepCmd">
              <plus :size="18" class="icon"></plus>
              {{ $t('apps.add_cmds') }}
            </button>
          </div>
          <table class="table" v-if="editForm['prep-cmd'].length > 0">
            <thead>
              <tr>
                <th scope="col">
                  <play :size="18" class="icon"></play>
                  {{ $t('_common.do_cmd') }}
                </th>
                <th scope="col">
                  <rotate-ccw :size="18" class="icon"></rotate-ccw>
                  {{ $t('_common.undo_cmd') }}
                </th>
                <th scope="col" v-if="platform === 'windows'">
                  <shield :size="18" class="icon"></shield>
                  {{ $t('_common.run_as') }}
                </th>
                <th scope="col"></th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="(c, i) in editForm['prep-cmd']">
                <td>
                  <div class="input-group">
                    <input type="text" class="form-control monospace" v-model="c.do" />
                    <button class="btn btn-secondary btn-sm" type="button" @click="browsePrep(i, 'do')">
                      <folder-open :size="14" class="icon"></folder-open>
                    </button>
                  </div>
                </td>
                <td>
                  <div class="input-group">
                    <input type="text" class="form-control monospace" v-model="c.undo" />
                    <button class="btn btn-secondary btn-sm" type="button" @click="browsePrep(i, 'undo')">
                      <folder-open :size="14" class="icon"></folder-open>
                    </button>
                  </div>
                </td>
                <td v-if="platform === 'windows'" class="align-middle">
                  <Checkbox :id="'prep-cmd-admin-' + i"
                            label="_common.elevated"
                            desc=""
                            v-model="c.elevated"
                  ></Checkbox>
                </td>
                <td class="align-middle">
                  <button class="btn btn-danger btn-sm ms-2" @click="deletePrepCmd(i)">
                    <trash-2 :size="16" class="icon"></trash-2>
                  </button>
                  <button class="btn btn-success btn-sm ms-2" @click="addPrepCmd">
                    <plus :size="16" class="icon"></plus>
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <!-- detached -->
        <div class="mb-3">
          <label for="appName" class="form-label">{{ $t('apps.detached_cmds') }}</label>
          <div v-for="(c,i) in editForm.detached" class="d-flex justify-content-between align-items-center my-2">
            <input type="text" v-model="editForm.detached[i]" class="form-control monospace">
            <button class="btn btn-secondary btn-sm ms-2" @click="browseDetached(i)">
              <folder-open :size="14" class="icon"></folder-open>
            </button>
            <button class="btn btn-danger btn-sm ms-2" @click="editForm.detached.splice(i,1)">
              <trash-2 :size="16" class="icon"></trash-2>
            </button>
            <button class="btn btn-success btn-sm ms-2" @click="addDetached">
              <plus :size="16" class="icon"></plus>
            </button>
          </div>
          <div class="d-flex justify-content-start mb-3 mt-3" v-if="editForm.detached.length === 0">
            <button class="btn btn-success" @click="addDetached">
              <plus :size="18" class="icon"></plus>
              {{ $t('apps.detached_cmds_add') }}
            </button>
          </div>
          <div class="form-text">
            {{ $t('apps.detached_cmds_desc') }}<br>
            <b>{{ $t('_common.note') }}</b> {{ $t('apps.detached_cmds_note') }}
          </div>
        </div>
        <!-- command -->
        <div class="mb-3">
          <label for="appCmd" class="form-label">{{ $t('apps.cmd') }}</label>
          <div class="input-group">
            <input type="text" class="form-control monospace" id="appCmd" aria-describedby="appCmdHelp"
              v-model="editForm.cmd" />
            <button class="btn btn-secondary" type="button"
              @click="browseFor('executable', 'file_browser.select_executable', editForm.cmd, v => editForm.cmd = v)">
              <folder-open :size="18" class="icon"></folder-open>
            </button>
          </div>
          <div id="appCmdHelp" class="form-text">
            {{ $t('apps.cmd_desc') }}<br>
            <b>{{ $t('_common.note') }}</b> {{ $t('apps.cmd_note') }}
          </div>
        </div>
        <!-- working dir -->
        <div class="mb-3">
          <label for="appWorkingDir" class="form-label">{{ $t('apps.working_dir') }}</label>
          <div class="input-group">
            <input type="text" class="form-control monospace" id="appWorkingDir" aria-describedby="appWorkingDirHelp"
              v-model="editForm['working-dir']" />
            <button class="btn btn-secondary" type="button"
              @click="browseFor('directory', 'file_browser.select_directory', editForm['working-dir'], v => editForm['working-dir'] = v)">
              <folder-open :size="18" class="icon"></folder-open>
            </button>
          </div>
          <div id="appWorkingDirHelp" class="form-text">{{ $t('apps.working_dir_desc') }}</div>
        </div>
        <!-- elevation -->
        <Checkbox v-if="platform === 'windows'"
                  class="mb-3"
                  id="appElevation"
                  label="_common.run_as"
                  desc="apps.run_as_desc"
                  v-model="editForm.elevated"
                  default="false"
        ></Checkbox>
        <!-- auto-detach -->
        <Checkbox class="mb-3"
                  id="autoDetach"
                  label="apps.auto_detach"
                  desc="apps.auto_detach_desc"
                  v-model="editForm['auto-detach']"
                  default="true"
        ></Checkbox>
        <!-- wait for all processes -->
        <Checkbox class="mb-3"
                  id="waitAll"
                  label="apps.wait_all"
                  desc="apps.wait_all_desc"
                  v-model="editForm['wait-all']"
                  default="true"
        ></Checkbox>
        <!-- exit timeout -->
        <div class="mb-3">
          <label for="exitTimeout" class="form-label">{{ $t('apps.exit_timeout') }}</label>
          <input type="number" class="form-control monospace" id="exitTimeout" aria-describedby="exitTimeoutHelp"
                 v-model="editForm['exit-timeout']" min="0" placeholder="5" />
          <div id="exitTimeoutHelp" class="form-text">{{ $t('apps.exit_timeout_desc') }}</div>
        </div>
        <div class="mb-3">
          <label for="appImagePath" class="form-label">{{ $t('apps.image') }}</label>
          <div class="input-group">
            <input type="text" class="form-control monospace" id="appImagePath" aria-describedby="appImagePathHelp"
              v-model="editForm['image-path']" />
            <button class="btn btn-secondary" type="button"
              @click="browseFor('file', 'file_browser.select_file', editForm['image-path'], v => editForm['image-path'] = v)">
              <folder-open :size="18" class="icon"></folder-open>
            </button>
            <button class="btn btn-secondary" type="button" data-bs-toggle="modal" data-bs-target="#coverFinderModal"
              @click="showCoverFinder">
              <search :size="18" class="icon"></search>
              {{ $t('apps.find_cover') }}
            </button>
          </div>
          <div id="appImagePathHelp" class="form-text">{{ $t('apps.image_desc') }}</div>
        </div>

        <!-- Cover Finder Modal -->
        <div class="modal fade" id="coverFinderModal" tabindex="-1" aria-labelledby="coverFinderModalLabel" aria-hidden="true" ref="coverFinderModal">
          <div class="modal-dialog modal-xl modal-dialog-scrollable modal-fullscreen-md-down">
            <div class="modal-content">
              <div class="modal-header">
                <h5 class="modal-title" id="coverFinderModalLabel">
                  <span v-if="coverSearching">{{ $t('apps.searching_covers') }}</span>
                  <span v-else-if="coverCandidates.length > 0">{{ $t('apps.covers_found') }} ({{ coverCandidates.length }})</span>
                  <span v-else>{{ $t('apps.no_covers_found') }}</span>
                </h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
              </div>
              <div class="modal-body">
                <div class="mb-3">
                  <div class="input-group">
                    <input
                      type="text"
                      class="form-control"
                      v-model="coverSearchQuery"
                      :placeholder="editForm.name"
                      @keyup.enter="performCoverSearch"
                    />
                    <button class="btn btn-primary" type="button" @click="performCoverSearch">
                      <search :size="18" class="icon"></search>
                      {{ $t('_common.search') }}
                    </button>
                  </div>
                  <div class="form-text mt-2">
                    <b>{{ $t('_common.note') }}</b> {{ $t('apps.cover_search_hint') }}
                    <a href="https://www.igdb.com/" target="_blank" rel="noopener noreferrer">IGDB</a>
                  </div>
                </div>
                <div class="cover-results" :class="{ busy: coverFinderBusy }">
                  <div class="row">
                    <div v-if="coverSearching" class="col-12 col-sm-6 col-lg-4 mb-3">
                      <div class="cover-container">
                        <div class="spinner-border" role="status">
                          <span class="visually-hidden">{{ $t('apps.loading') }}</span>
                        </div>
                      </div>
                    </div>
                    <div v-for="(cover,i) in coverCandidates" :key="'i'" class="col-12 col-sm-6 col-lg-3 mb-3"
                      @click="useCover(cover)">
                      <div class="cover-container result">
                        <img class="rounded" :src="cover.url" />
                      </div>
                      <label class="d-block text-nowrap text-center text-truncate">
                        {{cover.name}}
                      </label>
                    </div>
                  </div>
                </div>
              </div>
              <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
                  <x :size="18" class="icon"></x>
                  {{ $t('_common.cancel') }}
                </button>
              </div>
            </div>
          </div>
        </div>
        <div class="env-hint alert alert-info">
          <div class="form-text">
            <h4>{{ $t('apps.env_vars_about') }}</h4>
            {{ $t('apps.env_vars_desc') }}
          </div>
          <table class="env-table">
            <tr>
              <td><b>{{ $t('apps.env_var_name') }}</b></td>
              <td><b></b></td>
            </tr>
            <tr>
              <td style="font-family: monospace">SUNSHINE_APP_ID</td>
              <td>{{ $t('apps.env_app_id') }}</td>
            </tr>
            <tr>
              <td style="font-family: monospace">SUNSHINE_APP_NAME</td>
              <td>{{ $t('apps.env_app_name') }}</td>
            </tr>
            <tr>
              <td style="font-family: monospace">SUNSHINE_CLIENT_WIDTH</td>
              <td>{{ $t('apps.env_client_width') }}</td>
            </tr>
            <tr>
              <td style="font-family: monospace">SUNSHINE_CLIENT_HEIGHT</td>
              <td>{{ $t('apps.env_client_height') }}</td>
            </tr>
            <tr>
              <td style="font-family: monospace">SUNSHINE_CLIENT_FPS</td>
              <td>{{ $t('apps.env_client_fps') }}</td>
            </tr>
            <tr>
              <td style="font-family: monospace">SUNSHINE_CLIENT_HDR</td>
              <td>{{ $t('apps.env_client_hdr') }}</td>
            </tr>
            <tr>
              <td style="font-family: monospace">SUNSHINE_CLIENT_GCMAP</td>
              <td>{{ $t('apps.env_client_gcmap') }}</td>
            </tr>
            <tr>
              <td style="font-family: monospace">SUNSHINE_CLIENT_HOST_AUDIO</td>
              <td>{{ $t('apps.env_client_host_audio') }}</td>
            </tr>
            <tr>
              <td style="font-family: monospace">SUNSHINE_CLIENT_ENABLE_SOPS</td>
              <td>{{ $t('apps.env_client_enable_sops') }}</td>
            </tr>
            <tr>
              <td style="font-family: monospace">SUNSHINE_CLIENT_AUDIO_CONFIGURATION</td>
              <td>{{ $t('apps.env_client_audio_config') }}</td>
            </tr>
          </table>
          <div class="form-text" v-if="platform === 'windows'"><b>{{ $t('apps.env_qres_example') }}</b>
            <pre>cmd /C &lt;{{ $t('apps.env_qres_path') }}&gt;\QRes.exe /X:%SUNSHINE_CLIENT_WIDTH% /Y:%SUNSHINE_CLIENT_HEIGHT% /R:%SUNSHINE_CLIENT_FPS%</pre>
          </div>
          <div class="form-text" v-else-if="platform === 'freebsd' || platform === 'linux'"><b>{{ $t('apps.env_xrandr_example') }}</b>
            <pre>sh -c "xrandr --output HDMI-1 --mode \"${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}\" --rate ${SUNSHINE_CLIENT_FPS}"</pre>
          </div>
          <div class="form-text" v-else-if="platform === 'macos'"><b>{{ $t('apps.env_displayplacer_example') }}</b>
            <pre>sh -c "displayplacer "id:&lt;screenId&gt; res:${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} hz:${SUNSHINE_CLIENT_FPS} scaling:on origin:(0,0) degree:0""</pre>
          </div>
          <div class="form-text"><a
              href="https://docs.lizardbyte.dev/projects/sunshine/latest/md_docs_2app__examples.html"
              target="_blank">{{ $t('_common.see_more') }}</a></div>
        </div>
        <!-- Save buttons -->
        <div class="d-flex">
          <button @click="showEditForm = false" class="btn btn-secondary m-2">
            <x :size="18" class="icon"></x>
            {{ $t('_common.cancel') }}
          </button>
          <button class="btn btn-primary m-2" @click="save">
            <save :size="18" class="icon"></save>
            {{ $t('_common.save') }}
          </button>
        </div>
      </div>
    </div>
    <div class="mt-4" v-else>
      <button class="btn btn-primary" @click="newApp">
        <layers-plus :size="18" class="icon"></layers-plus>
        {{ $t('apps.add_new') }}
      </button>
    </div>

    <!-- Shared file browser modal -->
    <div class="modal fade" ref="fileBrowserModal" tabindex="-1" aria-hidden="true">
      <div class="modal-dialog modal-lg modal-dialog-scrollable modal-fullscreen-md-down">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title">{{ fileBrowserTitle || $t('file_browser.title') }}</h5>
            <button type="button" class="btn-close" @click="fileBrowserClose" :aria-label="$t('_common.close')"></button>
          </div>
          <div class="modal-body">
            <!-- Path input -->
            <div class="input-group mb-2">
              <input type="text" class="form-control monospace" v-model="fileBrowserTypedPath"
                @input="fileBrowserOnTypedInput" @keyup.enter="fileBrowserNavigate(fileBrowserTypedPath)" />
              <button class="btn btn-secondary" type="button" @click="fileBrowserNavigate(fileBrowserTypedPath)">
                <arrow-right :size="16" class="icon"></arrow-right>
              </button>
            </div>
            <!-- Up button -->
            <div class="mb-2">
              <button class="btn btn-sm btn-outline-secondary" type="button"
                :disabled="fileBrowserLoading || fileBrowserParentPath === fileBrowserCurrentPath"
                @click="fileBrowserNavigateUp">
                <folder-up :size="16" class="icon me-1"></folder-up>
                {{ $t('file_browser.up') }}
              </button>
            </div>
            <!-- Error -->
            <div v-if="fileBrowserError" class="alert alert-danger py-2 small">{{ fileBrowserError }}</div>
            <!-- Loading -->
            <div v-if="fileBrowserLoading" class="text-center py-3">
              <output class="spinner-border spinner-border-sm">
                <span class="visually-hidden">{{ $t('_common.loading') }}</span>
              </output>
            </div>
            <!-- Entries -->
            <div v-else class="list-group" style="max-height: 400px; overflow-y: auto;">
              <div v-if="fileBrowserEntries.length === 0" class="list-group-item text-muted text-center">
                {{ $t('file_browser.empty') }}
              </div>
              <button v-for="entry in fileBrowserEntries" :key="entry.path" type="button"
                class="list-group-item list-group-item-action d-flex align-items-center py-1"
                :class="{ active: fileBrowserSelectedPath === entry.path }"
                @click="fileBrowserSelectEntry(entry)" @dblclick="fileBrowserActivateEntry(entry)">
                <hard-drive v-if="!fileBrowserCurrentPath && entry.type === 'directory'" :size="16" class="icon me-2 flex-shrink-0"></hard-drive>
                <folder v-else-if="entry.type === 'directory'" :size="16" class="icon me-2 flex-shrink-0 text-warning"></folder>
                <file-text v-else :size="16" class="icon me-2 flex-shrink-0"></file-text>
                <span class="text-truncate">{{ entry.name }}</span>
              </button>
            </div>
          </div>
          <div class="modal-footer flex-wrap gap-2">
            <div class="flex-grow-1 text-muted small text-truncate" v-if="fileBrowserSelectedPath">
              <code>{{ fileBrowserSelectedPath }}</code>
            </div>
            <button type="button" class="btn btn-secondary" @click="fileBrowserClose">
              <x :size="16" class="icon me-1"></x>
              {{ $t('_common.cancel') }}
            </button>
            <button type="button" class="btn btn-primary" @click="fileBrowserConfirm"
              :disabled="!fileBrowserSelectedPath && !fileBrowserTypedPath">
              <check :size="16" class="icon me-1"></check>
              {{ $t('file_browser.select') }}
            </button>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

