> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siftstack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Explore (legacy) settings

> Settings, options, and behaviors for Explore (legacy) features.

export const MintTable = ({columns = [], rows = [], columnWidths = []}) => {
  const pushTextWithLineBreaks = (parts, text, keyBase) => {
    const segments = String(text).split(/\\n|\n/);
    segments.forEach((segment, idx) => {
      if (segment) {
        parts.push(<span key={`${keyBase}-text-${idx}`}>{segment}</span>);
      }
      if (idx < segments.length - 1) {
        parts.push(<br key={`${keyBase}-br-${idx}`} />);
      }
    });
  };
  const parseMarkdown = text => {
    if (text === null || text === undefined) return "";
    const str = String(text);
    const parts = [];
    let lastIndex = 0;
    const pattern = /(`[^`]+`|\*\*[^*]+\*\*|\*[^*]+\*|\[([^\]]+)\]\(([^)]+)\))/g;
    let match;
    while (true) {
      match = pattern.exec(str);
      if (match === null) {
        break;
      }
      if (match.index > lastIndex) {
        pushTextWithLineBreaks(parts, str.substring(lastIndex, match.index), `before-${lastIndex}`);
      }
      const fullMatch = match[0];
      if (fullMatch.startsWith("`") && fullMatch.endsWith("`")) {
        parts.push(<code key={match.index}>{fullMatch.slice(1, -1)}</code>);
      } else if (fullMatch.startsWith("**") && fullMatch.endsWith("**")) {
        parts.push(<strong key={match.index}>{fullMatch.slice(2, -2)}</strong>);
      } else if (fullMatch.startsWith("*") && fullMatch.endsWith("*")) {
        parts.push(<em key={match.index}>{fullMatch.slice(1, -1)}</em>);
      } else if (fullMatch.startsWith("[")) {
        const linkText = match[2];
        const linkUrl = match[3];
        parts.push(<a key={match.index} href={linkUrl} className="text-black-600 dark:text-black-400">
            {linkText}
          </a>);
      }
      lastIndex = pattern.lastIndex;
    }
    if (lastIndex < str.length) {
      pushTextWithLineBreaks(parts, str.substring(lastIndex), `tail-${lastIndex}`);
    }
    if (parts.length > 0) {
      return parts;
    }
    const plainParts = [];
    pushTextWithLineBreaks(plainParts, str, "plain");
    return plainParts.length ? plainParts : str;
  };
  const safeColumns = Array.isArray(columns) ? columns : [];
  const safeRows = Array.isArray(rows) ? rows : [];
  const safeColumnWidths = Array.isArray(columnWidths) ? columnWidths : [];
  const hasColumnWidths = safeColumnWidths.some(w => w !== null && w !== undefined && w !== "");
  const toCssWidth = width => typeof width === "number" ? `${width}px` : String(width);
  const getColumnStyle = idx => {
    const rawWidth = safeColumnWidths[idx];
    if (rawWidth === null || rawWidth === undefined || rawWidth === "") {
      return undefined;
    }
    const width = toCssWidth(rawWidth);
    return {
      width,
      minWidth: width
    };
  };
  const containerStyle = hasColumnWidths ? undefined : {
    overflowX: "auto"
  };
  const tableStyle = hasColumnWidths ? {
    tableLayout: "fixed",
    width: "100%"
  } : {
    width: "max-content",
    minWidth: "100%"
  };
  if (!Array.isArray(columns) || !Array.isArray(rows) || !Array.isArray(columnWidths)) {
    console.warn("MintTable received invalid props:", {
      columns,
      rows,
      columnWidths
    });
  }
  if (!safeColumns.length && !safeRows.length) {
    return null;
  }
  return <div className="mint-table-container" style={containerStyle}>
      <table style={tableStyle}>
        {hasColumnWidths && <colgroup>
            {safeColumns.map((_, idx) => {
    const style = getColumnStyle(idx);
    return <col key={idx} style={style} />;
  })}
          </colgroup>}
        <thead>
          <tr>
            {safeColumns.map((col, idx) => <th key={idx} className="text-left" style={getColumnStyle(idx)}>
                <b>{parseMarkdown(col)}</b>
              </th>)}
          </tr>
        </thead>
        <tbody>
          {safeRows.map((row, rIdx) => {
    const safeRow = Array.isArray(row) ? row : [];
    return <tr key={rIdx}>
                {safeRow.map((cell, cIdx) => <td key={cIdx} style={getColumnStyle(cIdx)}>
                    {parseMarkdown(cell)}
                  </td>)}
              </tr>;
  })}
        </tbody>
      </table>
    </div>;
};

Explore (legacy) is the previous version of Sift's telemetry visualization workspace. It remains available alongside [Explore](/documentation/reference/explore-settings).

## Table Viewer

Table Viewer is a data view in the Explore (legacy) workspace. It displays telemetry values from selected Channels in a tabular format. Each column represents a Channel. Rows are aligned with the time range currently visible in the line chart and update automatically when the chart is panned or zoomed.

### Settings

<MintTable
  columns={['Setting', 'Description', 'Default']}
  columnWidths={['25%', '55%', '20%']}
  rows={[
['Sync time to plot', 'Keeps Table Viewer synchronized with the line chart. When enabled, panning or zooming the chart updates the table to reflect the visible time range.', 'Enabled'],
['Include carried values', 'Displays the most recent known value for a Channel when no new data point exists at a given timestamp. Carried values are visually distinguished from actual values using lower opacity.', 'Enabled'],
['Row creation trigger', 'Determines when a column contributes to generating a new row. Configured per column. See Row creation trigger options below.', 'On sample'],
]}
/>

### Row creation trigger options

<MintTable
  columns={['Trigger', 'Description']}
  columnWidths={['25%', '75%']}
  rows={[
['On sample', 'A new row is created for every incoming sampled data point in the column.'],
['On change', 'A new row is created each time the column value changes.'],
['Reference only', 'The column does not generate its own rows. It fills values into rows created by other non-reference columns at the same timestamps.'],
]}
/>

### Filter operators

Filters are applied per column and are row-based. Active filters across columns are combined using logical AND.

<MintTable
  columns={['Operator', 'Description']}
  columnWidths={['25%', '75%']}
  rows={[
['`=`', 'Show rows where the value exactly matches the input.'],
['`≠`', 'Show rows where the value does not match the input.'],
['`>`', 'Show rows where the value is greater than the input.'],
['`<`', 'Show rows where the value is less than the input.'],
['`≥`', 'Show rows where the value is greater than or equal to the input.'],
['`≤`', 'Show rows where the value is less than or equal to the input.'],
['`Contains`', 'Show rows where the value includes the input substring. Applies to string data.'],
['`Doesn\'t contain`', 'Show rows where the value does not include the input substring.'],
]}
/>

