feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Problem: the hexagonal refactor churns the backend tree for nine more phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper, compose/web image) must move to its own repo first so doc/layout rewrites land once on a backend-only tree. Change: - New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/ (updateURL repointed to oikos-web releases), compose/, own CI (web + desktop jobs), own deploy script (CI-green gate, TOCTOU guard, version-tagged images, prune-to-3), own webhook receiver on :9798 + launchd unit, own compose project publishing the same 8091:80. - Cutover executed on mac-mini in order: oikos stack's web service stopped+removed, oikos-web project brought up on 8091; outer Caddy untouched (targets the published port) — serving + Authentik flow + /wails 404 quirk verified post-cutover. - Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install targets, the compose web service, oikos-web from deploy.sh's fallback prune list; wails + go-keyring dropped from go.mod, vendor synced. - README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now point at the new repo; mbse + mascot design docs carry a path note. Risk: production SPA serving depends on the new pipeline now; rollback is versioned-image re-up of the old web service from a pre-split checkout (port 8091). Desktop builds installed before the split still check dtoro/oikos releases — one manual reinstall, noted in the oikos-web release notes. Verification: go vet, make test (race), make generate-check, golangci (no new findings; baseline down 400→365); post-cutover curls — localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
This commit is contained in:
7
web/node_modules/bits-ui/dist/internal/floating-svelte/floating-utils.svelte.d.ts
generated
vendored
Normal file
7
web/node_modules/bits-ui/dist/internal/floating-svelte/floating-utils.svelte.d.ts
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { MaybeGetter } from "svelte-toolbelt";
|
||||
export declare function get<T>(valueOrGetValue: MaybeGetter<T>): T;
|
||||
export declare function getDPR(element: Element): number;
|
||||
export declare function roundByDPR(element: Element, value: number): number;
|
||||
export declare function getFloatingContentCSSVars(name: string): {
|
||||
[x: string]: string;
|
||||
};
|
||||
24
web/node_modules/bits-ui/dist/internal/floating-svelte/floating-utils.svelte.js
generated
vendored
Normal file
24
web/node_modules/bits-ui/dist/internal/floating-svelte/floating-utils.svelte.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
export function get(valueOrGetValue) {
|
||||
return typeof valueOrGetValue === "function"
|
||||
? valueOrGetValue()
|
||||
: valueOrGetValue;
|
||||
}
|
||||
export function getDPR(element) {
|
||||
if (typeof window === "undefined")
|
||||
return 1;
|
||||
const win = element.ownerDocument.defaultView || window;
|
||||
return win.devicePixelRatio || 1;
|
||||
}
|
||||
export function roundByDPR(element, value) {
|
||||
const dpr = getDPR(element);
|
||||
return Math.round(value * dpr) / dpr;
|
||||
}
|
||||
export function getFloatingContentCSSVars(name) {
|
||||
return {
|
||||
[`--bits-${name}-content-transform-origin`]: `var(--bits-floating-transform-origin)`,
|
||||
[`--bits-${name}-content-available-width`]: `var(--bits-floating-available-width)`,
|
||||
[`--bits-${name}-content-available-height`]: `var(--bits-floating-available-height)`,
|
||||
[`--bits-${name}-anchor-width`]: `var(--bits-floating-anchor-width)`,
|
||||
[`--bits-${name}-anchor-height`]: `var(--bits-floating-anchor-height)`,
|
||||
};
|
||||
}
|
||||
97
web/node_modules/bits-ui/dist/internal/floating-svelte/types.d.ts
generated
vendored
Normal file
97
web/node_modules/bits-ui/dist/internal/floating-svelte/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
import type { FloatingElement, Middleware, MiddlewareData, Placement, ReferenceElement, Strategy } from "@floating-ui/dom";
|
||||
import type { ReadableBox, WritableBox } from "svelte-toolbelt";
|
||||
type ValueOrGetValue<T> = T | (() => T);
|
||||
export type Measurable = {
|
||||
getBoundingClientRect: () => DOMRect;
|
||||
};
|
||||
export type UseFloatingOptions = {
|
||||
/**
|
||||
* Represents the open/close state of the floating element.
|
||||
* @default true
|
||||
*/
|
||||
open?: ValueOrGetValue<boolean | undefined>;
|
||||
/**
|
||||
* Where to place the floating element relative to its reference element.
|
||||
* @default 'bottom'
|
||||
*/
|
||||
placement?: ValueOrGetValue<Placement | undefined>;
|
||||
/**
|
||||
* The type of CSS position property to use.
|
||||
* @default 'absolute'
|
||||
*/
|
||||
strategy?: ValueOrGetValue<Strategy | undefined>;
|
||||
/**
|
||||
* These are plain objects that modify the positioning coordinates in some fashion,
|
||||
* or provide useful data for the consumer to use.
|
||||
* @default undefined
|
||||
*/
|
||||
middleware?: ValueOrGetValue<Middleware[] | undefined>;
|
||||
/**
|
||||
* Whether to use `transform` instead of `top` and `left` styles to
|
||||
* position the floating element (`floatingStyles`).
|
||||
* @default true
|
||||
*/
|
||||
transform?: ValueOrGetValue<boolean | undefined>;
|
||||
/**
|
||||
* Reference / Anchor element to position the floating element relative to
|
||||
*/
|
||||
reference: ReadableBox<Measurable | HTMLElement | null>;
|
||||
/**
|
||||
* Callback to handle mounting/unmounting of the elements.
|
||||
* @default undefined
|
||||
*/
|
||||
whileElementsMounted?: (reference: ReferenceElement, floating: FloatingElement, update: () => void) => () => void;
|
||||
/**
|
||||
* The offset from the reference element along the side axis.
|
||||
* Used to detect bad coordinates during transitions.
|
||||
* @default undefined
|
||||
*/
|
||||
sideOffset?: ValueOrGetValue<number | undefined>;
|
||||
/**
|
||||
* The offset from the reference element along the alignment axis.
|
||||
* Used to detect bad coordinates during transitions.
|
||||
* @default undefined
|
||||
*/
|
||||
alignOffset?: ValueOrGetValue<number | undefined>;
|
||||
};
|
||||
export type UseFloatingReturn = {
|
||||
/**
|
||||
* The reference element to position the floating element relative to.
|
||||
*/
|
||||
reference: ReadableBox<Measurable | HTMLElement | null>;
|
||||
/**
|
||||
* The floating element to position.
|
||||
*/
|
||||
floating: WritableBox<HTMLElement | null>;
|
||||
/**
|
||||
* The stateful placement, which can be different from the initial `placement` passed as options.
|
||||
*/
|
||||
placement: Readonly<Placement>;
|
||||
/**
|
||||
* The type of CSS position property to use.
|
||||
*/
|
||||
strategy: Readonly<Strategy>;
|
||||
/**
|
||||
* Additional data from middleware.
|
||||
*/
|
||||
middlewareData: Readonly<MiddlewareData>;
|
||||
/**
|
||||
* The boolean that let you know if the floating element has been positioned.
|
||||
*/
|
||||
isPositioned: Readonly<boolean>;
|
||||
/**
|
||||
* CSS styles to apply to the floating element to position it.
|
||||
*/
|
||||
floatingStyles: Readonly<{
|
||||
position: Strategy;
|
||||
top: string;
|
||||
left: string;
|
||||
transform?: string;
|
||||
willChange?: string;
|
||||
}>;
|
||||
/**
|
||||
* The function to update floating position manually.
|
||||
*/
|
||||
update: () => void;
|
||||
};
|
||||
export {};
|
||||
1
web/node_modules/bits-ui/dist/internal/floating-svelte/types.js
generated
vendored
Normal file
1
web/node_modules/bits-ui/dist/internal/floating-svelte/types.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
2
web/node_modules/bits-ui/dist/internal/floating-svelte/use-floating.svelte.d.ts
generated
vendored
Normal file
2
web/node_modules/bits-ui/dist/internal/floating-svelte/use-floating.svelte.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
import type { UseFloatingOptions, UseFloatingReturn } from "./types.js";
|
||||
export declare function useFloating(options: UseFloatingOptions): UseFloatingReturn;
|
||||
192
web/node_modules/bits-ui/dist/internal/floating-svelte/use-floating.svelte.js
generated
vendored
Normal file
192
web/node_modules/bits-ui/dist/internal/floating-svelte/use-floating.svelte.js
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
import { computePosition } from "@floating-ui/dom";
|
||||
import { simpleBox } from "svelte-toolbelt";
|
||||
import { get, getDPR, roundByDPR } from "./floating-utils.svelte.js";
|
||||
export function useFloating(options) {
|
||||
/** Options */
|
||||
const whileElementsMountedOption = options.whileElementsMounted;
|
||||
const openOption = $derived(get(options.open) ?? true);
|
||||
const middlewareOption = $derived(get(options.middleware));
|
||||
const transformOption = $derived(get(options.transform) ?? true);
|
||||
const placementOption = $derived(get(options.placement) ?? "bottom");
|
||||
const strategyOption = $derived(get(options.strategy) ?? "absolute");
|
||||
const sideOffsetOption = $derived(get(options.sideOffset) ?? 0);
|
||||
const alignOffsetOption = $derived(get(options.alignOffset) ?? 0);
|
||||
const reference = options.reference;
|
||||
/** State */
|
||||
let x = $state(0);
|
||||
let y = $state(0);
|
||||
const floating = simpleBox(null);
|
||||
// svelte-ignore state_referenced_locally
|
||||
let strategy = $state(strategyOption);
|
||||
// svelte-ignore state_referenced_locally
|
||||
let placement = $state(placementOption);
|
||||
let middlewareData = $state({});
|
||||
let isPositioned = $state(false);
|
||||
let hasWhileMountedPosition = false;
|
||||
let updateRequestId = 0;
|
||||
const floatingStyles = $derived.by(() => {
|
||||
// preserve last known position when floating ref is null (during transitions)
|
||||
const xVal = floating.current ? roundByDPR(floating.current, x) : x;
|
||||
const yVal = floating.current ? roundByDPR(floating.current, y) : y;
|
||||
if (transformOption) {
|
||||
return {
|
||||
position: strategy,
|
||||
left: "0",
|
||||
top: "0",
|
||||
transform: `translate(${xVal}px, ${yVal}px)`,
|
||||
...(floating.current &&
|
||||
getDPR(floating.current) >= 1.5 && {
|
||||
willChange: "transform",
|
||||
}),
|
||||
};
|
||||
}
|
||||
return {
|
||||
position: strategy,
|
||||
left: `${xVal}px`,
|
||||
top: `${yVal}px`,
|
||||
};
|
||||
});
|
||||
/** Effects */
|
||||
let whileElementsMountedCleanup;
|
||||
function update() {
|
||||
if (reference.current === null || floating.current === null)
|
||||
return;
|
||||
const referenceNode = reference.current;
|
||||
const floatingNode = floating.current;
|
||||
const requestId = ++updateRequestId;
|
||||
computePosition(referenceNode, floatingNode, {
|
||||
middleware: middlewareOption,
|
||||
placement: placementOption,
|
||||
strategy: strategyOption,
|
||||
}).then((position) => {
|
||||
// ignore stale async resolutions when newer updates were requested.
|
||||
if (requestId !== updateRequestId)
|
||||
return;
|
||||
// ignore stale resolutions after ref replacement.
|
||||
if (reference.current !== referenceNode || floating.current !== floatingNode)
|
||||
return;
|
||||
const referenceHidden = isReferenceHidden(referenceNode);
|
||||
if (referenceHidden) {
|
||||
// keep last good coordinates when the anchor disappears to avoid
|
||||
// a transient jump to viewport origin before close propagates.
|
||||
middlewareData = {
|
||||
...middlewareData,
|
||||
hide: {
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
...middlewareData.hide,
|
||||
referenceHidden: true,
|
||||
},
|
||||
};
|
||||
return;
|
||||
}
|
||||
// ignore bad coordinates that cause jumping during close transitions
|
||||
if (!openOption && x !== 0 && y !== 0) {
|
||||
// if we had a good position and now getting coordinates near
|
||||
// the expected offset bounds during close, ignore it
|
||||
const maxExpectedOffset = Math.max(Math.abs(sideOffsetOption), Math.abs(alignOffsetOption), 15);
|
||||
if (position.x <= maxExpectedOffset && position.y <= maxExpectedOffset)
|
||||
return;
|
||||
}
|
||||
x = position.x;
|
||||
y = position.y;
|
||||
strategy = position.strategy;
|
||||
placement = position.placement;
|
||||
middlewareData = position.middlewareData;
|
||||
isPositioned = true;
|
||||
});
|
||||
}
|
||||
function cleanup() {
|
||||
if (typeof whileElementsMountedCleanup === "function") {
|
||||
whileElementsMountedCleanup();
|
||||
whileElementsMountedCleanup = undefined;
|
||||
}
|
||||
updateRequestId++;
|
||||
}
|
||||
function attach() {
|
||||
cleanup();
|
||||
if (whileElementsMountedOption === undefined) {
|
||||
update();
|
||||
return;
|
||||
}
|
||||
if (!openOption)
|
||||
return;
|
||||
if (reference.current === null || floating.current === null)
|
||||
return;
|
||||
whileElementsMountedCleanup = whileElementsMountedOption(reference.current, floating.current, update);
|
||||
}
|
||||
function reset() {
|
||||
if (!openOption && floating.current === null) {
|
||||
isPositioned = false;
|
||||
}
|
||||
}
|
||||
function trackWhileMountedDeps() {
|
||||
return [
|
||||
middlewareOption,
|
||||
placementOption,
|
||||
strategyOption,
|
||||
sideOffsetOption,
|
||||
alignOffsetOption,
|
||||
openOption,
|
||||
];
|
||||
}
|
||||
$effect(() => {
|
||||
if (whileElementsMountedOption !== undefined)
|
||||
return;
|
||||
if (!openOption)
|
||||
return;
|
||||
update();
|
||||
});
|
||||
$effect(attach);
|
||||
$effect(() => {
|
||||
if (whileElementsMountedOption === undefined)
|
||||
return;
|
||||
trackWhileMountedDeps();
|
||||
if (!openOption) {
|
||||
hasWhileMountedPosition = false;
|
||||
return;
|
||||
}
|
||||
if (!isPositioned) {
|
||||
hasWhileMountedPosition = false;
|
||||
return;
|
||||
}
|
||||
// skip the first post-position run, since autoUpdate already computed it
|
||||
if (!hasWhileMountedPosition) {
|
||||
hasWhileMountedPosition = true;
|
||||
return;
|
||||
}
|
||||
update();
|
||||
});
|
||||
$effect(reset);
|
||||
$effect(() => cleanup);
|
||||
return {
|
||||
floating,
|
||||
reference,
|
||||
get strategy() {
|
||||
return strategy;
|
||||
},
|
||||
get placement() {
|
||||
return placement;
|
||||
},
|
||||
get middlewareData() {
|
||||
return middlewareData;
|
||||
},
|
||||
get isPositioned() {
|
||||
return isPositioned;
|
||||
},
|
||||
get floatingStyles() {
|
||||
return floatingStyles;
|
||||
},
|
||||
get update() {
|
||||
return update;
|
||||
},
|
||||
};
|
||||
}
|
||||
function isReferenceHidden(node) {
|
||||
if (!(node instanceof Element))
|
||||
return false;
|
||||
if (!node.isConnected)
|
||||
return true;
|
||||
if (node instanceof HTMLElement && node.hidden)
|
||||
return true;
|
||||
return node.getClientRects().length === 0;
|
||||
}
|
||||
Reference in New Issue
Block a user