@shapeshift-labs/frontier-lang-compiler 0.2.56 → 0.2.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -455,15 +455,20 @@ console.log(rustCandidate.sourceMap.mappings[0]?.semanticSymbolId); // generated
455
455
 
456
456
  `compileNativeSource` returns the import result, projection, target loss matrix cell, combined losses, readiness, evidence, output hash, and generated-output source maps. Same-language preserved output uses exact source mappings when the hash matches; generated stubs use declaration-level spans; adapter output uses adapter-supplied maps when present and otherwise gets an estimated fallback. Admission queues should treat `ok` as "code was emitted", not as merge approval; `readiness`, `targetCoverage`, and source-map precision carry the merge signal.
457
457
 
458
- Native projections and compile results also carry a `frontier.lang.nativeRoundtripEvidence` record under `evidence[].metadata.roundtripEvidence` and `metadata.roundtripEvidence`. That payload records import readiness/loss counts, universal AST validation and source-map precision, projection mode/hash verification, target adapter identity, target coverage, output source-map precision, and blocking/review loss ids in one coordinator-sortable JSON shape:
458
+ Native projections and compile results also carry a `frontier.lang.nativeRoundtripEvidence` record under `evidence[].metadata.roundtripEvidence` and `metadata.roundtripEvidence`. That payload records import readiness/loss counts, universal AST validation and source-map precision, projection mode/hash verification, target adapter identity, target coverage, output source-map precision, blocking/review loss ids, and a compact audit signal in one coordinator-sortable JSON shape:
459
459
 
460
460
  ```js
461
461
  const roundtrip = rustCandidate.metadata.roundtripEvidence;
462
462
  console.log(roundtrip.status); // "blocked", "stub-only", "preserved-source", or "target-adapter"
463
+ console.log(roundtrip.audit.disposition); // "reversible", "preserved-source", "stub-only", or "adapter-projected"
464
+ console.log(roundtrip.audit.claim); // bounded evidence label, not a semantic equivalence proof
465
+ console.log(roundtrip.audit.semanticEquivalenceClaim); // false
463
466
  console.log(roundtrip.output.sourceMaps.precision); // "exact", "declaration", "estimated", or "none"
464
467
  console.log(roundtrip.losses.blockingLossIds);
465
468
  ```
466
469
 
470
+ `roundtrip.audit` separates what was emitted from whether it can be merged: hash-verified same-language output with exact generated source maps is marked `reversible`, preserved source without generated exact maps stays `preserved-source`, generated declarations are `stub-only`, and host-owned target adapters are `adapter-projected`. Blocked and needs-review readiness still flow through `status`, `semanticMergeReadiness`, `reviewRequired`, loss ids, and source-map precision; the audit signal always keeps `autoMergeClaim: false` and `semanticEquivalenceClaim: false`.
471
+
467
472
  Provide a target projection adapter when the host owns real native-to-target translation semantics:
468
473
 
