@loafmarkets/ui 0.0.2 → 0.0.4

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/dist/index.d.mts CHANGED
@@ -56,6 +56,34 @@ interface PortfolioSummaryProps extends React.HTMLAttributes<HTMLDivElement> {
56
56
  }
57
57
  declare const PortfolioSummary: React.ForwardRefExoticComponent<PortfolioSummaryProps & React.RefAttributes<HTMLDivElement>>;
58
58
 
59
+ type OrderbookSide = "ask" | "bid";
60
+ type OrderbookLevel = {
61
+ price: number;
62
+ amount: number;
63
+ depth?: number;
64
+ };
65
+ type OrderbookTrade = {
66
+ type: "buy" | "sell";
67
+ price: number;
68
+ amount: number;
69
+ time?: string;
70
+ };
71
+ interface OrderbookProps extends React.HTMLAttributes<HTMLDivElement> {
72
+ asks: OrderbookLevel[];
73
+ bids: OrderbookLevel[];
74
+ midPrice: number;
75
+ midChangePercent?: number;
76
+ trades?: OrderbookTrade[];
77
+ priceLabel?: string;
78
+ amountLabel?: string;
79
+ precision?: number;
80
+ amountPrecision?: number;
81
+ defaultTab?: "orderbook" | "trades";
82
+ onTabChange?: (tab: "orderbook" | "trades") => void;
83
+ rightHeader?: React.ReactNode;
84
+ }
85
+ declare const Orderbook: React.ForwardRefExoticComponent<OrderbookProps & React.RefAttributes<HTMLDivElement>>;
86
+
59
87
  type HousePositionPendingOrder = {
60
88
  type: "buy" | "sell";
61
89
  tokens: number;
@@ -74,6 +102,10 @@ type HousePositionSliderOrderPayload = {
74
102
  targetTokens: number;
75
103
  targetValue: number;
76
104
  };
105
+ type HousePositionOrderbook = {
106
+ bids?: OrderbookLevel[];
107
+ asks?: OrderbookLevel[];
108
+ };
77
109
  interface HousePositionSliderProps extends React.HTMLAttributes<HTMLDivElement> {
78
110
  tokenId: string;
79
111
  tokenSymbol: string;
@@ -83,37 +115,10 @@ interface HousePositionSliderProps extends React.HTMLAttributes<HTMLDivElement>
83
115
  tokensHeld: number;
84
116
  pendingOrders?: HousePositionPendingOrder[];
85
117
  defaultOrderType?: "market" | "limit";
118
+ orderbook?: HousePositionOrderbook;
86
119
  onConfirmOrder?: (payload: HousePositionSliderOrderPayload) => void;
87
120
  }
88
- declare function HousePositionSlider({ tokenId, tokenSymbol, totalTokens, currentPrice, availableCash, tokensHeld, pendingOrders, defaultOrderType, onConfirmOrder, className, ...props }: HousePositionSliderProps): react_jsx_runtime.JSX.Element;
89
-
90
- type OrderbookSide = "ask" | "bid";
91
- type OrderbookLevel = {
92
- price: number;
93
- amount: number;
94
- depth?: number;
95
- };
96
- type OrderbookTrade = {
97
- type: "buy" | "sell";
98
- price: number;
99
- amount: number;
100
- time?: string;
101
- };
102
- interface OrderbookProps extends React.HTMLAttributes<HTMLDivElement> {
103
- asks: OrderbookLevel[];
104
- bids: OrderbookLevel[];
105
- midPrice: number;
106
- midChangePercent?: number;
107
- trades?: OrderbookTrade[];
108
- priceLabel?: string;
109
- amountLabel?: string;
110
- precision?: number;
111
- amountPrecision?: number;
112
- defaultTab?: "orderbook" | "trades";
113
- onTabChange?: (tab: "orderbook" | "trades") => void;
114
- rightHeader?: React.ReactNode;
115
- }
116
- declare const Orderbook: React.ForwardRefExoticComponent<OrderbookProps & React.RefAttributes<HTMLDivElement>>;
121
+ declare function HousePositionSlider({ tokenId, tokenSymbol, totalTokens, currentPrice, availableCash, tokensHeld, pendingOrders, defaultOrderType, orderbook, onConfirmOrder, className, ...props }: HousePositionSliderProps): react_jsx_runtime.JSX.Element;
117
122
 
118
123
  type PropertyTourProps = Omit<React.ComponentPropsWithoutRef<typeof Card>, "title"> & {
119
124
  title: string;
@@ -165,6 +170,7 @@ type YourOrder = {
165
170
  amount: number;
166
171
  total: number;
167
172
  filledPercent: number;
173
+ status?: string;
168
174
  };
169
175
  type YourOrdersProps = Omit<React.ComponentPropsWithoutRef<typeof Card>, "title"> & {
170
176
  title?: string;
@@ -268,4 +274,4 @@ declare const PropertySubheader: React.ForwardRefExoticComponent<React.HTMLAttri
268
274
  actions?: PropertySubheaderAction[];
269
275
  } & React.RefAttributes<HTMLDivElement>>;
270
276
 
271
- export { Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, type HousePositionPendingOrder, HousePositionSlider, type HousePositionSliderOrderPayload, type HousePositionSliderProps, Orderbook, type OrderbookLevel, type OrderbookProps, type OrderbookSide, type OrderbookTrade, PortfolioSummary, type PortfolioSummaryProps, PriceChart, type PriceChartCandle, type PriceChartProps, type PriceChartRange, PropertyHeroHeader, type PropertyHeroHeaderProps, type PropertyNewsItem, type PropertyNewsType, PropertyNewsUpdates, type PropertyNewsUpdatesProps, PropertySubheader, type PropertySubheaderAction, type PropertySubheaderProps, type PropertySubheaderTab, PropertyTour, type PropertyTourProps, type YourOrder, type YourOrderSide, YourOrders, type YourOrdersProps, badgeVariants, buttonVariants };
277
+ export { Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, type HousePositionOrderbook, type HousePositionPendingOrder, HousePositionSlider, type HousePositionSliderOrderPayload, type HousePositionSliderProps, Orderbook, type OrderbookLevel, type OrderbookProps, type OrderbookSide, type OrderbookTrade, PortfolioSummary, type PortfolioSummaryProps, PriceChart, type PriceChartCandle, type PriceChartProps, type PriceChartRange, PropertyHeroHeader, type PropertyHeroHeaderProps, type PropertyNewsItem, type PropertyNewsType, PropertyNewsUpdates, type PropertyNewsUpdatesProps, PropertySubheader, type PropertySubheaderAction, type PropertySubheaderProps, type PropertySubheaderTab, PropertyTour, type PropertyTourProps, type YourOrder, type YourOrderSide, YourOrders, type YourOrdersProps, badgeVariants, buttonVariants };
package/dist/index.d.ts CHANGED
@@ -56,6 +56,34 @@ interface PortfolioSummaryProps extends React.HTMLAttributes<HTMLDivElement> {
56
56
  }
57
57
  declare const PortfolioSummary: React.ForwardRefExoticComponent<PortfolioSummaryProps & React.RefAttributes<HTMLDivElement>>;
58
58
 
59
+ type OrderbookSide = "ask" | "bid";
60
+ type OrderbookLevel = {
61
+ price: number;
62
+ amount: number;
63
+ depth?: number;
64
+ };
65
+ type OrderbookTrade = {
66
+ type: "buy" | "sell";
67
+ price: number;
68
+ amount: number;
69
+ time?: string;
70
+ };
71
+ interface OrderbookProps extends React.HTMLAttributes<HTMLDivElement> {
72
+ asks: OrderbookLevel[];
73
+ bids: OrderbookLevel[];
74
+ midPrice: number;
75
+ midChangePercent?: number;
76
+ trades?: OrderbookTrade[];
77
+ priceLabel?: string;
78
+ amountLabel?: string;
79
+ precision?: number;
80
+ amountPrecision?: number;
81
+ defaultTab?: "orderbook" | "trades";
82
+ onTabChange?: (tab: "orderbook" | "trades") => void;
83
+ rightHeader?: React.ReactNode;
84
+ }
85
+ declare const Orderbook: React.ForwardRefExoticComponent<OrderbookProps & React.RefAttributes<HTMLDivElement>>;
86
+
59
87
  type HousePositionPendingOrder = {
60
88
  type: "buy" | "sell";
61
89
  tokens: number;
@@ -74,6 +102,10 @@ type HousePositionSliderOrderPayload = {
74
102
  targetTokens: number;
75
103
  targetValue: number;
76
104
  };
105
+ type HousePositionOrderbook = {
106
+ bids?: OrderbookLevel[];
107
+ asks?: OrderbookLevel[];
108
+ };
77
109
  interface HousePositionSliderProps extends React.HTMLAttributes<HTMLDivElement> {
78
110
  tokenId: string;
79
111
  tokenSymbol: string;
@@ -83,37 +115,10 @@ interface HousePositionSliderProps extends React.HTMLAttributes<HTMLDivElement>
83
115
  tokensHeld: number;
84
116
  pendingOrders?: HousePositionPendingOrder[];
85
117
  defaultOrderType?: "market" | "limit";
118
+ orderbook?: HousePositionOrderbook;
86
119
  onConfirmOrder?: (payload: HousePositionSliderOrderPayload) => void;
87
120
  }
88
- declare function HousePositionSlider({ tokenId, tokenSymbol, totalTokens, currentPrice, availableCash, tokensHeld, pendingOrders, defaultOrderType, onConfirmOrder, className, ...props }: HousePositionSliderProps): react_jsx_runtime.JSX.Element;
89
-
90
- type OrderbookSide = "ask" | "bid";
91
- type OrderbookLevel = {
92
- price: number;
93
- amount: number;
94
- depth?: number;
95
- };
96
- type OrderbookTrade = {
97
- type: "buy" | "sell";
98
- price: number;
99
- amount: number;
100
- time?: string;
101
- };
102
- interface OrderbookProps extends React.HTMLAttributes<HTMLDivElement> {
103
- asks: OrderbookLevel[];
104
- bids: OrderbookLevel[];
105
- midPrice: number;
106
- midChangePercent?: number;
107
- trades?: OrderbookTrade[];
108
- priceLabel?: string;
109
- amountLabel?: string;
110
- precision?: number;
111
- amountPrecision?: number;
112
- defaultTab?: "orderbook" | "trades";
113
- onTabChange?: (tab: "orderbook" | "trades") => void;
114
- rightHeader?: React.ReactNode;
115
- }
116
- declare const Orderbook: React.ForwardRefExoticComponent<OrderbookProps & React.RefAttributes<HTMLDivElement>>;
121
+ declare function HousePositionSlider({ tokenId, tokenSymbol, totalTokens, currentPrice, availableCash, tokensHeld, pendingOrders, defaultOrderType, orderbook, onConfirmOrder, className, ...props }: HousePositionSliderProps): react_jsx_runtime.JSX.Element;
117
122
 
118
123
  type PropertyTourProps = Omit<React.ComponentPropsWithoutRef<typeof Card>, "title"> & {
119
124
  title: string;
@@ -165,6 +170,7 @@ type YourOrder = {
165
170
  amount: number;
166
171
  total: number;
167
172
  filledPercent: number;
173
+ status?: string;
168
174
  };
169
175
  type YourOrdersProps = Omit<React.ComponentPropsWithoutRef<typeof Card>, "title"> & {
170
176
  title?: string;
@@ -268,4 +274,4 @@ declare const PropertySubheader: React.ForwardRefExoticComponent<React.HTMLAttri
268
274
  actions?: PropertySubheaderAction[];
269
275
  } & React.RefAttributes<HTMLDivElement>>;
270
276
 
271
- export { Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, type HousePositionPendingOrder, HousePositionSlider, type HousePositionSliderOrderPayload, type HousePositionSliderProps, Orderbook, type OrderbookLevel, type OrderbookProps, type OrderbookSide, type OrderbookTrade, PortfolioSummary, type PortfolioSummaryProps, PriceChart, type PriceChartCandle, type PriceChartProps, type PriceChartRange, PropertyHeroHeader, type PropertyHeroHeaderProps, type PropertyNewsItem, type PropertyNewsType, PropertyNewsUpdates, type PropertyNewsUpdatesProps, PropertySubheader, type PropertySubheaderAction, type PropertySubheaderProps, type PropertySubheaderTab, PropertyTour, type PropertyTourProps, type YourOrder, type YourOrderSide, YourOrders, type YourOrdersProps, badgeVariants, buttonVariants };
277
+ export { Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, type HousePositionOrderbook, type HousePositionPendingOrder, HousePositionSlider, type HousePositionSliderOrderPayload, type HousePositionSliderProps, Orderbook, type OrderbookLevel, type OrderbookProps, type OrderbookSide, type OrderbookTrade, PortfolioSummary, type PortfolioSummaryProps, PriceChart, type PriceChartCandle, type PriceChartProps, type PriceChartRange, PropertyHeroHeader, type PropertyHeroHeaderProps, type PropertyNewsItem, type PropertyNewsType, PropertyNewsUpdates, type PropertyNewsUpdatesProps, PropertySubheader, type PropertySubheaderAction, type PropertySubheaderProps, type PropertySubheaderTab, PropertyTour, type PropertyTourProps, type YourOrder, type YourOrderSide, YourOrders, type YourOrdersProps, badgeVariants, buttonVariants };
package/dist/index.js CHANGED
@@ -234,6 +234,66 @@ var PortfolioSummary = React5__namespace.forwardRef(
234
234
  PortfolioSummary.displayName = "PortfolioSummary";
235
235
  var clamp = (v, min, max) => Math.min(max, Math.max(min, v));
236
236
  var fmt0 = (v) => Math.abs(v).toLocaleString(void 0, { maximumFractionDigits: 0 });
237
+ var normalizeLevels = (levels = []) => levels.filter((level) => Number.isFinite(level.price) && level.price > 0 && Number.isFinite(level.amount) && level.amount > 0);
238
+ var estimateMarketBuyFromUsd = (levels = [], usdAmount) => {
239
+ if (!Number.isFinite(usdAmount) || usdAmount <= 0) return { tokens: 0, value: 0, avgPrice: null };
240
+ const asks = normalizeLevels(levels).sort((a, b) => a.price - b.price);
241
+ let remainingUsd = usdAmount;
242
+ let tokensFilled = 0;
243
+ let spent = 0;
244
+ for (const level of asks) {
245
+ if (remainingUsd <= 0) break;
246
+ const levelValueCapacity = level.amount * level.price;
247
+ const usdToSpend = Math.min(remainingUsd, levelValueCapacity);
248
+ const tokensFromLevel = usdToSpend / level.price;
249
+ tokensFilled += tokensFromLevel;
250
+ spent += usdToSpend;
251
+ remainingUsd -= usdToSpend;
252
+ }
253
+ return {
254
+ tokens: tokensFilled,
255
+ value: spent,
256
+ avgPrice: tokensFilled > 0 ? spent / tokensFilled : null
257
+ };
258
+ };
259
+ var estimateMarketBuyFromTokens = (levels = [], tokenAmount) => {
260
+ if (!Number.isFinite(tokenAmount) || tokenAmount <= 0) return { tokens: 0, value: 0, avgPrice: null };
261
+ const asks = normalizeLevels(levels).sort((a, b) => a.price - b.price);
262
+ let remainingTokens = tokenAmount;
263
+ let tokensFilled = 0;
264
+ let spent = 0;
265
+ for (const level of asks) {
266
+ if (remainingTokens <= 0) break;
267
+ const tokensFromLevel = Math.min(remainingTokens, level.amount);
268
+ spent += tokensFromLevel * level.price;
269
+ tokensFilled += tokensFromLevel;
270
+ remainingTokens -= tokensFromLevel;
271
+ }
272
+ return {
273
+ tokens: tokensFilled,
274
+ value: spent,
275
+ avgPrice: tokensFilled > 0 ? spent / tokensFilled : null
276
+ };
277
+ };
278
+ var estimateMarketSellFromTokens = (levels = [], tokenAmount) => {
279
+ if (!Number.isFinite(tokenAmount) || tokenAmount <= 0) return { tokens: 0, value: 0, avgPrice: null };
280
+ const bids = normalizeLevels(levels).sort((a, b) => b.price - a.price);
281
+ let remainingTokens = tokenAmount;
282
+ let tokensFilled = 0;
283
+ let received = 0;
284
+ for (const level of bids) {
285
+ if (remainingTokens <= 0) break;
286
+ const tokensFromLevel = Math.min(remainingTokens, level.amount);
287
+ received += tokensFromLevel * level.price;
288
+ tokensFilled += tokensFromLevel;
289
+ remainingTokens -= tokensFromLevel;
290
+ }
291
+ return {
292
+ tokens: tokensFilled,
293
+ value: received,
294
+ avgPrice: tokensFilled > 0 ? received / tokensFilled : null
295
+ };
296
+ };
237
297
  function HousePositionSlider({
238
298
  tokenId,
239
299
  tokenSymbol,
@@ -243,6 +303,7 @@ function HousePositionSlider({
243
303
  tokensHeld,
244
304
  pendingOrders = [],
245
305
  defaultOrderType = "market",
306
+ orderbook,
246
307
  onConfirmOrder,
247
308
  className,
248
309
  ...props
@@ -256,45 +317,103 @@ function HousePositionSlider({
256
317
  const [orderType, setOrderType] = React5__namespace.useState(defaultOrderType);
257
318
  const [limitPrice, setLimitPrice] = React5__namespace.useState(currentPrice);
258
319
  const [limitPriceInput, setLimitPriceInput] = React5__namespace.useState(currentPrice.toFixed(2));
320
+ const [limitPriceDirty, setLimitPriceDirty] = React5__namespace.useState(false);
259
321
  const [ownershipInput, setOwnershipInput] = React5__namespace.useState("");
260
322
  const [tokenAmountInput, setTokenAmountInput] = React5__namespace.useState("");
261
323
  const houseRef = React5__namespace.useRef(null);
324
+ const asks = orderbook?.asks ?? [];
325
+ const bids = orderbook?.bids ?? [];
262
326
  React5__namespace.useEffect(() => {
327
+ if (orderType !== "limit") return;
328
+ if (limitPriceDirty) return;
263
329
  setLimitPrice(currentPrice);
264
330
  setLimitPriceInput(currentPrice.toFixed(2));
265
- }, [currentPrice]);
331
+ }, [currentPrice, limitPriceDirty, orderType]);
266
332
  const effectivePrice = orderType === "limit" ? limitPrice : currentPrice;
267
- const holdingsValue = tokensHeld * effectivePrice;
268
- const totalCapacity = holdingsValue + availableCash;
269
333
  const pendingBuyValue = pendingOrders.filter((o) => o.type === "buy").reduce((s, o) => s + o.value, 0);
270
334
  const pendingSellTokens = pendingOrders.filter((o) => o.type === "sell").reduce((s, o) => s + Math.abs(o.tokens), 0);
271
335
  const effectiveAvailableCash = Math.max(0, availableCash - pendingBuyValue);
272
336
  const effectiveTokensHeld = Math.max(0, tokensHeld - pendingSellTokens);
273
- const baselinePct = totalCapacity <= 0 ? 0 : holdingsValue / totalCapacity * 100;
337
+ const holdingsValue = tokensHeld * effectivePrice;
338
+ const sliderHoldingsValue = effectiveTokensHeld * effectivePrice;
339
+ const sliderTotalCapacity = sliderHoldingsValue + effectiveAvailableCash;
340
+ const baselinePct = sliderTotalCapacity <= 0 ? 0 : sliderHoldingsValue / sliderTotalCapacity * 100;
274
341
  let deltaTokens = 0;
275
342
  let deltaValue = 0;
343
+ let marketAvgPrice = null;
276
344
  let targetTokens = tokensHeld;
277
345
  let targetValue = holdingsValue;
346
+ const limitPriceSafe = limitPrice > 0 ? limitPrice : currentPrice || 1;
278
347
  if (orderMode === "buy") {
279
- if (buyTrackingMode === "tokens") {
280
- deltaTokens = deltaTokensBuy;
281
- deltaValue = deltaTokensBuy * effectivePrice;
348
+ if (orderType === "market") {
349
+ if (buyTrackingMode === "tokens") {
350
+ const desiredTokens = Math.max(0, deltaTokensBuy);
351
+ const result = estimateMarketBuyFromTokens(asks, desiredTokens);
352
+ deltaTokens = result.tokens;
353
+ deltaValue = result.value;
354
+ marketAvgPrice = result.avgPrice;
355
+ } else {
356
+ const notional = Math.min(Math.max(0, deltaDollars), effectiveAvailableCash);
357
+ const result = estimateMarketBuyFromUsd(asks, notional);
358
+ deltaTokens = result.tokens;
359
+ deltaValue = result.value;
360
+ marketAvgPrice = result.avgPrice;
361
+ }
282
362
  } else {
283
- deltaValue = deltaDollars;
284
- deltaTokens = deltaDollars / (effectivePrice || 1);
363
+ if (buyTrackingMode === "tokens") {
364
+ deltaTokens = deltaTokensBuy;
365
+ deltaValue = deltaTokensBuy * limitPriceSafe;
366
+ } else {
367
+ const notional = Math.min(Math.max(0, deltaDollars), effectiveAvailableCash);
368
+ deltaValue = notional;
369
+ deltaTokens = notional / limitPriceSafe;
370
+ }
285
371
  }
286
372
  } else if (orderMode === "sell") {
287
- deltaTokens = deltaTokensSell;
288
- deltaValue = deltaTokensSell * effectivePrice;
373
+ if (orderType === "market") {
374
+ const tokensToSell = Math.abs(deltaTokensSell);
375
+ const result = estimateMarketSellFromTokens(bids, tokensToSell);
376
+ deltaTokens = -result.tokens;
377
+ deltaValue = -result.value;
378
+ marketAvgPrice = result.avgPrice;
379
+ } else {
380
+ deltaTokens = deltaTokensSell;
381
+ deltaValue = deltaTokensSell * limitPriceSafe;
382
+ }
289
383
  }
290
384
  targetTokens = tokensHeld + deltaTokens;
291
385
  targetValue = targetTokens * effectivePrice;
292
- const targetPct = totalCapacity <= 0 ? 0 : targetValue / totalCapacity * 100;
386
+ const plannedDeltaValue = (() => {
387
+ if (orderMode === "buy") {
388
+ if (buyTrackingMode === "dollars") {
389
+ const notional = Math.min(Math.max(0, deltaDollars), effectiveAvailableCash);
390
+ return notional;
391
+ }
392
+ const tokensPlanned = Math.max(0, deltaTokensBuy);
393
+ const referencePrice = orderType === "market" ? currentPrice || limitPriceSafe : limitPriceSafe;
394
+ return Math.min(tokensPlanned * referencePrice, effectiveAvailableCash);
395
+ }
396
+ if (orderMode === "sell") {
397
+ const tokensToSell = Math.abs(Math.min(0, deltaTokensSell));
398
+ const sellValue = tokensToSell * effectivePrice;
399
+ return -Math.min(sellValue, sliderHoldingsValue);
400
+ }
401
+ return 0;
402
+ })();
403
+ const sliderTargetValue = clamp(sliderHoldingsValue + plannedDeltaValue, 0, sliderTotalCapacity);
404
+ const targetPct = sliderTotalCapacity <= 0 ? 0 : sliderTargetValue / sliderTotalCapacity * 100;
293
405
  const isIncrease = orderMode === "buy";
294
406
  const hasChange = orderMode !== "none" && (Math.abs(deltaTokens) > 1e-3 || Math.abs(deltaValue) > 0.01);
295
407
  const currentOwnership = totalTokens <= 0 ? 0 : tokensHeld / totalTokens * 100;
296
408
  const targetOwnership = totalTokens <= 0 ? 0 : targetTokens / totalTokens * 100;
297
409
  const estFeeTokens = Math.abs(deltaValue) * 5e-3 / (effectivePrice || 1);
410
+ const resetOrder = React5__namespace.useCallback(() => {
411
+ setOrderMode("none");
412
+ setBuyTrackingMode("dollars");
413
+ setDeltaDollars(0);
414
+ setDeltaTokensBuy(0);
415
+ setDeltaTokensSell(0);
416
+ }, []);
298
417
  const updateOrderFromTargetValue = React5__namespace.useCallback(
299
418
  (newTargetValue) => {
300
419
  const newDeltaValue = newTargetValue - holdingsValue;
@@ -315,13 +434,9 @@ function HousePositionSlider({
315
434
  setDeltaTokensBuy(0);
316
435
  return;
317
436
  }
318
- setOrderMode("none");
319
- setBuyTrackingMode("dollars");
320
- setDeltaDollars(0);
321
- setDeltaTokensBuy(0);
322
- setDeltaTokensSell(0);
437
+ resetOrder();
323
438
  },
324
- [effectiveAvailableCash, effectivePrice, effectiveTokensHeld, holdingsValue, tokensHeld]
439
+ [effectiveAvailableCash, effectivePrice, effectiveTokensHeld, holdingsValue, resetOrder, tokensHeld]
325
440
  );
326
441
  const updateOrderFromOwnership = React5__namespace.useCallback(
327
442
  (newOwnershipPercent) => {
@@ -350,13 +465,47 @@ function HousePositionSlider({
350
465
  setDeltaTokensBuy(0);
351
466
  return;
352
467
  }
353
- setOrderMode("none");
354
- setBuyTrackingMode("dollars");
355
- setDeltaDollars(0);
356
- setDeltaTokensBuy(0);
357
- setDeltaTokensSell(0);
468
+ resetOrder();
469
+ },
470
+ [effectiveAvailableCash, effectivePrice, effectiveTokensHeld, resetOrder]
471
+ );
472
+ const updateOrderFromSlider = React5__namespace.useCallback(
473
+ (pct) => {
474
+ const normalized = (pct - 50) / 50;
475
+ const magnitude = Math.min(Math.abs(normalized), 1);
476
+ if (magnitude < 0.02) {
477
+ resetOrder();
478
+ return;
479
+ }
480
+ if (normalized > 0) {
481
+ const notional = clamp(magnitude * effectiveAvailableCash, 0, effectiveAvailableCash);
482
+ if (notional <= 0) {
483
+ resetOrder();
484
+ return;
485
+ }
486
+ setOrderMode("buy");
487
+ setBuyTrackingMode("dollars");
488
+ setDeltaDollars(notional);
489
+ setDeltaTokensBuy(0);
490
+ setDeltaTokensSell(0);
491
+ return;
492
+ }
493
+ if (normalized < 0) {
494
+ const tokensToSell = clamp(magnitude * effectiveTokensHeld, 0, effectiveTokensHeld);
495
+ if (tokensToSell <= 0) {
496
+ resetOrder();
497
+ return;
498
+ }
499
+ setOrderMode("sell");
500
+ setBuyTrackingMode("dollars");
501
+ setDeltaTokensSell(-tokensToSell);
502
+ setDeltaDollars(0);
503
+ setDeltaTokensBuy(0);
504
+ return;
505
+ }
506
+ resetOrder();
358
507
  },
359
- [effectiveAvailableCash, effectivePrice, effectiveTokensHeld]
508
+ [effectiveAvailableCash, effectiveTokensHeld, resetOrder]
360
509
  );
361
510
  const handleDragAtClientY = React5__namespace.useCallback(
362
511
  (clientY) => {
@@ -364,9 +513,9 @@ function HousePositionSlider({
364
513
  const rect = houseRef.current.getBoundingClientRect();
365
514
  const y = clientY - rect.top;
366
515
  const pct = clamp(100 - y / rect.height * 100, 0, 100);
367
- updateOrderFromTargetValue(pct / 100 * totalCapacity);
516
+ updateOrderFromSlider(pct);
368
517
  },
369
- [totalCapacity, updateOrderFromTargetValue]
518
+ [updateOrderFromSlider]
370
519
  );
371
520
  const onMouseDown = (e) => {
372
521
  e.preventDefault();
@@ -396,15 +545,21 @@ function HousePositionSlider({
396
545
  document.addEventListener("touchend", onEnd);
397
546
  };
398
547
  const handleCancel = () => {
399
- setOrderMode("none");
400
- setBuyTrackingMode("dollars");
401
- setDeltaDollars(0);
402
- setDeltaTokensBuy(0);
403
- setDeltaTokensSell(0);
548
+ resetOrder();
549
+ };
550
+ const handleOrderTypeSelection = (next) => {
551
+ setOrderType(next);
552
+ if (next === "limit") {
553
+ setLimitPriceDirty(false);
554
+ setLimitPrice(currentPrice);
555
+ setLimitPriceInput(currentPrice.toFixed(2));
556
+ } else {
557
+ setLimitPriceDirty(false);
558
+ }
404
559
  };
405
560
  const handleConfirm = () => {
406
561
  if (!hasChange) return;
407
- const priceToUse = orderType === "market" ? currentPrice : limitPrice;
562
+ const priceToUse = orderType === "market" ? marketAvgPrice ?? currentPrice : limitPrice;
408
563
  onConfirmOrder?.({
409
564
  side: isIncrease ? "buy" : "sell",
410
565
  orderType,
@@ -429,6 +584,8 @@ function HousePositionSlider({
429
584
  const showIncrease = targetPct > baselinePct;
430
585
  const showDecrease = targetPct < baselinePct;
431
586
  const valueLabel = orderType === "limit" ? `${tokenSymbol} Owned at Limit` : `${tokenSymbol} Owned`;
587
+ const deltaSign = deltaValue > 0 ? "+" : deltaValue < 0 ? "-" : "";
588
+ const percentMarkers = [100, 50, 25, 0];
432
589
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative flex w-full flex-col items-center gap-6 rounded-[12px] bg-black/20 px-8 pb-6 pt-12", className), ...props, children: [
433
590
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-4 top-4 text-[1.1rem] font-semibold tracking-[0.5px] text-white", children: "Place Order" }),
434
591
  hasChange ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -442,46 +599,53 @@ function HousePositionSlider({
442
599
  ) : null,
443
600
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
444
601
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2 text-xs uppercase tracking-[1px] text-[#888]", children: valueLabel }),
445
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-baseline gap-2 text-[2rem] font-bold text-white", children: isDragging ? /* @__PURE__ */ jsxRuntime.jsxs(
446
- "span",
447
- {
448
- className: cn("text-[2rem] font-semibold", deltaValue >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]"),
449
- children: [
450
- deltaValue > 0 ? "+" : deltaValue < 0 ? "-" : "",
451
- "$",
452
- fmt0(deltaValue)
453
- ]
454
- }
455
- ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
456
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
457
- "$",
458
- fmt0(targetValue)
459
- ] }),
460
- hasChange ? /* @__PURE__ */ jsxRuntime.jsxs(
602
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-center gap-3", children: [
603
+ /* @__PURE__ */ jsxRuntime.jsxs(
461
604
  "span",
462
605
  {
463
606
  className: cn(
464
- "relative top-[-0.5rem] text-[0.85rem] font-semibold",
607
+ "text-[2.2rem] font-semibold",
465
608
  deltaValue >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]"
466
609
  ),
467
610
  children: [
468
- deltaValue > 0 ? "+" : deltaValue < 0 ? "-" : "",
611
+ deltaSign,
612
+ "$",
469
613
  fmt0(deltaValue)
470
614
  ]
471
615
  }
472
- ) : null
473
- ] }) })
474
- ] }),
475
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: houseRef, className: "h-[200px] w-[160px] select-none touch-none", style: { cursor: "ns-resize" }, onMouseDown, onTouchStart, children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 120 160", className: "h-full w-full overflow-visible", children: [
476
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "10", y: "10", width: "100", height: "140", rx: "8", fill: "rgba(255,255,255,0.04)", stroke: "rgba(255,255,255,0.10)" }),
477
- /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "loaf-clip", children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "10", y: "10", width: "100", height: "140", rx: "8" }) }),
478
- /* @__PURE__ */ jsxRuntime.jsxs("g", { clipPath: "url(#loaf-clip)", children: [
479
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "10", y: showDecrease ? targetFillY : baselineFillY, width: "100", height: showDecrease ? targetFillHeight : baselineFillHeight, fill: "rgba(220,175,120,0.7)" }),
480
- showIncrease ? /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "10", y: targetFillY, width: "100", height: targetFillHeight - baselineFillHeight, fill: "rgba(14,203,129,0.35)" }) : null,
481
- showDecrease ? /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "10", y: baselineFillY, width: "100", height: baselineFillHeight - targetFillHeight, fill: "rgba(246,70,93,0.35)" }) : null,
482
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: targetFillY, x2: "108", y2: targetFillY, stroke: showIncrease ? "#0ecb81" : showDecrease ? "#f6465d" : "rgba(234,217,162,1)", strokeWidth: "2", strokeLinecap: "round" })
616
+ ),
617
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-lg font-semibold text-white/60", children: [
618
+ "$",
619
+ fmt0(targetValue)
620
+ ] })
483
621
  ] })
