Skip to content

Commit ebce717

Browse files
committed
fix: Update npm package to speed up layout generation
1 parent 1e61e35 commit ebce717

17 files changed

+124
-91
lines changed

web/components/BomTab.vue

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script lang="ts" setup>
2+
import type { BoardLayoutLeftover } from '@aklinker1/cutlist';
3+
24
const { data: doc } = useDocumentQuery();
35
const { data, isLoading } = useBoardLayoutsQuery();
46
const distanceUnit = useDistanceUnit();
@@ -8,11 +10,9 @@ const rows = computed(() => {
810
if (data.value == null) return [];
911
1012
const map = [
11-
...data.value?.layouts.flatMap((layout) =>
12-
layout.placements.map((p) => p.data),
13-
),
13+
...data.value?.layouts.flatMap((layout) => layout.placements),
1414
...data.value?.leftovers,
15-
].reduce<Map<number, PartToCut[]>>((acc, part) => {
15+
].reduce<Map<number, BoardLayoutLeftover[]>>((acc, part) => {
1616
const items = acc.get(part.partNumber) ?? [];
1717
items.push(part);
1818
acc.set(part.partNumber, items);
@@ -28,7 +28,7 @@ const rows = computed(() => {
2828
'Part Name': part.name,
2929
QTY: instanceList.length,
3030
Material: part.material,
31-
[`Size (${distanceUnit.value})`]: `${formatDistance(part.size.thickness)} × ${formatDistance(part.size.width)} × ${formatDistance(part.size.length)}`,
31+
[`Size (${distanceUnit.value})`]: `${formatDistance(part.thicknessM)} × ${formatDistance(part.widthM)} × ${formatDistance(part.lengthM)}`,
3232
};
3333
});
3434
});

web/components/LayoutListItem.vue

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
<script lang="ts" setup>
2-
import { Distance } from '@aklinker1/cutlist';
2+
import { type BoardLayout } from '@aklinker1/cutlist';
33
44
const props = defineProps<{
55
layout: BoardLayout;
66
}>();
77
8-
const widthPx = usePx(() => props.layout.stock.width);
9-
const heightPx = usePx(() => props.layout.stock.height);
8+
const widthPx = usePx(() => props.layout.stock.widthM);
9+
const heightPx = usePx(() => props.layout.stock.lengthM);
1010
11-
const thickness = useFormattedDistance(() => props.layout.stock.data.thickness);
12-
const width = useFormattedDistance(() => props.layout.stock.data.width);
13-
const length = useFormattedDistance(() => props.layout.stock.data.length);
11+
const thickness = useFormattedDistance(() => props.layout.stock.thicknessM);
12+
const width = useFormattedDistance(() => props.layout.stock.widthM);
13+
const length = useFormattedDistance(() => props.layout.stock.lengthM);
1414
</script>
1515

1616
<template>
1717
<li class="flex flex-col items-center gap-4">
1818
<p class="text-center">
19-
<span class="font-bold text-nowrap">{{
20-
layout.stock.data.material
21-
}}</span>
19+
<span class="font-bold text-nowrap">{{ layout.stock.material }}</span>
2220
<br />
2321
<span class="text-xs text-nowrap"
2422
>{{ thickness }} &times; {{ width }} &times; {{ length }}</span

web/components/LeftoverListItem.vue

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<script lang="ts" setup>
2-
import { useElementHover } from '@vueuse/core';
2+
import type { BoardLayoutLeftover } from '@aklinker1/cutlist';
33
44
const props = defineProps<{
5-
part: PartToCut;
5+
part: BoardLayoutLeftover;
66
}>();
7-
const width = useFormattedDistance(() => props.part.size.width);
8-
const length = useFormattedDistance(() => props.part.size.length);
9-
const thickness = useFormattedDistance(() => props.part.size.thickness);
7+
const width = useFormattedDistance(() => props.part.widthM);
8+
const length = useFormattedDistance(() => props.part.lengthM);
9+
const thickness = useFormattedDistance(() => props.part.thicknessM);
1010
</script>
1111

1212
<template>

web/components/MainSidebar.vue

+26-14
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
<script lang="ts" setup>
2-
import type { Config, Project } from '@aklinker1/cutlist';
3-
import { Distance } from '@aklinker1/cutlist';
2+
import type { HorizontalNavigationLink } from '#ui/types';
43
54
const url = useAssemblyUrl();
65
7-
const { data: doc, isLoading: isLoadingDoc } = useDocumentQuery();
6+
const { data: doc, isFetching: isFetchingDoc } = useDocumentQuery();
7+
const { isFetching: isFetchingLayouts } = useBoardLayoutsQuery();
8+
9+
const refresh = useRefreshOnshapeQueries();
810
911
const { data: boardLayouts } = useBoardLayoutsQuery();
1012
@@ -14,7 +16,7 @@ const warningsBadge = computed(() => {
1416
return leftovers.length;
1517
});
1618
17-
const links = computed(() => [
19+
const links = computed<HorizontalNavigationLink[]>(() => [
1820
{
1921
label: 'BOM',
2022
icon: 'i-heroicons-table-cells',
@@ -91,24 +93,34 @@ const tab = ref<'bom' | 'stock' | 'settings' | 'warnings'>('bom');
9193
color="white"
9294
:trailing="false"
9395
placeholder="Enter URL..."
94-
:loading="isLoadingDoc"
96+
:loading="isFetchingDoc"
9597
/>
9698
<div
9799
v-if="doc"
98-
class="py-2 px-3 bg-gray-900 rounded-lg border border-gray-700 mt-2 flex justify-between items-center"
100+
class="p-2 pl-3 bg-gray-900 rounded-lg border border-gray-700 mt-2 flex justify-between items-center"
99101
>
100102
<p>
101103
{{ doc.name }}
102104
<span class="text-sm opacity-50">by {{ doc.owner.name }}</span>
103105
</p>
104-
<ULink
105-
:to="url"
106-
target="_blank"
107-
class="text-sm text-primary hover:underline flex items-center gap-1"
108-
>
109-
<span>Open</span>
110-
<UIcon name="i-heroicons-arrow-top-right-on-square" />
111-
</ULink>
106+
<div class="flex items-center gap-2">
107+
<UButton
108+
title="Get latest parts from Onshape"
109+
:icon="isFetchingLayouts ? undefined : 'i-heroicons-arrow-path'"
110+
color="gray"
111+
size="sm"
112+
:loading="isFetchingLayouts"
113+
@click="refresh"
114+
/>
115+
<UButton
116+
title="Open in Onshape"
117+
:to="url"
118+
target="_blank"
119+
icon="i-heroicons-arrow-top-right-on-square"
120+
color="gray"
121+
size="sm"
122+
/>
123+
</div>
112124
</div>
113125
</UFormGroup>
114126

web/components/PartDetails.vue

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
<script lang="ts" setup>
2+
import type {
3+
BoardLayoutLeftover,
4+
BoardLayoutPlacement,
5+
} from '@aklinker1/cutlist';
6+
27
const props = defineProps<{
3-
part: PartToCut;
4-
placement?: Rectanble<any>;
8+
part: BoardLayoutLeftover;
9+
placement?: BoardLayoutPlacement;
510
}>();
611
7-
const width = useFormattedDistance(() => props.part.size.width);
8-
const length = useFormattedDistance(() => props.part.size.length);
9-
const thickness = useFormattedDistance(() => props.part.size.thickness);
12+
const width = useFormattedDistance(() => props.part.widthM);
13+
const length = useFormattedDistance(() => props.part.lengthM);
14+
const thickness = useFormattedDistance(() => props.part.thicknessM);
1015
11-
const top = useFormattedDistance(() => props.placement?.top);
12-
const left = useFormattedDistance(() => props.placement?.left);
13-
const right = useFormattedDistance(() => props.placement?.right);
14-
const bottom = useFormattedDistance(() => props.placement?.bottom);
16+
const top = useFormattedDistance(() => props.placement?.topM);
17+
const left = useFormattedDistance(() => props.placement?.leftM);
18+
const right = useFormattedDistance(() => props.placement?.rightM);
19+
const bottom = useFormattedDistance(() => props.placement?.bottomM);
1520
</script>
1621

1722
<template>

web/components/PartDetailsTooltip.vue

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<script lang="ts" setup>
2+
import type { BoardLayoutPlacement } from '@aklinker1/cutlist';
3+
24
const props = defineProps<{
3-
part: Rectangle<PartToCut>;
5+
part: BoardLayoutPlacement;
46
}>();
57
68
const { x, y } = useGlobalMouse();
@@ -14,7 +16,7 @@ const { x, y } = useGlobalMouse();
1416
>
1517
<PartDetails
1618
class="translate-x-[-50%] translate-y-8 p-2 bg-gray-600 rounded shadow-xl min-w-[256px]"
17-
:part="part.data"
19+
:part="part"
1820
:placement="part"
1921
/>
2022
</div>

web/components/PartListItem.vue

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
<script lang="ts" setup>
2+
import type { BoardLayoutPlacement } from '@aklinker1/cutlist';
23
import { useElementHover } from '@vueuse/core';
34
45
const props = defineProps<{
5-
placement: Rectangle<PartToCut>;
6+
placement: BoardLayoutPlacement;
67
}>();
78
89
const container = ref<HTMLDivElement>();
910
const isHovered = useElementHover(container);
1011
11-
const width = usePx(() => props.placement.width);
12-
const height = usePx(() => props.placement.height);
13-
const left = usePx(() => props.placement.left);
14-
const bottom = usePx(() => props.placement.bottom);
12+
const width = usePx(() => props.placement.widthM);
13+
const height = usePx(() => props.placement.lengthM);
14+
const left = usePx(() => props.placement.leftM);
15+
const bottom = usePx(() => props.placement.bottomM);
1516
1617
const getPx = useGetPx();
1718
const fontSize = usePx(() =>
1819
Math.min(
19-
props.placement.width / 2,
20+
props.placement.widthM / 2,
2021
0.0254, // 1 in to m
2122
),
2223
);
@@ -40,7 +41,7 @@ const showPartNumbers = useShowPartNumbers();
4041
class="w-full text-clip text-gray-400 group-hover:text-primary text-right p-px"
4142
:style="`font-size:${fontSize};line-height:${fontSize}`"
4243
>
43-
{{ placement.data.partNumber }}
44+
{{ placement.partNumber }}
4445
</p>
4546
</UPlaceholder>
4647
<PartDetailsTooltip v-if="isHovered" :part="placement" />

web/components/ScaleController.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const percent = computed(() => `${Math.round(scale.value * 100)}%`);
1717
/>
1818
<UButton
1919
:title="`${percent} - Click to reset to 100%`"
20-
class="w-20 text-center"
20+
class="w-20 justify-center"
2121
size="lg"
2222
color="black"
2323
@click="resetZoom"

web/components/StockMatrixInput.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { StockMatrix } from '@aklinker1/cutlist';
33
import { z } from 'zod';
44
import YAML from 'js-yaml';
55
6-
const model = defineModel<StockMatrix>();
6+
const model = defineModel<StockMatrix[]>();
77
88
const getModelString = () =>
99
YAML.dump(model.value, { indent: 2, flowLevel: 2 });
+16-24
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,25 @@
11
import { useQuery } from '@tanstack/vue-query';
2-
import { useOnshapeUrl } from './useOnshapeUrl';
3-
import { type Project, getBoardLayouts, type Config } from '@aklinker1/cutlist';
2+
import { generateBoardLayouts } from '@aklinker1/cutlist';
43

54
export default function () {
6-
const onshape = useOnshapeApi();
5+
const loader = useOnshapeLoader();
76
const url = useAssemblyUrl();
8-
const onshapeUrl = useOnshapeUrl(url);
9-
const bladeWidth = useBladeWidth();
10-
const optimize = useOptimizeFor();
7+
const config = useCutlistConfig();
118
const stock = useStock();
129

13-
return useQuery({
14-
queryKey: ['board-layouts', url, bladeWidth, optimize, stock],
15-
queryFn: async () => {
16-
if (onshapeUrl.value == null) return undefined;
10+
const partsQuery = useQuery({
11+
queryKey: ['onshape', 'board-layouts', url],
12+
queryFn: () => loader.getParts(url.value),
13+
});
1714

18-
const project: Project = {
19-
source: {
20-
type: 'onshape',
21-
id: onshapeUrl.value.did,
22-
assemblyId: onshapeUrl.value.eid,
23-
},
24-
};
25-
const config: Config = {
26-
bladeWidth: bladeWidth.value,
27-
optimize: optimize.value,
28-
};
29-
return await getBoardLayouts(onshape, project, stock.value, config);
30-
},
31-
staleTime: 5 * 60e3, // 5 minutes, will refrech latest document
15+
const layouts = computed(() => {
16+
const parts = partsQuery.data.value;
17+
if (parts == null) return undefined;
18+
return generateBoardLayouts(parts, stock.value, config.value);
3219
});
20+
21+
return {
22+
...partsQuery,
23+
data: layouts,
24+
};
3325
}

web/composables/useCutlistConfig.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { Config } from '@aklinker1/cutlist';
2+
3+
export default createSharedComposable(() => {
4+
const bladeWidth = useBladeWidth();
5+
const optimize = useOptimizeFor();
6+
7+
return computed<Config>(() => ({
8+
bladeWidth: bladeWidth.value,
9+
optimize: optimize.value,
10+
}));
11+
});

web/composables/useDocumentQuery.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { useQuery } from '@tanstack/vue-query';
22
import { useOnshapeUrl } from './useOnshapeUrl';
33

44
export default function () {
5-
const onshape = useOnshapeApi();
5+
const onshape = useOnshapeLoader();
66

77
const url = useAssemblyUrl();
88
const onshapeUrl = useOnshapeUrl(url);
99

1010
return useQuery({
11-
queryKey: ['document', computed(() => toValue(url))],
11+
queryKey: ['onshape', 'document', computed(() => toValue(url))],
1212
queryFn: async () => {
1313
if (onshapeUrl.value == null) return undefined;
1414
return await onshape.getDocument(onshapeUrl.value.did);

web/composables/useOnshapeApi.ts

-8
This file was deleted.

web/composables/useOnshapeLoader.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineOnshapeLoader } from '@aklinker1/cutlist/onshape';
2+
3+
export default createGlobalState(() =>
4+
defineOnshapeLoader({
5+
// Forward requests through server
6+
baseUrl: '/api/onshape',
7+
// Server adds auth, no need to pass anything here
8+
// auth: { ... },
9+
}),
10+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { useQueryClient } from '@tanstack/vue-query';
2+
3+
export default function () {
4+
const queries = useQueryClient();
5+
return async () => {
6+
await queries.refetchQueries({
7+
queryKey: ['onshape'],
8+
});
9+
};
10+
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { NitroApp } from 'nitropack';
2-
import type { OnshapeApiClient } from '@aklinker1/cutlist/onshape';
2+
import type { OnshapeLoader } from '@aklinker1/cutlist/onshape';
33

44
export default function () {
55
return useNitroApp() as ExtendedNitroApp;
66
}
77

88
export interface ExtendedNitroApp extends NitroApp {
9-
onshape: OnshapeApiClient;
9+
onshape: OnshapeLoader;
1010
}

web/server/plugins/onshape.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { defineOnshapeApi } from '@aklinker1/cutlist/onshape';
1+
import { defineOnshapeLoader } from '@aklinker1/cutlist/onshape';
22
import { type ExtendedNitroApp } from '../composables/useExtendedNitroApp';
33

44
export default defineNitroPlugin((nitro) => {
55
const config = useRuntimeConfig();
6-
(nitro as ExtendedNitroApp).onshape = defineOnshapeApi({
6+
(nitro as ExtendedNitroApp).onshape = defineOnshapeLoader({
77
auth: config.onshape,
88
});
99
});

0 commit comments

Comments
 (0)