469
474
  ```js
@@ -107,6 +107,20 @@ export type NativeRoundtripEvidenceStatus = NativeImportRoundtripReadinessStatus
107
107
 
108
108
  export type NativeRoundtripSourceMapPrecision = SourceMapMappingRecord['precision'] | 'line' | 'declaration' | 'estimated' | 'unknown' | 'none' | string;
109
109
 
110
+ export type NativeRoundtripAuditDisposition =
111
+ | 'reversible'
112
+ | 'preserved-source'
113
+ | 'stub-only'
114
+ | 'adapter-projected'
115
+ | 'review-required';
116
+
117
+ export type NativeRoundtripAuditClaim =
118
+ | 'source-text-reversible'
119
+ | 'source-preserved'
120
+ | 'declaration-stubs-only'
121
+ | 'host-adapter-projected'
122
+ | 'review-required';
123
+
110
124
  export interface NativeRoundtripSourceMapEvidence {
111
125
  readonly total: number;
112
126
  readonly ids: readonly string[];
@@ -120,6 +134,30 @@ export interface NativeRoundtripSourceMapEvidence {
120
134
  readonly targetPaths: readonly string[];
121
135
  }
122
136
 
137
+ export interface NativeRoundtripAuditSignal {
138
+ readonly schema: 'frontier.lang.nativeRoundtripAuditSignal';
139
+ readonly version: 1;
140
+ readonly disposition: NativeRoundtripAuditDisposition;
141
+ readonly claim: NativeRoundtripAuditClaim;
142
+ readonly sourceLanguage?: FrontierSourceLanguage | string;
143
+ readonly target?: CompileTarget | string;
144
+ readonly sameLanguage: boolean;
145
+ readonly outputMode?: NativeSourceCompileOutputMode;
146
+ readonly projectionMode?: NativeSourceProjectionMode;
147
+ readonly sourceHashVerified: boolean;
148
+ readonly outputSourceMapPrecision: NativeRoundtripSourceMapPrecision;
149
+ readonly universalSourceMapPrecision: NativeRoundtripSourceMapPrecision;
150
+ readonly targetProjectionAdapterId?: string;
151
+ readonly targetCoverageLossClass?: ProjectionTargetLossClass | string;
152
+ readonly reviewRequired: boolean;
153
+ readonly semanticMergeReadiness: SemanticMergeReadiness;
154
+ readonly semanticEquivalenceClaim: false;
155
+ readonly autoMergeClaim: false;
156
+ readonly blockingLossCount: number;
157
+ readonly reviewLossCount: number;
158
+ readonly reasonCodes: readonly string[];
159
+ }
160
+
123
161
  export interface NativeRoundtripEvidenceMetadata {
124
162
  readonly status: NativeRoundtripEvidenceStatus;
125
163
  readonly semanticMergeReadiness: SemanticMergeReadiness;
@@ -127,6 +165,7 @@ export interface NativeRoundtripEvidenceMetadata {
127
165
  readonly sourcePath?: string;
128
166
  readonly language?: FrontierSourceLanguage | string;
129
167
  readonly target?: CompileTarget | string;
168
+ readonly audit: NativeRoundtripAuditSignal;
130
169
  readonly import: {
131
170
  readonly id?: string;
132
171
  readonly readiness: SemanticMergeReadiness;
@@ -0,0 +1,134 @@
1
+ import{createParadigmSemanticsLayer,createProofSpecLayer}from'@shapeshift-labs/frontier-lang-kernel';
2
+
3
+ export function createLightweightSemanticLayers(input) {
4
+ const evidenceIds = (input.evidence ?? []).map((record) => record.id).filter(Boolean);
5
+ const symbols = input.semanticIndex?.symbols ?? [];
6
+ const mappings = (input.sourceMaps ?? []).flatMap((sourceMap) => sourceMap.mappings ?? []);
7
+ const lossIds = (input.losses ?? []).map((loss) => loss.id).filter(Boolean);
8
+ const firstSubject = symbols[0]?.id ?? input.nativeSource?.id ?? input.nativeAst?.id;
9
+ const firstSubjectKind = symbols[0]?.id ? 'semanticSymbol' : input.nativeSource?.id ? 'nativeSource' : 'nativeAst';
10
+ const proof = createProofSpecLayer({
11
+ id: `proof_lightweight_${input.importIdPart}`,
12
+ invariants: [proofInvariant(input, firstSubject, firstSubjectKind, evidenceIds, lossIds)],
13
+ obligations: proofObligations(input, firstSubject, firstSubjectKind, evidenceIds, lossIds),
14
+ artifacts: [{
15
+ id: `proof_artifact_review_${input.importIdPart}`,
16
+ kind: 'manualReview',
17
+ evidenceIds,
18
+ lossIds,
19
+ summary: 'Lightweight import requires host parser, checker, tests, or human review before semantic merge.'
20
+ }],
21
+ assumptions: [{
22
+ id: `proof_assumption_lightweight_${input.importIdPart}`,
23
+ scope: 'native-import',
24
+ subjectId: input.nativeSource?.id,
25
+ subjectKind: 'nativeSource',
26
+ evidenceIds,
27
+ statement: 'Native source text and scanner evidence are preserved, but executable semantics are not proven.'
28
+ }],
29
+ evidence: input.evidence ?? []
30
+ });
31
+ const paradigmSemantics = createParadigmSemanticsLayer({
32
+ id: `paradigm_lightweight_${input.importIdPart}`,
33
+ bindingScopes: [{
34
+ id: `binding_scope_${input.importIdPart}`,
35
+ kind: 'moduleScope',
36
+ nativeSourceId: input.nativeSource?.id,
37
+ evidenceIds,
38
+ sourcePath: input.sourcePath,
39
+ sourceHash: input.sourceHash
40
+ }],
41
+ bindings: symbols.slice(0, 40).map((symbol) => ({
42
+ id: `binding_${symbol.id}`,
43
+ kind: 'nativeDeclarationBinding',
44
+ bindingScopeId: `binding_scope_${input.importIdPart}`,
45
+ semanticSymbolId: symbol.id,
46
+ nativeSourceId: input.nativeSource?.id,
47
+ evidenceIds
48
+ })),
49
+ evaluationModels: [evaluationModel(input, evidenceIds)],
50
+ effectRegions: effectRegions(input, evidenceIds),
51
+ loweringRecords: mappings.slice(0, 40).map((mapping) => ({
52
+ id: `lowering_${mapping.id}`,
53
+ kind: 'nativeSourceToFrontierSemanticIndex',
54
+ sourceMapId: mapping.sourceMapId,
55
+ sourceMapMappingId: mapping.id,
56
+ semanticSymbolId: mapping.semanticSymbolId,
57
+ nativeAstNodeId: mapping.nativeAstNodeId,
58
+ evidenceIds,
59
+ lossIds
60
+ })),
61
+ macroExpansions: macroBoundaryRecords(input, evidenceIds, lossIds),
62
+ reflectionBoundaries: reflectionBoundaryRecords(input, evidenceIds, lossIds),
63
+ evidence: input.evidence ?? []
64
+ });
65
+ return { proof, paradigmSemantics };
66
+ }
67
+
68
+ function proofInvariant(input, subjectId, subjectKind, evidenceIds, lossIds) {
69
+ return {
70
+ id: `proof_invariant_source_hash_${input.importIdPart}`,
71
+ kind: 'sourceHashFreshness',
72
+ subjectId,
73
+ subjectKind,
74
+ evidenceIds,
75
+ lossIds,
76
+ statement: 'Semantic merge candidates must match the preserved source hash before admission.'
77
+ };
78
+ }
79
+
80
+ function proofObligations(input, subjectId, subjectKind, evidenceIds, lossIds) {
81
+ return [
82
+ {
83
+ id: `proof_obligation_review_${input.importIdPart}`,
84
+ kind: 'semanticMergeReview',
85
+ status: 'open',
86
+ subjectId,
87
+ subjectKind,
88
+ contractIds: [`proof_invariant_source_hash_${input.importIdPart}`],
89
+ evidenceIds,
90
+ lossIds,
91
+ statement: 'Review ownership, source maps, losses, and focused tests before applying this semantic patch.'
92
+ },
93
+ {
94
+ id: `proof_obligation_external_semantics_${input.importIdPart}`,
95
+ kind: 'externalSemanticEquivalence',
96
+ status: 'external-tool-required',
97
+ subjectId,
98
+ subjectKind,
99
+ evidenceIds,
100
+ lossIds,
101
+ statement: 'Cross-language executable equivalence requires a target checker, oracle, or proof artifact.'
102
+ }
103
+ ];
104
+ }
105
+
106
+ function evaluationModel(input, evidenceIds) {
107
+ const language = String(input.language ?? 'unknown').toLowerCase();
108
+ const kind = ['javascript', 'typescript', 'python', 'ruby', 'php', 'lua'].includes(language)
109
+ ? 'dynamicRuntime'
110
+ : ['rust', 'c', 'cpp', 'c++', 'zig'].includes(language)
111
+ ? 'compileTimeOwnershipRuntime'
112
+ : 'languageRuntime';
113
+ return { id: `evaluation_model_${input.importIdPart}`, kind, nativeSourceId: input.nativeSource?.id, evidenceIds };
114
+ }
115
+
116
+ function effectRegions(input, evidenceIds) {
117
+ const predicates = new Set((input.semanticIndex?.relations ?? []).map((relation) => relation.predicate));
118
+ return [...predicates].filter((predicate) => ['calls', 'imports', 'uses', 'requires'].includes(predicate)).map((predicate) => ({
119
+ id: `effect_region_${input.importIdPart}_${predicate}`,
120
+ kind: `${predicate}DependencyEffect`,
121
+ nativeSourceId: input.nativeSource?.id,
122
+ evidenceIds
123
+ }));
124
+ }
125
+
126
+ function macroBoundaryRecords(input, evidenceIds, lossIds) {
127
+ const hasMacroLoss = (input.losses ?? []).some((loss) => ['macroExpansion', 'preprocessor', 'metaprogramming'].includes(loss.kind));
128
+ return hasMacroLoss ? [{ id: `macro_boundary_${input.importIdPart}`, kind: 'reviewRequiredMacroBoundary', nativeSourceId: input.nativeSource?.id, evidenceIds, lossIds }] : [];
129
+ }
130
+
131
+ function reflectionBoundaryRecords(input, evidenceIds, lossIds) {
132
+ const hasReflectionLoss = (input.losses ?? []).some((loss) => ['dynamicRuntime', 'reflection', 'opaqueNative'].includes(loss.kind));
133
+ return hasReflectionLoss ? [{ id: `reflection_boundary_${input.importIdPart}`, kind: 'reviewRequiredDynamicBoundary', nativeSourceId: input.nativeSource?.id, evidenceIds, lossIds }] : [];
134
+ }
@@ -24,7 +24,26 @@ export function createNativeRoundtripEvidence(importResult,options={}) {
24
24
  const outputMode=options.outputMode??options.targetProjection?.outputMode??projection?.mode;
25
25
  const sourceHashVerified=Boolean(projection?.sourceHash&&projection?.outputHash===projection.sourceHash)||projection?.metadata?.sourceHashVerified===true;
26
26
  const status=semanticMerge==='blocked'?'blocked':outputMode==='target-adapter'?'target-adapter':projection?.mode==='native-source-stubs'||outputMode==='target-stubs'?'stub-only':sourceHashVerified?'preserved-source':'needs-review';
27
+ const universalSourceMapEvidence=summarizeSourceMaps(universalSourceMaps);
27
28
  const sourceMapEvidence=summarizeSourceMaps(hasOutputSourceMaps?outputSourceMaps:universalSourceMaps);
29
+ const sourceLanguage=projection?.language??importResult.language;
30
+ const target=options.target??options.targetCoverage?.target??options.targetProjection?.target;
31
+ const audit=createRoundtripAudit({
32
+ status,
33
+ semanticMerge,
34
+ sourceLanguage,
35
+ target,
36
+ sameLanguage:Boolean(sourceLanguage&&target&&String(sourceLanguage)===String(target)),
37
+ outputMode,
38
+ projectionMode:projection?.mode,
39
+ sourceHashVerified,
40
+ hasOutputSourceMaps,
41
+ sourceMapEvidence,
42
+ universalSourceMapEvidence,
43
+ targetProjection:options.targetProjection,
44
+ targetCoverage:options.targetCoverage,
45
+ lossSummary
46
+ });
28
47
  const metadata={
29
48
  schema:'frontier.lang.nativeRoundtripEvidence',
30
49
  version:1,
@@ -33,8 +52,9 @@ export function createNativeRoundtripEvidence(importResult,options={}) {
33
52
  semanticMergeReadiness:semanticMerge,
34
53
  reviewRequired:semanticMerge!=='ready',
35
54
  sourcePath:projection?.sourcePath??importResult.sourcePath,
36
- language:projection?.language??importResult.language,
37
- target:options.target??options.targetCoverage?.target??options.targetProjection?.target,
55
+ language:sourceLanguage,
56
+ target,
57
+ audit,
38
58
  import:{
39
59
  id:importResult.id,
40
60
  readiness:importReadiness,
@@ -49,7 +69,7 @@ export function createNativeRoundtripEvidence(importResult,options={}) {
49
69
  issues:universalIssues,
50
70
  nativeSources:universalAst?.nativeSources?.length??importResult.nativeSources?.length??(importResult.nativeSource?1:0),
51
71
  semanticSymbols:(importResult.semanticIndex??universalAst?.semanticIndex)?.symbols?.length??0,
52
- sourceMaps:summarizeSourceMaps(universalSourceMaps)
72
+ sourceMaps:universalSourceMapEvidence
53
73
  },
54
74
  projection:{
55
75
  id:projection?.id,
@@ -115,3 +135,55 @@ function summarizeSourceMaps(sourceMaps){
115
135
  }
116
136
  function normalizePrecision(value){const precision=String(value??'unknown');return Object.prototype.hasOwnProperty.call(precisionRank,precision)?precision:'unknown';}
117
137
  function worstPrecision(precisions){if(!precisions.length)return'none';return precisions.reduce((worst,next)=>precisionRank[next]>precisionRank[worst]?next:worst,'exact');}
138
+ function createRoundtripAudit(input){
139
+ const disposition=roundtripAuditDisposition(input);
140
+ return{
141
+ schema:'frontier.lang.nativeRoundtripAuditSignal',
142
+ version:1,
143
+ disposition,
144
+ claim:roundtripAuditClaim(disposition),
145
+ sourceLanguage:input.sourceLanguage,
146
+ target:input.target,
147
+ sameLanguage:input.sameLanguage,
148
+ outputMode:input.outputMode,
149
+ projectionMode:input.projectionMode,
150
+ sourceHashVerified:input.sourceHashVerified,
151
+ outputSourceMapPrecision:input.sourceMapEvidence.precision,
152
+ universalSourceMapPrecision:input.universalSourceMapEvidence.precision,
153
+ targetProjectionAdapterId:input.targetProjection?.adapter?.id,
154
+ targetCoverageLossClass:input.targetCoverage?.lossClass,
155
+ reviewRequired:input.semanticMerge!=='ready',
156
+ semanticMergeReadiness:input.semanticMerge,
157
+ semanticEquivalenceClaim:false,
158
+ autoMergeClaim:false,
159
+ blockingLossCount:input.lossSummary.blockingLossIds.length,
160
+ reviewLossCount:input.lossSummary.reviewLossIds.length,
161
+ reasonCodes:uniqueStrings([
162
+ `status:${input.status}`,
163
+ `semantic:${input.semanticMerge}`,
164
+ `output:${input.outputMode??'unknown'}`,
165
+ `projection:${input.projectionMode??'unknown'}`,
166
+ input.sourceHashVerified?'source-hash:verified':'source-hash:unverified',
167
+ `output-source-map:${input.sourceMapEvidence.precision}`,
168
+ `universal-source-map:${input.universalSourceMapEvidence.precision}`,
169
+ input.targetCoverage?.lossClass?`target-loss:${input.targetCoverage.lossClass}`:undefined,
170
+ input.targetProjection?.adapter?.id?`target-adapter:${input.targetProjection.adapter.id}`:undefined,
171
+ input.lossSummary.blockingLossIds.length?'losses:blocking':undefined,
172
+ input.lossSummary.reviewLossIds.length?'losses:review':undefined
173
+ ])
174
+ };
175
+ }
176
+ function roundtripAuditDisposition(input){
177
+ if(input.outputMode==='target-adapter')return'adapter-projected';
178
+ if(input.projectionMode==='native-source-stubs'||input.outputMode==='target-stubs')return'stub-only';
179
+ if(input.sourceHashVerified&&input.outputMode==='preserved-source'&&input.hasOutputSourceMaps&&input.sourceMapEvidence.precision==='exact')return'reversible';
180
+ if(input.sourceHashVerified||input.projectionMode==='preserved-source')return'preserved-source';
181
+ return'review-required';
182
+ }
183
+ function roundtripAuditClaim(disposition){
184
+ if(disposition==='reversible')return'source-text-reversible';
185
+ if(disposition==='preserved-source')return'source-preserved';
186
+ if(disposition==='stub-only')return'declaration-stubs-only';
187
+ if(disposition==='adapter-projected')return'host-adapter-projected';
188
+ return'review-required';
189
+ }
@@ -1,5 +1,6 @@
1
1
  import{commonGeneratedTargetPath,idFragment}from'../../native-import-utils.js';import{inferSourceMapMappings,normalizeSourceMapMappings,normalizeSourceMaps}from'../../native-source-maps.js';import{createKernelSourcePreservationRecords,summarizeKernelSourcePreservationRecords}from'../../semantic-import-source-preservation.js';import{createDocument,createImportResult,createNativeAstRecord,createPatch,createSourceMapRecord,createUniversalAstEnvelope,hashSemanticValue,nativeSourceNode}from'@shapeshift-labs/frontier-lang-kernel';
2
2
  import{attachInputUniversalDialectRegistry}from'../../universal-dialect-registry.js';
3
+ import{createLightweightSemanticLayers}from'./createLightweightSemanticLayers.js';
3
4
  import{attachNativeImportLossSummary}from'./attachNativeImportLossSummary.js';import{createLightweightNativeImport}from'./createLightweightNativeImport.js';import{createNativeSourcePreservation}from'./createNativeSourcePreservation.js';import{hasNativeExactAstEvidence}from'./hasNativeExactAstEvidence.js';import{normalizeNativeLossRecords}from'./normalizeNativeLossRecords.js';import{summarizeNativeImportLosses}from'./summarizeNativeImportLosses.js';import{unverifiedNativeAstLosses}from'./unverifiedNativeAstLosses.js';import{withNativeImportReadiness}from'./withNativeImportReadiness.js';
4
5
  export function importNativeSource(input) {
5
6
  const language = input.language ?? input.nativeAst?.language;
@@ -216,6 +217,7 @@ export function importNativeSource(input) {
216
217
  });
217
218
  const kernelSourcePreservationSummary = summarizeKernelSourcePreservationRecords(sourcePreservationRecords);
218
219
  const resultSourceMapMappings = sourceMaps.flatMap((sourceMap) => sourceMap.mappings ?? []);
220
+ const semanticLayers=input.semanticLayers??createLightweightSemanticLayers({importIdPart,language,sourcePath,sourceHash,nativeSource,nativeAst,semanticIndex,sourceMaps,losses,evidence,sourcePreservationRecords});
219
221
  let universalAst = createUniversalAstEnvelope({
220
222
  id: input.universalAstId ?? `universal_ast_${importIdPart}`,
221
223
  document,
@@ -224,6 +226,7 @@ export function importNativeSource(input) {
224
226
  sourceMaps,
225
227
  losses,
226
228
  evidence,
229
+ proof:input.proof??input.universalAstProof??semanticLayers.proof,paradigmSemantics:input.paradigmSemantics??input.universalAstParadigmSemantics??semanticLayers.paradigmSemantics,
227
230
  metadata: {
228
231
  sourceLanguage: language,
229
232
  sourcePath,
@@ -41,7 +41,9 @@ export function renderWorkbenchHtml(initialState) {
41
41
  <h2>TypeScript</h2>
42
42
  <span id="typescriptStatus">source</span>
43
43
  </div>
44
- <textarea id="typescriptInput" spellcheck="false" aria-label="TypeScript source"></textarea>
44
+ <div class="paneBody editorBody">
45
+ <textarea id="typescriptInput" spellcheck="false" aria-label="TypeScript source"></textarea>
46
+ </div>
45
47
  </section>
46
48
 
47
49
  <section class="pane graphPane" data-view-panel="frontier">
@@ -52,8 +54,8 @@ export function renderWorkbenchHtml(initialState) {
52
54
  <button data-frontier-tab="json" aria-pressed="false">JSON</button>
53
55
  </div>
54
56
  </div>
55
- <div id="graphView" class="graphView"></div>
56
- <pre id="frontierJson" class="codeBlock" hidden></pre>
57
+ <div id="graphView" class="paneBody graphView"></div>
58
+ <pre id="frontierJson" class="paneBody codeBlock" hidden></pre>
57
59
  </section>
58
60
 
59
61
  <section class="pane outputPane" data-view-panel="rust">
@@ -61,7 +63,9 @@ export function renderWorkbenchHtml(initialState) {
61
63
  <h2>Rust</h2>
62
64
  <span id="rustStatus">target</span>
63
65
  </div>
64
- <textarea id="rustInput" spellcheck="false" aria-label="Rust source"></textarea>
66
+ <div class="paneBody editorBody">
67
+ <textarea id="rustInput" spellcheck="false" aria-label="Rust source"></textarea>
68
+ </div>
65
69
  </section>
66
70
  </section>
67
71
  </main>
@@ -2,6 +2,9 @@ export function workbenchStyles() {
2
2
  return `