484
- ] }) }),
622
+ ] }),
623
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
624
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[200px] flex-col justify-between text-xs text-white/40", children: percentMarkers.map((marker) => /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
625
+ marker,
626
+ "%"
627
+ ] }, marker)) }),
628
+ /* @__PURE__ */ jsxRuntime.jsx(
629
+ "div",
630
+ {
631
+ ref: houseRef,
632
+ className: "h-[200px] w-[160px] select-none touch-none",
633
+ style: { cursor: "ns-resize" },
634
+ onMouseDown,
635
+ onTouchStart,
636
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 120 160", className: "h-full w-full overflow-visible", children: [
637
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "10", y: "10", width: "100", height: "140", rx: "8", fill: "rgba(255,255,255,0.04)", stroke: "rgba(255,255,255,0.10)" }),
638
+ /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "loaf-clip", children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "10", y: "10", width: "100", height: "140", rx: "8" }) }),
639
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { clipPath: "url(#loaf-clip)", children: [
640
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "10", y: showDecrease ? targetFillY : baselineFillY, width: "100", height: showDecrease ? targetFillHeight : baselineFillHeight, fill: "rgba(220,175,120,0.7)" }),
641
+ showIncrease ? /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "10", y: targetFillY, width: "100", height: targetFillHeight - baselineFillHeight, fill: "rgba(14,203,129,0.35)" }) : null,
642
+ showDecrease ? /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "10", y: baselineFillY, width: "100", height: baselineFillHeight - targetFillHeight, fill: "rgba(246,70,93,0.35)" }) : null,
643
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: targetFillY, x2: "108", y2: targetFillY, stroke: showIncrease ? "#0ecb81" : showDecrease ? "#f6465d" : "rgba(234,217,162,1)", strokeWidth: "2", strokeLinecap: "round" })
644
+ ] })
645
+ ] })
646
+ }
647
+ )
648
+ ] }),
485
649
  /* @__PURE__ */ jsxRuntime.jsx(
486
650
  "button",
487
651
  {
@@ -531,7 +695,7 @@ function HousePositionSlider({
531
695
  )
532
696
  ] })
