Implements Phases 1-8 of the TFTSR implementation plan. Rust backend (Tauri 2.x, src-tauri/): - Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama - PII detection engine: 11 regex patterns with overlap resolution - SQLCipher AES-256 encrypted database with 10 versioned migrations - 28 Tauri IPC commands for triage, analysis, document, and system ops - Ollama: hardware probe, model recommendations, pull/delete with events - RCA and blameless post-mortem Markdown document generators - PDF export via printpdf - Audit log: SHA-256 hash of every external data send - Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2) Frontend (React 18 + TypeScript + Vite, src/): - 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings - 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives - 3 Zustand stores: session, settings (persisted), history - Type-safe tauriCommands.ts matching Rust backend types exactly - 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs) DevOps: - .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push - .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload Verified: - cargo check: zero errors - tsc --noEmit: zero errors - vitest run: 13/13 unit tests passing Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
155 lines
3.5 KiB
Plaintext
155 lines
3.5 KiB
Plaintext
import { LitElement, css, html } from 'lit'
|
|
<%- answers.isUsingTypeScript ? `import { customElement, property } from 'lit/decorators.js'\n` : ''
|
|
%>
|
|
/**
|
|
* An example element.
|
|
*
|
|
* @slot - This element has a slot
|
|
* @csspart button - The button
|
|
*/
|
|
<%- answers.isUsingTypeScript ? `@customElement('my-element')\n` : ''
|
|
%>export class MyElement extends LitElement {<%
|
|
if (answers.isUsingTypeScript) { %>
|
|
/**
|
|
* Copy for the read the docs hint.
|
|
*/
|
|
@property()
|
|
docsHint = 'Click on the WebdriverIO logo to learn more!'
|
|
|
|
/**
|
|
* The number of times the button has been clicked.
|
|
*/
|
|
@property({ type: Number })
|
|
count = 0
|
|
<% } else { %>
|
|
static get properties() {
|
|
return {
|
|
/**
|
|
* Copy for the read the docs hint.
|
|
*/
|
|
docsHint: { type: String },
|
|
|
|
/**
|
|
* The number of times the button has been clicked.
|
|
*/
|
|
count: { type: Number },
|
|
}
|
|
}
|
|
|
|
constructor() {
|
|
super()
|
|
this.docsHint = 'Click on the WebdriverIO logo to learn more!'
|
|
this.count = 0
|
|
}
|
|
<% } %>
|
|
render() {
|
|
return html`
|
|
<div>
|
|
<a href="https://webdriver.io/docs/component-testing" target="_blank">
|
|
<img src="https://webdriver.io/assets/images/robot-3677788dd63849c56aa5cb3f332b12d5.svg" className="logo"
|
|
alt="WebdriverIO logo" />
|
|
</a>
|
|
</div>
|
|
<h1>
|
|
<slot></slot>
|
|
</h1>
|
|
<div class="card">
|
|
<button @click=${this._onClick} part="button">
|
|
count is ${this.count}
|
|
</button>
|
|
<p>
|
|
Edit <code>src/Component.test.<%- answers.isUsingTypeScript ? `ts` : 'js' %></code> and save to test HMR
|
|
</p>
|
|
</div>
|
|
<p class="read-the-docs">${this.docsHint}</p>
|
|
`
|
|
}
|
|
|
|
<%- answers.isUsingTypeScript ? `private ` : ''%>_onClick() {
|
|
this.count++
|
|
}
|
|
|
|
<% if (answers.isUsingTypeScript) { %>static styles = css`<% } else { %>static get styles() {
|
|
return css`<% } %>
|
|
:host {
|
|
max-width: 1280px;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.logo {
|
|
height: 6em;
|
|
padding: 1.5em;
|
|
will-change: filter;
|
|
transition: filter 300ms;
|
|
}
|
|
.logo:hover {
|
|
filter: drop-shadow(0 0 2em #646cffaa);
|
|
}
|
|
.logo.lit:hover {
|
|
filter: drop-shadow(0 0 2em #325cffaa);
|
|
}
|
|
|
|
.card {
|
|
padding: 2em;
|
|
}
|
|
|
|
.read-the-docs {
|
|
color: #888;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 3.2em;
|
|
line-height: 1.1;
|
|
}
|
|
|
|
a {
|
|
font-weight: 500;
|
|
color: #646cff;
|
|
text-decoration: inherit;
|
|
}
|
|
a:hover {
|
|
color: #535bf2;
|
|
}
|
|
|
|
button {
|
|
border-radius: 8px;
|
|
border: 1px solid transparent;
|
|
padding: 0.6em 1.2em;
|
|
font-size: 1em;
|
|
font-weight: 500;
|
|
font-family: inherit;
|
|
background-color: #1a1a1a;
|
|
cursor: pointer;
|
|
transition: border-color 0.25s;
|
|
}
|
|
button:hover {
|
|
border-color: #646cff;
|
|
}
|
|
button:focus,
|
|
button:focus-visible {
|
|
outline: 4px auto -webkit-focus-ring-color;
|
|
}
|
|
|
|
@media (prefers-color-scheme: light) {
|
|
a:hover {
|
|
color: #747bff;
|
|
}
|
|
button {
|
|
background-color: #f9f9f9;
|
|
}
|
|
}
|
|
`
|
|
<% if (!answers.isUsingTypeScript) { %>}<% } %>
|
|
}
|
|
<% if (answers.isUsingTypeScript) { %>
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
'my-element': MyElement
|
|
}
|
|
}<%
|
|
} else { %>
|
|
window.customElements.define('my-element', MyElement)<%
|
|
} %>
|