3
3
  :root {
4
4
  color-scheme: dark;
5
+ --topbar-height: 58px;
6
+ --status-height: 46px;
7
+ --chrome-height: calc(var(--topbar-height) + var(--status-height));
5
8
  --bg: #08090a;
6
9
  --panel: #111417;
7
10
  --panel-2: #171b20;
@@ -22,6 +25,7 @@ export function workbenchStyles() {
22
25
  * { box-sizing: border-box; }
23
26
  html, body { height: 100%; margin: 0; }
24
27
  body {
28
+ width: 100%;
25
29
  background: var(--bg);
26
30
  color: var(--text);
27
31
  font-family: var(--ui);
@@ -29,9 +33,9 @@ body {
29
33
  }
30
34
 
31
35
  .appShell {
32
- min-height: 100vh;
33
- display: grid;
34
- grid-template-rows: 58px 46px minmax(0, 1fr);
36
+ height: 100vh;
37
+ height: 100dvh;
38
+ overflow: hidden;
35
39
  }
36
40
 
37
41
  .topbar, .statusStrip, .paneHeader {
@@ -41,6 +45,10 @@ body {
41
45
  }
42
46
 
43
47
  .topbar {
48
+ position: fixed;
49
+ inset: 0 0 auto 0;
50
+ z-index: 20;
51
+ height: var(--topbar-height);
44
52
  justify-content: space-between;
45
53
  padding: 0 14px;
46
54
  background: #0c0e10;
@@ -84,38 +92,75 @@ body {
84
92
  .iconButton:hover, .segmented button:hover, .modeGroup button:hover, .runButton:hover { background: var(--panel-3); }
85
93
 
86
94
  .statusStrip {
95
+ position: fixed;
96
+ inset: var(--topbar-height) 0 auto 0;
97
+ z-index: 19;
98
+ height: var(--status-height);
87
99
  display: grid;
88
100
  grid-template-columns: repeat(4, minmax(0, 1fr));
89
101
  background: #0f1214;
102
+ overflow: hidden;
90
103
  }
91
104
  .statusStrip div {
92
105
  min-width: 0;
106
+ min-height: 0;
93
107
  padding: 8px 14px;
94
108
  border-right: 1px solid var(--line);
109
+ overflow: hidden;
95
110
  }
96
111
  .label {
97
112
  display: block;
98
113
  color: var(--faint);
99
114
  font-size: 10px;
115
+ line-height: 1.1;
100
116
  text-transform: uppercase;
101
117
  }
102
- .statusStrip strong { font-size: 13px; font-weight: 650; }
118
+ .statusStrip strong {
119
+ display: block;
120
+ overflow: hidden;
121
+ text-overflow: ellipsis;
122
+ white-space: nowrap;
123
+ font-size: 13px;
124
+ font-weight: 650;
125
+ line-height: 1.2;
126
+ }
103
127
 
104
128
  .workspace {
129
+ position: fixed;
130
+ inset: var(--chrome-height) 0 0 0;
131
+ height: calc(100vh - var(--chrome-height));
132
+ height: calc(100dvh - var(--chrome-height));
105
133
  min-height: 0;
106
134
  display: grid;
107
135
  grid-template-columns: minmax(270px, 0.95fr) minmax(330px, 1.1fr) minmax(300px, 1fr);
136
+ overflow: hidden;
108
137
  }
109
138
  .pane {
110
139
  min-width: 0;
111
140
  min-height: 0;
141
+ height: 100%;
112
142
  display: grid;
113
143
  grid-template-rows: 44px minmax(0, 1fr);
114
144
  border-right: 1px solid var(--line);
115
145
  background: var(--panel);
146
+ overflow: hidden;
116
147
  }
117
148
  .pane:last-child { border-right: 0; }
118
149
  .pane.isSource .paneHeader { box-shadow: inset 0 -2px 0 var(--green); }
150
+ .paneBody {
151
+ grid-row: 2;
152
+ grid-column: 1;
153
+ min-width: 0;
154
+ min-height: 0;
155
+ height: 100%;
156
+ max-height: 100%;
157
+ overflow: auto;
158
+ overscroll-behavior: contain;
159
+ scrollbar-gutter: stable;
160
+ }
161
+ .editorBody {
162
+ overflow: hidden;
163
+ }
119
164
  .paneHeader {
120
165
  justify-content: space-between;
121
166
  padding: 0 12px;
@@ -125,9 +170,11 @@ body {
125
170
  .paneHeader span { color: var(--muted); font-size: 12px; font-family: var(--mono); }
126
171
 
127
172
  textarea, .codeBlock {
173
+ display: block;
128
174
  width: 100%;
129
175
  height: 100%;
130
176
  min-height: 0;
177
+ max-height: 100%;
131
178
  margin: 0;
132
179
  border: 0;
133
180
  resize: none;
@@ -139,14 +186,16 @@ textarea, .codeBlock {
139
186
  line-height: 1.48;
140
187
  padding: 14px;
141
188
  overflow: auto;
189
+ overscroll-behavior: contain;
142
190
  white-space: pre;
143
191
  }
144
192
 
145
193
  .graphView {
146
- min-height: 0;
147
- overflow: auto;
148
194
  padding: 12px;
149
195
  }
196
+ #graphView[hidden], #frontierJson[hidden] {
197
+ display: none;
198
+ }
150
199
  .graphGrid {
151
200
  display: grid;
152
201
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
@@ -230,12 +279,28 @@ textarea, .codeBlock {
230
279
  .segmented button[aria-pressed="true"] { background: #213127; color: var(--green); }
231
280
 
232
281
  @media (max-width: 980px) {
233
- body { overflow: auto; }
234
- .appShell { min-height: 100vh; grid-template-rows: auto auto auto; }
235
- .workspace { grid-template-columns: 1fr; }
236
- .pane { min-height: 360px; border-right: 0; border-bottom: 1px solid var(--line); }
282
+ :root {
283
+ --topbar-height: 96px;
284
+ --status-height: 88px;
285
+ }
286
+ body { overflow: hidden; }
287
+ .topbar {
288
+ align-items: flex-start;
289
+ gap: 10px;
290
+ padding: 10px 14px;
291
+ }
292
+ .workspace {
293
+ grid-template-columns: 1fr;
294
+ grid-template-rows: repeat(3, minmax(0, 1fr));
295
+ overflow: hidden;
296
+ }
297
+ .pane {
298
+ height: 100%;
299
+ min-height: 0;
300
+ border-right: 0;
301
+ border-bottom: 1px solid var(--line);
302
+ }
237
303
  .statusStrip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
238
- .topbar { align-items: flex-start; gap: 10px; padding: 10px 14px; }
239
304
  .topActions { flex-wrap: wrap; justify-content: flex-end; }
240
305
  .boundsGrid { grid-template-columns: 1fr; }
241
306
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.56",
3
+ "version": "0.2.58",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",