533
697
  ] }),
534
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between py-2", children: [
698
+ orderType === "market" ? null : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between py-2", children: [
535
699
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white/50", children: isIncrease ? "Buying" : "Selling" }),
536
700
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
537
701
  orderMode === "buy" && buyTrackingMode === "dollars" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-1 text-[#0ecb81]", children: "~" }) : null,
@@ -580,7 +744,7 @@ function HousePositionSlider({
580
744
  "button",
581
745
  {
582
746
  type: "button",
583
- onClick: () => setOrderType("market"),
747
+ onClick: () => handleOrderTypeSelection("market"),
584
748
  className: cn(
585
749
  "flex-1 rounded-[6px] px-3 py-2 text-[0.8rem] font-medium transition",
586
750
  orderType === "market" ? "bg-[rgba(201,162,39,0.2)] text-[#C9A227]" : "text-white/50 hover:bg-white/5"
@@ -592,7 +756,7 @@ function HousePositionSlider({
592
756
  "button",
593
757
  {
594
758
  type: "button",
595
- onClick: () => setOrderType("limit"),
759
+ onClick: () => handleOrderTypeSelection("limit"),
596
760
  className: cn(
597
761
  "flex-1 rounded-[6px] px-3 py-2 text-[0.8rem] font-medium transition",
598
762
  orderType === "limit" ? "bg-[rgba(201,162,39,0.2)] text-[#C9A227]" : "text-white/50 hover:bg-white/5"
@@ -610,6 +774,7 @@ function HousePositionSlider({
610
774
  onChange: (e) => {
611
775
  const input = e.target.value;
612
776
  if (input === "" || /^[0-9]*\.?[0-9]*$/.test(input)) {
777
+ setLimitPriceDirty(true);
613
778
  setLimitPriceInput(input);
614
779
  const num = Number.parseFloat(input);
615
780
  if (Number.isFinite(num) && num > 0) setLimitPrice(num);
@@ -1150,7 +1315,8 @@ var YourOrders = React5__namespace.forwardRef(
1150
1315
  ] }),
1151
1316
  orders.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-10 text-center text-sm text-white/50", children: "No orders" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: orders.map((order) => {
1152
1317
  const sideClass = order.side === "buy" ? "text-[#0ecb81]" : "text-[#f6465d]";
1153
- const filled = clampPct(order.filledPercent);
1318
+ const filled = clampPct(order.filledPercent ?? 0);
1319
+ const isCancelled = order.status?.toUpperCase() === "CANCELLED";
1154
1320
  const canEditPrice = Boolean(onEditPrice);
1155
1321
  const canEditAmount = Boolean(onEditAmount);
1156
1322
  const canCancel = Boolean(onCancel);
@@ -1208,7 +1374,7 @@ var YourOrders = React5__namespace.forwardRef(
1208
1374
  ] }),
1209
1375
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 h-1 w-4/5 overflow-hidden rounded-sm bg-white/10", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full rounded-sm bg-[#C9A227]", style: { width: `${filled}%` } }) })
1210
1376
  ] }),
1211
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(
1377
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 text-center", children: isCancelled ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center justify-center rounded border border-white/20 px-3 py-1 text-[0.78rem] uppercase tracking-wide text-[#f6465d]", children: "Cancelled" }) : /* @__PURE__ */ jsxRuntime.jsx(
1212
1378
  "button",
1213
1379
  {
1214
1380
  type: "button",