trusty-analyze 0.2.0

Sidecar code-analysis daemon for trusty-search: complexity, smells, quality, facts
Documentation
<script>
  /*
   * Why: FactStore is the (subject, predicate, object) knowledge layer; the
   * UI lets operators search and review facts without leaving the dashboard.
   * What: Search inputs for subject/predicate that drive /facts queries, plus
   * a results table.
   * Test: Type a subject, click Search, confirm only matching rows return.
   */
  import { onMount } from 'svelte';
  import { api } from '../api.js';
  import { getFacts, refreshFacts } from '../state.svelte.js';

  let facts = $derived(getFacts());
  let subject = $state('');
  let predicate = $state('');
  let error = $state(null);
  let busy = $state(false);

  onMount(() => {
    refreshFacts().catch((e) => {
      error = e.message || String(e);
    });
  });

  async function search() {
    error = null;
    busy = true;
    try {
      await refreshFacts(subject || undefined, predicate || undefined);
    } catch (e) {
      error = e.message || String(e);
    } finally {
      busy = false;
    }
  }

  async function remove(id) {
    if (!id) return;
    try {
      await api.deleteFact(id);
      await refreshFacts(subject || undefined, predicate || undefined);
    } catch (e) {
      error = e.message || String(e);
    }
  }
</script>

<h1 class="page-title">Facts</h1>

<div class="card mb-4">
  <div class="card-body">
    <div class="search-row">
      <label class="form-group" style="flex: 1; margin: 0">
        <div class="form-label">Subject</div>
        <input class="input" type="text" bind:value={subject} placeholder="fn auth" />
      </label>
      <label class="form-group" style="flex: 1; margin: 0">
        <div class="form-label">Predicate</div>
        <input class="input" type="text" bind:value={predicate} placeholder="uses" />
      </label>
      <button class="btn btn-primary" onclick={search} disabled={busy}>
        {busy ? 'Searching…' : 'Search'}
      </button>
    </div>
    {#if error}
      <p class="text-sm mt-3" style="color: var(--trusty-danger)">{error}</p>
    {/if}
  </div>
</div>

<div class="card">
  <div class="card-header">Results ({facts.length})</div>
  <div class="card-body" style="padding: 0">
    {#if facts.length === 0}
      <div class="empty">No facts.</div>
    {:else}
      <table class="table">
        <thead>
          <tr>
            <th>Subject</th>
            <th>Predicate</th>
            <th>Object</th>
            <th>Provenance</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          {#each facts as f}
            <tr>
              <td class="text-mono text-xs">{f.subject}</td>
              <td class="text-mono text-xs">{f.predicate}</td>
              <td class="text-mono text-xs">{f.object}</td>
              <td class="text-muted text-xs">{f.provenance || '—'}</td>
              <td>
                {#if f.id}
                  <button class="btn btn-sm btn-danger" onclick={() => remove(f.id)}>
                    delete
                  </button>
                {/if}
              </td>
            </tr>
          {/each}
        </tbody>
      </table>
    {/if}
  </div>
</div>

<style>
  .page-title {
    font-size: var(--trusty-fs-xl);
    margin: 0 0 var(--trusty-space-5) 0;
    font-weight: 600;
  }
  .search-row {
    display: flex;
    gap: var(--trusty-space-3);
    align-items: flex-end;
  }
</style>