### Export

The Table Viewer tab includes an **Export as CSV** button. Only data currently visible in the table, based on the active time range and filters, is included in the export.

## Parameterized URLs

The `/explore` route accepts query parameters to preload a specific Run, Assets, Channels, and time range when Explore (legacy) opens. This allows targeted analysis views to be constructed and shared without manual setup.

### URL structure

```
https://{domainName}/explore?run={runName} \
  &assets={asset1,asset2,...} \
  &channels={channel1,channel2,...} \
  &from={ISO8601} \
  &to={ISO8601}
```

* **Format**: Line breaks and backslashes (`\`) are shown for readability. In actual use, the URL must be a single continuous string joined with `&`, without spaces or backslashes.
* Parameters must be percent-encoded for proper URL handling.
* If `from` and `to` are not provided, the view defaults to the start and stop times of the provided Run.

### URL parameters

<MintTable
  columns={['Parameter', 'Type', 'Required', 'Description']}
  columnWidths={['15%', '20%', '10%', '55%']}
  rows={[
['`run`', 'String', 'No', 'The name of the Run to display. Scopes the view to a specific Run. When both `run` and `assets` are provided, `run` takes precedence and the Asset associated with the Run overrides any separately specified Assets.'],
['`assets`', 'String, String, …', 'No', 'Comma-separated list of Asset names to display. Can be used with or without a Run.'],
['`channels`', 'String, String, …', 'No', 'Comma-separated list of Channel names to plot.'],
['`from`', 'ISO 8601 UTC', 'No', 'Start of the time range to display. Defaults to the Run start time if not provided.'],
['`to`', 'ISO 8601 UTC', 'No', 'End of the time range to display. Defaults to the Run end time if not provided.'],
]}
/>

### Examples

**Run, Asset, and Channels**

```
https://app.siftstack.com/explore?run=run-name \
  &assets=asset-name \
  &channels=max_ground_temp%2Cmax_air_temp
```

**Run, Asset, Channels, and time range**

```
https://app.siftstack.com/explore?run=run-name \
  &assets=asset-name \
  &channels=max_ground_temp%2Cmax_air_temp \
  &from=2021-09-25T10%3A14%3A11 \
  &to=2021-10-14T13%3A29%3A37
```

**Run only**

```
https://app.siftstack.com/explore?run=run-name
```

**Multiple Assets with Channels and time range**

```
https://app.siftstack.com/explore?assets=asset-one-name%2Casset-two-name \
  &channels=max_air_temp%2Cmin_air_temp \
  &from=2021-09-25T10%3A14%3A11 \
  &to=2021-10-14T13%3A29%3A37
```
