@localflag/react 0.0.1 → 0.0.2

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
@@ -1,6 +1,8 @@
1
1
  # @localflag/react
2
2
 
3
- Type-safe feature flags for React with built-in DevTools.
3
+ Type-safe feature flags for React with built-in LocalFlagDevTools.
4
+
5
+ [Website](https://localflag.io) · [Documentation](https://docs.localflag.io) · [Demo](https://demo.localflag.io) · [GitHub](https://github.com/localflag/localflag)
4
6
 
5
7
  ## Installation
6
8
 
@@ -11,7 +13,7 @@ npm install @localflag/react
11
13
  ## Quick Start
12
14
 
13
15
  ```tsx
14
- import { FeatureFlagProvider, useFeatureFlag, DevTools } from '@localflag/react';
16
+ import { FeatureFlagProvider, useFeatureFlag, LocalFlagDevTools } from '@localflag/react';
15
17
 
16
18
  // 1. Define your flags
17
19
  const defaultFlags = {
@@ -28,7 +30,7 @@ function App() {
28
30
  return (
29
31
  <FeatureFlagProvider<AppFlags> defaultFlags={defaultFlags}>
30
32
  <MyApp />
31
- <DevTools /> {/* Optional: adds a floating panel to toggle flags */}
33
+ <LocalFlagDevTools /> {/* Optional: adds a floating panel to toggle flags */}
32
34
  </FeatureFlagProvider>
33
35
  );
34
36
  }
@@ -119,14 +121,14 @@ import { FeatureFlag } from '@localflag/react';
119
121
  </FeatureFlag>
120
122
  ```
121
123
 
122
- #### `DevTools`
124
+ #### `LocalFlagDevTools`
123
125
 
124
126
  A floating panel for toggling flags during development.
125
127
 
126
128
  ```tsx
127
- import { DevTools } from '@localflag/react';
129
+ import { LocalFlagDevTools } from '@localflag/react';
128
130
 
129
- <DevTools
131
+ <LocalFlagDevTools
130
132
  position="bottom-right" // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
131
133
  defaultOpen={false} // Start expanded or collapsed
132
134
  />
@@ -151,11 +153,49 @@ const version = useFeatureFlagValue<AppFlags>('apiVersion');
151
153
  // TypeScript knows this is 'v2' (string literal type)
152
154
  ```
153
155
 
156
+ ### Config File Pattern
157
+
158
+ For larger projects, you can use `defineFlags` to create a centralized config file:
159
+
160
+ ```ts
161
+ // flags.config.ts
162
+ import { defineFlags } from '@localflag/react';
163
+
164
+ export const flags = defineFlags({
165
+ darkMode: false,
166
+ newCheckout: true,
167
+ maxRetries: 3,
168
+ });
169
+
170
+ export type AppFlags = typeof flags;
171
+ ```
172
+
173
+ Then import it in your app:
174
+
175
+ ```tsx
176
+ // App.tsx
177
+ import { flags, AppFlags } from './flags.config';
178
+ import { FeatureFlagProvider, useFeatureFlag } from '@localflag/react';
179
+
180
+ function App() {
181
+ return (
182
+ <FeatureFlagProvider<AppFlags> defaultFlags={flags}>
183
+ <MyApp />
184
+ </FeatureFlagProvider>
185
+ );
186
+ }
187
+ ```
188
+
189
+ Benefits:
190
+ - All flags managed in one place
191
+ - Easy to export and share types across your app
192
+ - Enables future tooling integration
193
+
154
194
  ## Persistence
155
195
 
156
196
  By default, flag overrides are persisted to `localStorage`. This means:
157
197
 
158
- - Flags you toggle in DevTools persist across page reloads
198
+ - Flags you toggle in LocalFlagDevTools persist across page reloads
159
199
  - Each user can have their own overrides for testing
160
200
  - Call `resetFlags()` to clear all overrides and return to defaults
161
201
 
package/dist/index.d.ts CHANGED
@@ -4,6 +4,9 @@ type FlagValue = boolean | string | number;
4
4
  interface FeatureFlags {
5
5
  [key: string]: FlagValue;
6
6
  }
7
+ interface FlagDescriptions {
8
+ [key: string]: string;
9
+ }
7
10
  interface FeatureFlagContextValue<T extends FeatureFlags = FeatureFlags> {
8
11
  flags: T;
9
12
  isEnabled: (flagName: keyof T) => boolean;
@@ -14,11 +17,12 @@ interface FeatureFlagContextValue<T extends FeatureFlags = FeatureFlags> {
14
17
  interface FeatureFlagProviderProps<T extends FeatureFlags = FeatureFlags> {
15
18
  children: React.ReactNode;
16
19
  defaultFlags: T;
20
+ descriptions?: Partial<Record<keyof T, string>>;
17
21
  storageKey?: string;
18
22
  persistOverrides?: boolean;
19
23
  }
20
24
 
21
- declare function FeatureFlagProvider<T extends FeatureFlags>({ children, defaultFlags, storageKey, persistOverrides, }: FeatureFlagProviderProps<T>): react_jsx_runtime.JSX.Element;
25
+ declare function FeatureFlagProvider<T extends FeatureFlags>({ children, defaultFlags, descriptions, storageKey, persistOverrides, }: FeatureFlagProviderProps<T>): react_jsx_runtime.JSX.Element;
22
26
  declare function useFeatureFlagContext<T extends FeatureFlags = FeatureFlags>(): FeatureFlagContextValue<T>;
23
27
 
24
28
  /**
@@ -49,10 +53,73 @@ declare function FeatureFlag<T extends FeatureFlags = FeatureFlags>({ flag, chil
49
53
  fallback?: React.ReactNode;
50
54
  }): React.ReactNode;
51
55
 
56
+ type Position = "bottom-right" | "bottom-left" | "top-right" | "top-left";
52
57
  interface DevToolsProps {
53
- position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
58
+ position?: Position;
54
59
  defaultOpen?: boolean;
60
+ /**
61
+ * Keyboard shortcut to toggle the DevTools panel.
62
+ * Set to `false` to disable the shortcut.
63
+ * @default "mod+shift+f" (Cmd+Shift+F on Mac, Ctrl+Shift+F on Windows/Linux)
64
+ */
65
+ shortcut?: string | false;
55
66
  }
56
- declare function DevTools({ position, defaultOpen, }: DevToolsProps): react_jsx_runtime.JSX.Element | null;
67
+ declare function LocalFlagDevTools({ position, defaultOpen, shortcut, }: DevToolsProps): react_jsx_runtime.JSX.Element | null;
68
+ /** @deprecated Use `LocalFlagDevTools` instead */
69
+ declare const DevTools: typeof LocalFlagDevTools;
70
+
71
+ /**
72
+ * Helper function to define feature flags with type safety.
73
+ * This is a type-only helper with no runtime logic - it simply returns the flags as-is.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * // flags.config.ts
78
+ * import { defineFlags } from '@localflag/react';
79
+ *
80
+ * export const flags = defineFlags({
81
+ * darkMode: false,
82
+ * newCheckout: true,
83
+ * maxRetries: 3,
84
+ * });
85
+ *
86
+ * export type AppFlags = typeof flags;
87
+ * ```
88
+ */
89
+ declare function defineFlags<T extends Record<string, FlagValue>>(flags: T): T;
90
+ /**
91
+ * Flag configuration with value and optional description.
92
+ */
93
+ interface FlagConfig<T extends FlagValue = FlagValue> {
94
+ value: T;
95
+ description?: string;
96
+ }
97
+ type FlagInput<T extends FlagValue = FlagValue> = T | FlagConfig<T>;
98
+ type ExtractFlagValue<T> = T extends FlagConfig<infer V> ? V : T;
99
+ type ExtractFlags<T extends Record<string, FlagInput>> = {
100
+ [K in keyof T]: ExtractFlagValue<T[K]>;
101
+ };
102
+ /**
103
+ * Helper function to define feature flags with descriptions.
104
+ * Returns both the flags object and a descriptions object.
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * // flags.config.ts
109
+ * import { createFlags } from '@localflag/react';
110
+ *
111
+ * export const { flags, descriptions } = createFlags({
112
+ * darkMode: { value: false, description: "Enable dark theme" },
113
+ * newCheckout: { value: true, description: "Use new checkout flow" },
114
+ * maxRetries: 3, // Simple value without description
115
+ * });
116
+ *
117
+ * export type AppFlags = typeof flags;
118
+ * ```
119
+ */
120
+ declare function createFlags<T extends Record<string, FlagInput>>(config: T): {
121
+ flags: ExtractFlags<T>;
122
+ descriptions: Partial<Record<keyof T, string>>;
123
+ };
57
124
 
58
- export { DevTools, FeatureFlag, type FeatureFlagContextValue, FeatureFlagProvider, type FeatureFlagProviderProps, type FeatureFlags, type FlagValue, useFeatureFlag, useFeatureFlagContext, useFeatureFlagControls, useFeatureFlagValue, useFeatureFlags };
125
+ export { DevTools, FeatureFlag, type FeatureFlagContextValue, FeatureFlagProvider, type FeatureFlagProviderProps, type FeatureFlags, type FlagConfig, type FlagDescriptions, type FlagValue, LocalFlagDevTools, createFlags, defineFlags, useFeatureFlag, useFeatureFlagContext, useFeatureFlagControls, useFeatureFlagValue, useFeatureFlags };
package/dist/index.js CHANGED
@@ -46,6 +46,7 @@ function notifyListeners() {
46
46
  function FeatureFlagProvider({
47
47
  children,
48
48
  defaultFlags,
49
+ descriptions = {},
49
50
  storageKey = STORAGE_KEY_DEFAULT,
50
51
  persistOverrides = true
51
52
  }) {
@@ -57,9 +58,13 @@ function FeatureFlagProvider({
57
58
  [defaultFlags, overrides]
58
59
  );
59
60
  useEffect(() => {
60
- currentState = { flags, defaultFlags };
61
+ currentState = {
62
+ flags,
63
+ defaultFlags,
64
+ descriptions
65
+ };
61
66
  notifyListeners();
62
- }, [flags, defaultFlags]);
67
+ }, [flags, defaultFlags, descriptions]);
63
68
  useEffect(() => {
64
69
  if (persistOverrides) {
65
70
  setStoredOverrides(storageKey, overrides);
@@ -141,174 +146,271 @@ function FeatureFlag({
141
146
  }
142
147
 
143
148
  // src/devtools.tsx
144
- import { useState as useState2 } from "react";
145
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
149
+ import { useState as useState2, useEffect as useEffect2 } from "react";
150
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
151
+ var POSITION_STORAGE_KEY = "localflag:position";
146
152
  var positionStyles = {
147
- "bottom-right": { bottom: 16, right: 16 },
148
- "bottom-left": { bottom: 16, left: 16 },
149
- "top-right": { top: 16, right: 16 },
150
- "top-left": { top: 16, left: 16 }
153
+ "bottom-right": { bottom: 12, right: 12 },
154
+ "bottom-left": { bottom: 12, left: 12 },
155
+ "top-right": { top: 12, right: 12 },
156
+ "top-left": { top: 12, left: 12 }
151
157
  };
152
158
  var baseStyles = {
153
159
  position: "fixed",
154
160
  zIndex: 99999,
155
- fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
156
- fontSize: 13
161
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
162
+ fontSize: 12,
163
+ lineHeight: 1.5,
164
+ WebkitFontSmoothing: "antialiased"
157
165
  };
158
166
  var panelStyles = {
159
- backgroundColor: "#1a1a2e",
160
- border: "1px solid #2d2d44",
161
- borderRadius: 8,
162
- boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
167
+ backgroundColor: "#0a0a0a",
168
+ border: "1px solid rgba(255, 255, 255, 0.08)",
169
+ borderRadius: 10,
170
+ boxShadow: "0 0 0 1px rgba(0, 0, 0, 0.5), 0 16px 70px rgba(0, 0, 0, 0.6)",
163
171
  overflow: "hidden",
164
- minWidth: 280,
165
- maxWidth: 360,
166
- maxHeight: "70vh"
172
+ minWidth: 260,
173
+ maxWidth: 320,
174
+ maxHeight: "60vh"
167
175
  };
168
176
  var headerStyles = {
169
177
  display: "flex",
170
178
  alignItems: "center",
171
179
  justifyContent: "space-between",
172
180
  padding: "10px 12px",
173
- backgroundColor: "#16162a",
174
- borderBottom: "1px solid #2d2d44",
181
+ borderBottom: "1px solid rgba(255, 255, 255, 0.06)",
175
182
  cursor: "pointer",
176
183
  userSelect: "none"
177
184
  };
178
185
  var titleStyles = {
179
- color: "#e0e0e0",
180
- fontWeight: 600,
181
- fontSize: 12,
182
- textTransform: "uppercase",
183
- letterSpacing: "0.5px",
186
+ color: "rgba(255, 255, 255, 0.9)",
187
+ fontWeight: 500,
188
+ fontSize: 11,
189
+ letterSpacing: "0.01em",
184
190
  display: "flex",
185
191
  alignItems: "center",
186
- gap: 6
192
+ gap: 8
193
+ };
194
+ var tabBarStyles = {
195
+ display: "flex",
196
+ borderBottom: "1px solid rgba(255, 255, 255, 0.06)",
197
+ padding: "0 12px",
198
+ gap: 4
199
+ };
200
+ var tabStyles = {
201
+ padding: "8px 10px",
202
+ fontSize: 11,
203
+ fontWeight: 500,
204
+ color: "rgba(255, 255, 255, 0.5)",
205
+ backgroundColor: "transparent",
206
+ border: "none",
207
+ borderBottom: "2px solid transparent",
208
+ cursor: "pointer",
209
+ transition: "color 0.15s ease",
210
+ fontFamily: "inherit",
211
+ marginBottom: -1
212
+ };
213
+ var tabActiveStyles = {
214
+ ...tabStyles,
215
+ color: "rgba(255, 255, 255, 0.9)",
216
+ borderBottomColor: "#fff"
187
217
  };
188
218
  var contentStyles = {
189
219
  padding: 0,
190
220
  overflowY: "auto",
191
- maxHeight: "calc(70vh - 50px)"
221
+ maxHeight: "calc(60vh - 84px)"
192
222
  };
193
223
  var flagRowStyles = {
194
224
  display: "flex",
195
225
  alignItems: "center",
196
226
  justifyContent: "space-between",
197
- padding: "10px 12px",
198
- borderBottom: "1px solid #2d2d44",
199
- transition: "background-color 0.15s"
227
+ padding: "8px 12px",
228
+ borderBottom: "1px solid rgba(255, 255, 255, 0.04)",
229
+ transition: "background-color 0.15s ease",
230
+ gap: 12
200
231
  };
201
- var flagNameStyles = {
202
- color: "#c0c0c0",
203
- fontSize: 13,
204
- fontWeight: 500,
232
+ var flagRowHoverStyles = {
233
+ backgroundColor: "rgba(255, 255, 255, 0.03)"
234
+ };
235
+ var flagNameContainerStyles = {
205
236
  flex: 1,
206
237
  overflow: "hidden",
238
+ display: "flex",
239
+ flexDirection: "column",
240
+ gap: 2,
241
+ minWidth: 0
242
+ };
243
+ var flagNameStyles = {
244
+ color: "rgba(255, 255, 255, 0.7)",
245
+ fontSize: 12,
246
+ fontWeight: 400,
247
+ overflow: "hidden",
248
+ textOverflow: "ellipsis",
249
+ whiteSpace: "nowrap",
250
+ display: "flex",
251
+ alignItems: "center",
252
+ gap: 6
253
+ };
254
+ var flagDescriptionStyles = {
255
+ color: "rgba(255, 255, 255, 0.4)",
256
+ fontSize: 10,
257
+ fontWeight: 400,
258
+ overflow: "hidden",
207
259
  textOverflow: "ellipsis",
208
260
  whiteSpace: "nowrap"
209
261
  };
210
262
  var toggleStyles = {
211
263
  position: "relative",
212
- width: 40,
213
- height: 22,
214
- backgroundColor: "#3d3d5c",
215
- borderRadius: 11,
264
+ width: 28,
265
+ height: 16,
266
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
267
+ borderRadius: 8,
216
268
  cursor: "pointer",
217
- transition: "background-color 0.2s",
269
+ transition: "background-color 0.15s ease",
218
270
  flexShrink: 0,
219
271
  border: "none",
220
272
  padding: 0
221
273
  };
222
274
  var toggleActiveStyles = {
223
275
  ...toggleStyles,
224
- backgroundColor: "#4ade80"
276
+ backgroundColor: "#fff"
225
277
  };
226
278
  var toggleKnobStyles = {
227
279
  position: "absolute",
228
280
  top: 2,
229
281
  left: 2,
230
- width: 18,
231
- height: 18,
232
- backgroundColor: "#fff",
282
+ width: 12,
283
+ height: 12,
284
+ backgroundColor: "rgba(255, 255, 255, 0.5)",
233
285
  borderRadius: "50%",
234
- transition: "transform 0.2s",
235
- boxShadow: "0 1px 3px rgba(0, 0, 0, 0.3)"
286
+ transition: "transform 0.15s ease, background-color 0.15s ease"
236
287
  };
237
288
  var toggleKnobActiveStyles = {
238
289
  ...toggleKnobStyles,
239
- transform: "translateX(18px)"
290
+ transform: "translateX(12px)",
291
+ backgroundColor: "#0a0a0a"
240
292
  };
241
293
  var inputStyles = {
242
- backgroundColor: "#2d2d44",
243
- border: "1px solid #3d3d5c",
244
- borderRadius: 4,
245
- color: "#e0e0e0",
246
- padding: "4px 8px",
247
- fontSize: 12,
248
- width: 80,
249
- outline: "none"
250
- };
251
- var badgeStyles = {
252
- backgroundColor: "#4ade80",
253
- color: "#1a1a2e",
254
- fontSize: 10,
255
- fontWeight: 700,
256
- padding: "2px 6px",
257
- borderRadius: 4,
258
- marginLeft: 6
294
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
295
+ border: "1px solid rgba(255, 255, 255, 0.1)",
296
+ borderRadius: 5,
297
+ color: "rgba(255, 255, 255, 0.9)",
298
+ padding: "3px 8px",
299
+ fontSize: 11,
300
+ width: 72,
301
+ outline: "none",
302
+ transition: "border-color 0.15s ease, background-color 0.15s ease"
259
303
  };
260
- var overrideBadgeStyles = {
261
- backgroundColor: "#f59e0b",
262
- color: "#1a1a2e",
263
- fontSize: 9,
264
- fontWeight: 600,
265
- padding: "1px 4px",
266
- borderRadius: 3,
267
- marginLeft: 6
304
+ var overrideDotStyles = {
305
+ width: 5,
306
+ height: 5,
307
+ borderRadius: "50%",
308
+ backgroundColor: "#3b82f6",
309
+ flexShrink: 0
268
310
  };
269
311
  var buttonStyles = {
270
312
  backgroundColor: "transparent",
271
313
  border: "none",
272
- color: "#888",
314
+ color: "rgba(255, 255, 255, 0.4)",
273
315
  cursor: "pointer",
274
316
  padding: 4,
275
317
  borderRadius: 4,
276
318
  display: "flex",
277
319
  alignItems: "center",
278
320
  justifyContent: "center",
279
- transition: "color 0.15s, background-color 0.15s"
321
+ transition: "color 0.15s ease"
280
322
  };
281
323
  var resetButtonStyles = {
282
- backgroundColor: "#2d2d44",
283
- border: "1px solid #3d3d5c",
284
- borderRadius: 4,
285
- color: "#c0c0c0",
286
- padding: "6px 12px",
324
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
325
+ border: "1px solid rgba(255, 255, 255, 0.08)",
326
+ borderRadius: 6,
327
+ color: "rgba(255, 255, 255, 0.6)",
328
+ padding: "6px 10px",
287
329
  fontSize: 11,
330
+ fontWeight: 500,
288
331
  cursor: "pointer",
289
- margin: "8px 12px 12px",
332
+ margin: "8px 12px 10px",
290
333
  width: "calc(100% - 24px)",
291
- transition: "background-color 0.15s"
334
+ transition: "background-color 0.15s ease, color 0.15s ease",
335
+ fontFamily: "inherit"
292
336
  };
293
337
  var fabStyles = {
294
- width: 48,
295
- height: 48,
296
- borderRadius: "50%",
297
- backgroundColor: "#4ade80",
298
- border: "none",
338
+ height: 32,
339
+ paddingLeft: 10,
340
+ paddingRight: 10,
341
+ borderRadius: 8,
342
+ backgroundColor: "#0a0a0a",
343
+ border: "1px solid rgba(255, 255, 255, 0.1)",
344
+ cursor: "pointer",
345
+ display: "flex",
346
+ alignItems: "center",
347
+ justifyContent: "center",
348
+ gap: 6,
349
+ boxShadow: "0 0 0 1px rgba(0, 0, 0, 0.5), 0 4px 16px rgba(0, 0, 0, 0.4)",
350
+ transition: "background-color 0.15s ease, border-color 0.15s ease",
351
+ fontFamily: "inherit",
352
+ fontSize: 11,
353
+ fontWeight: 500,
354
+ color: "rgba(255, 255, 255, 0.8)"
355
+ };
356
+ var badgeStyles = {
357
+ backgroundColor: "#3b82f6",
358
+ color: "#fff",
359
+ fontSize: 9,
360
+ fontWeight: 600,
361
+ minWidth: 14,
362
+ height: 14,
363
+ padding: "0 4px",
364
+ borderRadius: 7,
365
+ display: "inline-flex",
366
+ alignItems: "center",
367
+ justifyContent: "center"
368
+ };
369
+ var optionsSectionStyles = {
370
+ padding: "12px"
371
+ };
372
+ var optionsLabelStyles = {
373
+ fontSize: 10,
374
+ fontWeight: 500,
375
+ color: "rgba(255, 255, 255, 0.5)",
376
+ textTransform: "uppercase",
377
+ letterSpacing: "0.05em",
378
+ marginBottom: 8,
379
+ display: "block"
380
+ };
381
+ var positionGridStyles = {
382
+ display: "grid",
383
+ gridTemplateColumns: "1fr 1fr",
384
+ gap: 6
385
+ };
386
+ var positionButtonStyles = {
387
+ padding: "8px",
388
+ fontSize: 10,
389
+ fontWeight: 500,
390
+ color: "rgba(255, 255, 255, 0.6)",
391
+ backgroundColor: "rgba(255, 255, 255, 0.03)",
392
+ border: "1px solid rgba(255, 255, 255, 0.08)",
393
+ borderRadius: 6,
299
394
  cursor: "pointer",
395
+ transition: "all 0.15s ease",
396
+ fontFamily: "inherit",
300
397
  display: "flex",
301
398
  alignItems: "center",
302
399
  justifyContent: "center",
303
- boxShadow: "0 4px 12px rgba(74, 222, 128, 0.4)",
304
- transition: "transform 0.15s, box-shadow 0.15s"
400
+ gap: 6
305
401
  };
306
- function FlagIcon() {
402
+ var positionButtonActiveStyles = {
403
+ ...positionButtonStyles,
404
+ color: "rgba(255, 255, 255, 0.9)",
405
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
406
+ borderColor: "rgba(255, 255, 255, 0.2)"
407
+ };
408
+ function FlagIcon({ size = 14 }) {
307
409
  return /* @__PURE__ */ jsxs(
308
410
  "svg",
309
411
  {
310
- width: "20",
311
- height: "20",
412
+ width: size,
413
+ height: size,
312
414
  viewBox: "0 0 24 24",
313
415
  fill: "none",
314
416
  stroke: "currentColor",
@@ -322,22 +424,57 @@ function FlagIcon() {
322
424
  }
323
425
  );
324
426
  }
325
- function CloseIcon() {
326
- return /* @__PURE__ */ jsxs(
427
+ function ChevronIcon({ direction }) {
428
+ return /* @__PURE__ */ jsx2(
327
429
  "svg",
328
430
  {
329
- width: "16",
330
- height: "16",
431
+ width: "12",
432
+ height: "12",
331
433
  viewBox: "0 0 24 24",
332
434
  fill: "none",
333
435
  stroke: "currentColor",
334
436
  strokeWidth: "2",
335
437
  strokeLinecap: "round",
336
438
  strokeLinejoin: "round",
337
- children: [
338
- /* @__PURE__ */ jsx2("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
339
- /* @__PURE__ */ jsx2("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
340
- ]
439
+ style: {
440
+ transform: direction === "up" ? "rotate(180deg)" : "rotate(0deg)",
441
+ transition: "transform 0.15s ease"
442
+ },
443
+ children: /* @__PURE__ */ jsx2("polyline", { points: "6 9 12 15 18 9" })
444
+ }
445
+ );
446
+ }
447
+ function PositionIcon({ position }) {
448
+ const dotPosition = {
449
+ "top-left": { top: 2, left: 2 },
450
+ "top-right": { top: 2, right: 2 },
451
+ "bottom-left": { bottom: 2, left: 2 },
452
+ "bottom-right": { bottom: 2, right: 2 }
453
+ }[position];
454
+ return /* @__PURE__ */ jsx2(
455
+ "div",
456
+ {
457
+ style: {
458
+ width: 14,
459
+ height: 14,
460
+ border: "1px solid currentColor",
461
+ borderRadius: 2,
462
+ position: "relative",
463
+ opacity: 0.7
464
+ },
465
+ children: /* @__PURE__ */ jsx2(
466
+ "div",
467
+ {
468
+ style: {
469
+ position: "absolute",
470
+ width: 4,
471
+ height: 4,
472
+ backgroundColor: "currentColor",
473
+ borderRadius: 1,
474
+ ...dotPosition
475
+ }
476
+ }
477
+ )
341
478
  }
342
479
  );
343
480
  }
@@ -345,9 +482,11 @@ function FlagRow({
345
482
  name,
346
483
  value,
347
484
  defaultValue,
485
+ description,
348
486
  onToggle,
349
487
  onChange
350
488
  }) {
489
+ const [isHovered, setIsHovered] = useState2(false);
351
490
  const isOverridden = value !== defaultValue;
352
491
  const isBoolean = typeof value === "boolean";
353
492
  const handleInputChange = (e) => {
@@ -359,43 +498,136 @@ function FlagRow({
359
498
  onChange(newValue);
360
499
  }
361
500
  };
362
- return /* @__PURE__ */ jsxs("div", { style: flagRowStyles, children: [
363
- /* @__PURE__ */ jsxs("span", { style: flagNameStyles, children: [
364
- name,
365
- isOverridden && /* @__PURE__ */ jsx2("span", { style: overrideBadgeStyles, children: "override" })
366
- ] }),
367
- isBoolean ? /* @__PURE__ */ jsx2(
368
- "button",
369
- {
370
- style: value ? toggleActiveStyles : toggleStyles,
371
- onClick: onToggle,
372
- type: "button",
373
- "aria-label": `Toggle ${name}`,
374
- children: /* @__PURE__ */ jsx2("span", { style: value ? toggleKnobActiveStyles : toggleKnobStyles })
375
- }
376
- ) : /* @__PURE__ */ jsx2(
377
- "input",
378
- {
379
- style: inputStyles,
380
- type: "text",
381
- value: String(value),
382
- onChange: handleInputChange
383
- }
384
- )
501
+ return /* @__PURE__ */ jsxs(
502
+ "div",
503
+ {
504
+ style: {
505
+ ...flagRowStyles,
506
+ ...isHovered ? flagRowHoverStyles : {}
507
+ },
508
+ onMouseEnter: () => setIsHovered(true),
509
+ onMouseLeave: () => setIsHovered(false),
510
+ children: [
511
+ /* @__PURE__ */ jsxs("div", { style: flagNameContainerStyles, children: [
512
+ /* @__PURE__ */ jsxs("span", { style: flagNameStyles, children: [
513
+ isOverridden && /* @__PURE__ */ jsx2("span", { style: overrideDotStyles }),
514
+ name
515
+ ] }),
516
+ description && /* @__PURE__ */ jsx2("span", { style: flagDescriptionStyles, children: description })
517
+ ] }),
518
+ isBoolean ? /* @__PURE__ */ jsx2(
519
+ "button",
520
+ {
521
+ style: value ? toggleActiveStyles : toggleStyles,
522
+ onClick: onToggle,
523
+ type: "button",
524
+ "aria-label": `Toggle ${name}`,
525
+ children: /* @__PURE__ */ jsx2("span", { style: value ? toggleKnobActiveStyles : toggleKnobStyles })
526
+ }
527
+ ) : /* @__PURE__ */ jsx2(
528
+ "input",
529
+ {
530
+ style: inputStyles,
531
+ type: "text",
532
+ value: String(value),
533
+ onChange: handleInputChange,
534
+ onFocus: (e) => {
535
+ e.target.style.borderColor = "rgba(255, 255, 255, 0.2)";
536
+ e.target.style.backgroundColor = "rgba(255, 255, 255, 0.08)";
537
+ },
538
+ onBlur: (e) => {
539
+ e.target.style.borderColor = "rgba(255, 255, 255, 0.1)";
540
+ e.target.style.backgroundColor = "rgba(255, 255, 255, 0.05)";
541
+ }
542
+ }
543
+ )
544
+ ]
545
+ }
546
+ );
547
+ }
548
+ function OptionsTab({
549
+ position,
550
+ onPositionChange
551
+ }) {
552
+ const [hoveredPosition, setHoveredPosition] = useState2(null);
553
+ const positions = [
554
+ { value: "top-left", label: "Top Left" },
555
+ { value: "top-right", label: "Top Right" },
556
+ { value: "bottom-left", label: "Bottom Left" },
557
+ { value: "bottom-right", label: "Bottom Right" }
558
+ ];
559
+ return /* @__PURE__ */ jsxs("div", { style: optionsSectionStyles, children: [
560
+ /* @__PURE__ */ jsx2("span", { style: optionsLabelStyles, children: "Position" }),
561
+ /* @__PURE__ */ jsx2("div", { style: positionGridStyles, children: positions.map(({ value, label }) => {
562
+ const isActive = position === value;
563
+ const isHovered = hoveredPosition === value;
564
+ return /* @__PURE__ */ jsxs(
565
+ "button",
566
+ {
567
+ style: {
568
+ ...isActive ? positionButtonActiveStyles : positionButtonStyles,
569
+ ...isHovered && !isActive ? {
570
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
571
+ borderColor: "rgba(255, 255, 255, 0.12)"
572
+ } : {}
573
+ },
574
+ onClick: () => onPositionChange(value),
575
+ onMouseEnter: () => setHoveredPosition(value),
576
+ onMouseLeave: () => setHoveredPosition(null),
577
+ type: "button",
578
+ children: [
579
+ /* @__PURE__ */ jsx2(PositionIcon, { position: value }),
580
+ label
581
+ ]
582
+ },
583
+ value
584
+ );
585
+ }) })
385
586
  ] });
386
587
  }
387
588
  function DevToolsInner({
388
- position,
389
- defaultOpen
589
+ position: initialPosition,
590
+ defaultOpen,
591
+ shortcut
390
592
  }) {
391
593
  const [isOpen, setIsOpen] = useState2(defaultOpen);
594
+ const [activeTab, setActiveTab] = useState2("flags");
595
+ const [position, setPosition] = useState2(() => {
596
+ if (typeof window !== "undefined") {
597
+ const stored = localStorage.getItem(POSITION_STORAGE_KEY);
598
+ if (stored && stored in positionStyles) {
599
+ return stored;
600
+ }
601
+ }
602
+ return initialPosition;
603
+ });
604
+ const [fabHovered, setFabHovered] = useState2(false);
605
+ const [resetHovered, setResetHovered] = useState2(false);
606
+ const [headerHovered, setHeaderHovered] = useState2(false);
392
607
  const flagState = useFlagState();
393
608
  const context = useFeatureFlagContext();
609
+ useEffect2(() => {
610
+ if (shortcut === false) return;
611
+ const handleKeyDown = (e) => {
612
+ const isMod = e.metaKey || e.ctrlKey;
613
+ if (isMod && e.shiftKey && e.key.toLowerCase() === "f") {
614
+ e.preventDefault();
615
+ setIsOpen((prev) => !prev);
616
+ }
617
+ };
618
+ window.addEventListener("keydown", handleKeyDown);
619
+ return () => window.removeEventListener("keydown", handleKeyDown);
620
+ }, [shortcut]);
621
+ useEffect2(() => {
622
+ if (typeof window !== "undefined") {
623
+ localStorage.setItem(POSITION_STORAGE_KEY, position);
624
+ }
625
+ }, [position]);
394
626
  if (!flagState) {
395
627
  return null;
396
628
  }
397
629
  const { setFlag, resetFlags } = context;
398
- const { flags, defaultFlags } = flagState;
630
+ const { flags, defaultFlags, descriptions } = flagState;
399
631
  const flagEntries = Object.entries(flags);
400
632
  const overrideCount = flagEntries.filter(
401
633
  ([key, value]) => value !== defaultFlags[key]
@@ -410,54 +642,169 @@ function DevToolsInner({
410
642
  setFlag(key, value);
411
643
  };
412
644
  if (!isOpen) {
413
- return /* @__PURE__ */ jsx2("div", { style: { ...baseStyles, ...positionStyles[position] }, children: /* @__PURE__ */ jsx2(
645
+ return /* @__PURE__ */ jsx2("div", { style: { ...baseStyles, ...positionStyles[position] }, children: /* @__PURE__ */ jsxs(
414
646
  "button",
415
647
  {
416
- style: fabStyles,
648
+ style: {
649
+ ...fabStyles,
650
+ ...fabHovered ? {
651
+ backgroundColor: "#141414",
652
+ borderColor: "rgba(255, 255, 255, 0.15)"
653
+ } : {}
654
+ },
417
655
  onClick: () => setIsOpen(true),
656
+ onMouseEnter: () => setFabHovered(true),
657
+ onMouseLeave: () => setFabHovered(false),
418
658
  type: "button",
419
659
  "aria-label": "Open feature flags devtools",
420
- children: /* @__PURE__ */ jsx2("span", { style: { color: "#1a1a2e" }, children: /* @__PURE__ */ jsx2(FlagIcon, {}) })
660
+ children: [
661
+ /* @__PURE__ */ jsx2(FlagIcon, { size: 12 }),
662
+ /* @__PURE__ */ jsx2("span", { children: "Flags" }),
663
+ overrideCount > 0 && /* @__PURE__ */ jsx2("span", { style: badgeStyles, children: overrideCount })
664
+ ]
421
665
  }
422
666
  ) });
423
667
  }
424
668
  return /* @__PURE__ */ jsx2("div", { style: { ...baseStyles, ...positionStyles[position] }, children: /* @__PURE__ */ jsxs("div", { style: panelStyles, children: [
425
- /* @__PURE__ */ jsxs("div", { style: headerStyles, onClick: () => setIsOpen(false), children: [
426
- /* @__PURE__ */ jsxs("span", { style: titleStyles, children: [
427
- /* @__PURE__ */ jsx2(FlagIcon, {}),
428
- "Feature Flags",
429
- overrideCount > 0 && /* @__PURE__ */ jsx2("span", { style: badgeStyles, children: overrideCount })
430
- ] }),
431
- /* @__PURE__ */ jsx2("button", { style: buttonStyles, type: "button", "aria-label": "Close", children: /* @__PURE__ */ jsx2(CloseIcon, {}) })
432
- ] }),
433
- /* @__PURE__ */ jsx2("div", { style: contentStyles, children: flagEntries.map(([key, value]) => /* @__PURE__ */ jsx2(
434
- FlagRow,
669
+ /* @__PURE__ */ jsxs(
670
+ "div",
435
671
  {
436
- name: key,
437
- value,
438
- defaultValue: defaultFlags[key],
439
- onToggle: () => handleToggle(key),
440
- onChange: (newValue) => handleChange(key, newValue)
441
- },
442
- key
443
- )) }),
444
- overrideCount > 0 && /* @__PURE__ */ jsx2("button", { style: resetButtonStyles, onClick: resetFlags, type: "button", children: "Reset all overrides" })
672
+ style: {
673
+ ...headerStyles,
674
+ ...headerHovered ? { backgroundColor: "rgba(255, 255, 255, 0.02)" } : {}
675
+ },
676
+ onClick: () => setIsOpen(false),
677
+ onMouseEnter: () => setHeaderHovered(true),
678
+ onMouseLeave: () => setHeaderHovered(false),
679
+ children: [
680
+ /* @__PURE__ */ jsxs("span", { style: titleStyles, children: [
681
+ /* @__PURE__ */ jsx2(FlagIcon, { size: 12 }),
682
+ "Feature Flags",
683
+ overrideCount > 0 && /* @__PURE__ */ jsx2("span", { style: badgeStyles, children: overrideCount })
684
+ ] }),
685
+ /* @__PURE__ */ jsx2(
686
+ "button",
687
+ {
688
+ style: {
689
+ ...buttonStyles,
690
+ ...headerHovered ? { color: "rgba(255, 255, 255, 0.6)" } : {}
691
+ },
692
+ type: "button",
693
+ "aria-label": "Close",
694
+ children: /* @__PURE__ */ jsx2(ChevronIcon, { direction: "down" })
695
+ }
696
+ )
697
+ ]
698
+ }
699
+ ),
700
+ /* @__PURE__ */ jsxs("div", { style: tabBarStyles, children: [
701
+ /* @__PURE__ */ jsx2(
702
+ "button",
703
+ {
704
+ style: activeTab === "flags" ? tabActiveStyles : tabStyles,
705
+ onClick: (e) => {
706
+ e.stopPropagation();
707
+ setActiveTab("flags");
708
+ },
709
+ type: "button",
710
+ children: "Flags"
711
+ }
712
+ ),
713
+ /* @__PURE__ */ jsx2(
714
+ "button",
715
+ {
716
+ style: activeTab === "options" ? tabActiveStyles : tabStyles,
717
+ onClick: (e) => {
718
+ e.stopPropagation();
719
+ setActiveTab("options");
720
+ },
721
+ type: "button",
722
+ children: "Options"
723
+ }
724
+ )
725
+ ] }),
726
+ activeTab === "flags" ? /* @__PURE__ */ jsxs(Fragment, { children: [
727
+ /* @__PURE__ */ jsx2("div", { style: contentStyles, children: flagEntries.map(([key, value]) => /* @__PURE__ */ jsx2(
728
+ FlagRow,
729
+ {
730
+ name: key,
731
+ value,
732
+ defaultValue: defaultFlags[key],
733
+ description: descriptions[key],
734
+ onToggle: () => handleToggle(key),
735
+ onChange: (newValue) => handleChange(key, newValue)
736
+ },
737
+ key
738
+ )) }),
739
+ overrideCount > 0 && /* @__PURE__ */ jsx2(
740
+ "button",
741
+ {
742
+ style: {
743
+ ...resetButtonStyles,
744
+ ...resetHovered ? {
745
+ backgroundColor: "rgba(255, 255, 255, 0.08)",
746
+ color: "rgba(255, 255, 255, 0.8)"
747
+ } : {}
748
+ },
749
+ onClick: resetFlags,
750
+ onMouseEnter: () => setResetHovered(true),
751
+ onMouseLeave: () => setResetHovered(false),
752
+ type: "button",
753
+ children: "Reset overrides"
754
+ }
755
+ )
756
+ ] }) : /* @__PURE__ */ jsx2(OptionsTab, { position, onPositionChange: setPosition })
445
757
  ] }) });
446
758
  }
447
- function DevTools({
759
+ function LocalFlagDevTools({
448
760
  position = "bottom-right",
449
- defaultOpen = false
761
+ defaultOpen = false,
762
+ shortcut = "mod+shift+f"
450
763
  }) {
451
764
  const isDev = typeof __DEV__ !== "undefined" ? __DEV__ : true;
452
765
  if (!isDev) {
453
766
  return null;
454
767
  }
455
- return /* @__PURE__ */ jsx2(DevToolsInner, { position, defaultOpen });
768
+ return /* @__PURE__ */ jsx2(
769
+ DevToolsInner,
770
+ {
771
+ position,
772
+ defaultOpen,
773
+ shortcut
774
+ }
775
+ );
776
+ }
777
+ var DevTools = LocalFlagDevTools;
778
+
779
+ // src/config.ts
780
+ function defineFlags(flags) {
781
+ return flags;
782
+ }
783
+ function createFlags(config) {
784
+ const flags = {};
785
+ const descriptions = {};
786
+ for (const [key, value] of Object.entries(config)) {
787
+ if (typeof value === "object" && value !== null && "value" in value) {
788
+ flags[key] = value.value;
789
+ if (value.description) {
790
+ descriptions[key] = value.description;
791
+ }
792
+ } else {
793
+ flags[key] = value;
794
+ }
795
+ }
796
+ return {
797
+ flags,
798
+ descriptions
799
+ };
456
800
  }
457
801
  export {
458
802
  DevTools,
459
803
  FeatureFlag,
460
804
  FeatureFlagProvider,
805
+ LocalFlagDevTools,
806
+ createFlags,
807
+ defineFlags,
461
808
  useFeatureFlag,
462
809
  useFeatureFlagContext,
463
810
  useFeatureFlagControls,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/context.tsx","../src/hooks.ts","../src/devtools.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport type {\n FeatureFlagContextValue,\n FeatureFlagProviderProps,\n FeatureFlags,\n} from \"./types\";\n\nconst FeatureFlagContext = createContext<FeatureFlagContextValue | null>(null);\n\nconst STORAGE_KEY_DEFAULT = \"localflag:overrides\";\n\nfunction getStoredOverrides<T extends FeatureFlags>(\n storageKey: string\n): Partial<T> {\n if (typeof window === \"undefined\") return {};\n try {\n const stored = localStorage.getItem(storageKey);\n return stored ? JSON.parse(stored) : {};\n } catch {\n return {};\n }\n}\n\nfunction setStoredOverrides<T extends FeatureFlags>(\n storageKey: string,\n overrides: Partial<T>\n): void {\n if (typeof window === \"undefined\") return;\n try {\n if (Object.keys(overrides).length === 0) {\n localStorage.removeItem(storageKey);\n } else {\n localStorage.setItem(storageKey, JSON.stringify(overrides));\n }\n } catch {\n // Ignore storage errors\n }\n}\n\n// Event emitter for devtools communication\ntype Listener = () => void;\nconst listeners = new Set<Listener>();\nlet currentState: { flags: FeatureFlags; defaultFlags: FeatureFlags } | null = null;\n\nexport function subscribeToFlagChanges(listener: Listener): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getFlagState() {\n return currentState;\n}\n\nfunction notifyListeners() {\n listeners.forEach((listener) => listener());\n}\n\nexport function FeatureFlagProvider<T extends FeatureFlags>({\n children,\n defaultFlags,\n storageKey = STORAGE_KEY_DEFAULT,\n persistOverrides = true,\n}: FeatureFlagProviderProps<T>) {\n const [overrides, setOverrides] = useState<Partial<T>>(() =>\n persistOverrides ? getStoredOverrides<T>(storageKey) : {}\n );\n\n const flags = useMemo(\n () => ({ ...defaultFlags, ...overrides }) as T,\n [defaultFlags, overrides]\n );\n\n // Update global state for devtools\n useEffect(() => {\n currentState = { flags, defaultFlags };\n notifyListeners();\n }, [flags, defaultFlags]);\n\n // Persist overrides to localStorage\n useEffect(() => {\n if (persistOverrides) {\n setStoredOverrides(storageKey, overrides);\n }\n }, [overrides, storageKey, persistOverrides]);\n\n const isEnabled = useCallback(\n (flagName: keyof T): boolean => {\n const value = flags[flagName];\n return Boolean(value);\n },\n [flags]\n );\n\n const getValue = useCallback(\n <K extends keyof T>(flagName: K): T[K] => {\n return flags[flagName];\n },\n [flags]\n );\n\n const setFlag = useCallback(<K extends keyof T>(flagName: K, value: T[K]) => {\n setOverrides((prev) => ({ ...prev, [flagName]: value }));\n }, []);\n\n const resetFlags = useCallback(() => {\n setOverrides({});\n }, []);\n\n const value = useMemo<FeatureFlagContextValue<T>>(\n () => ({\n flags,\n isEnabled,\n getValue,\n setFlag,\n resetFlags,\n }),\n [flags, isEnabled, getValue, setFlag, resetFlags]\n );\n\n return (\n <FeatureFlagContext.Provider value={value as unknown as FeatureFlagContextValue}>\n {children}\n </FeatureFlagContext.Provider>\n );\n}\n\nexport function useFeatureFlagContext<\n T extends FeatureFlags = FeatureFlags\n>(): FeatureFlagContextValue<T> {\n const context = useContext(FeatureFlagContext);\n if (!context) {\n throw new Error(\n \"useFeatureFlagContext must be used within a FeatureFlagProvider\"\n );\n }\n return context as unknown as FeatureFlagContextValue<T>;\n}\n\n// Hook for devtools to subscribe to flag changes\nexport function useFlagState() {\n return useSyncExternalStore(\n subscribeToFlagChanges,\n getFlagState,\n () => null\n );\n}\n","import { useMemo } from \"react\";\nimport { useFeatureFlagContext } from \"./context\";\nimport type { FeatureFlags } from \"./types\";\n\n/**\n * Hook to check if a feature flag is enabled (truthy)\n */\nexport function useFeatureFlag<T extends FeatureFlags = FeatureFlags>(\n flagName: keyof T\n): boolean {\n const { isEnabled } = useFeatureFlagContext<T>();\n return isEnabled(flagName);\n}\n\n/**\n * Hook to get the raw value of a feature flag\n */\nexport function useFeatureFlagValue<\n T extends FeatureFlags = FeatureFlags,\n K extends keyof T = keyof T\n>(flagName: K): T[K] {\n const { getValue } = useFeatureFlagContext<T>();\n return getValue(flagName);\n}\n\n/**\n * Hook to get all feature flags\n */\nexport function useFeatureFlags<T extends FeatureFlags = FeatureFlags>(): T {\n const { flags } = useFeatureFlagContext<T>();\n return flags;\n}\n\n/**\n * Hook to get flag controls (setFlag, resetFlags)\n */\nexport function useFeatureFlagControls<T extends FeatureFlags = FeatureFlags>() {\n const { setFlag, resetFlags } = useFeatureFlagContext<T>();\n return useMemo(() => ({ setFlag, resetFlags }), [setFlag, resetFlags]);\n}\n\n/**\n * Component wrapper that renders children only if flag is enabled\n */\nexport function FeatureFlag<T extends FeatureFlags = FeatureFlags>({\n flag,\n children,\n fallback = null,\n}: {\n flag: keyof T;\n children: React.ReactNode;\n fallback?: React.ReactNode;\n}): React.ReactNode {\n const enabled = useFeatureFlag<T>(flag);\n return enabled ? children : fallback;\n}\n","import { useState } from \"react\";\nimport { useFlagState, useFeatureFlagContext } from \"./context\";\nimport type { FlagValue, FeatureFlags } from \"./types\";\n\ndeclare const __DEV__: boolean | undefined;\n\ninterface DevToolsProps {\n position?: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\";\n defaultOpen?: boolean;\n}\n\nconst positionStyles: Record<string, React.CSSProperties> = {\n \"bottom-right\": { bottom: 16, right: 16 },\n \"bottom-left\": { bottom: 16, left: 16 },\n \"top-right\": { top: 16, right: 16 },\n \"top-left\": { top: 16, left: 16 },\n};\n\nconst baseStyles: React.CSSProperties = {\n position: \"fixed\",\n zIndex: 99999,\n fontFamily:\n 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, monospace',\n fontSize: 13,\n};\n\nconst panelStyles: React.CSSProperties = {\n backgroundColor: \"#1a1a2e\",\n border: \"1px solid #2d2d44\",\n borderRadius: 8,\n boxShadow: \"0 8px 32px rgba(0, 0, 0, 0.4)\",\n overflow: \"hidden\",\n minWidth: 280,\n maxWidth: 360,\n maxHeight: \"70vh\",\n};\n\nconst headerStyles: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"10px 12px\",\n backgroundColor: \"#16162a\",\n borderBottom: \"1px solid #2d2d44\",\n cursor: \"pointer\",\n userSelect: \"none\",\n};\n\nconst titleStyles: React.CSSProperties = {\n color: \"#e0e0e0\",\n fontWeight: 600,\n fontSize: 12,\n textTransform: \"uppercase\",\n letterSpacing: \"0.5px\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n};\n\nconst contentStyles: React.CSSProperties = {\n padding: 0,\n overflowY: \"auto\",\n maxHeight: \"calc(70vh - 50px)\",\n};\n\nconst flagRowStyles: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"10px 12px\",\n borderBottom: \"1px solid #2d2d44\",\n transition: \"background-color 0.15s\",\n};\n\nconst flagNameStyles: React.CSSProperties = {\n color: \"#c0c0c0\",\n fontSize: 13,\n fontWeight: 500,\n flex: 1,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n};\n\nconst toggleStyles: React.CSSProperties = {\n position: \"relative\",\n width: 40,\n height: 22,\n backgroundColor: \"#3d3d5c\",\n borderRadius: 11,\n cursor: \"pointer\",\n transition: \"background-color 0.2s\",\n flexShrink: 0,\n border: \"none\",\n padding: 0,\n};\n\nconst toggleActiveStyles: React.CSSProperties = {\n ...toggleStyles,\n backgroundColor: \"#4ade80\",\n};\n\nconst toggleKnobStyles: React.CSSProperties = {\n position: \"absolute\",\n top: 2,\n left: 2,\n width: 18,\n height: 18,\n backgroundColor: \"#fff\",\n borderRadius: \"50%\",\n transition: \"transform 0.2s\",\n boxShadow: \"0 1px 3px rgba(0, 0, 0, 0.3)\",\n};\n\nconst toggleKnobActiveStyles: React.CSSProperties = {\n ...toggleKnobStyles,\n transform: \"translateX(18px)\",\n};\n\nconst inputStyles: React.CSSProperties = {\n backgroundColor: \"#2d2d44\",\n border: \"1px solid #3d3d5c\",\n borderRadius: 4,\n color: \"#e0e0e0\",\n padding: \"4px 8px\",\n fontSize: 12,\n width: 80,\n outline: \"none\",\n};\n\nconst badgeStyles: React.CSSProperties = {\n backgroundColor: \"#4ade80\",\n color: \"#1a1a2e\",\n fontSize: 10,\n fontWeight: 700,\n padding: \"2px 6px\",\n borderRadius: 4,\n marginLeft: 6,\n};\n\nconst overrideBadgeStyles: React.CSSProperties = {\n backgroundColor: \"#f59e0b\",\n color: \"#1a1a2e\",\n fontSize: 9,\n fontWeight: 600,\n padding: \"1px 4px\",\n borderRadius: 3,\n marginLeft: 6,\n};\n\nconst buttonStyles: React.CSSProperties = {\n backgroundColor: \"transparent\",\n border: \"none\",\n color: \"#888\",\n cursor: \"pointer\",\n padding: 4,\n borderRadius: 4,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n transition: \"color 0.15s, background-color 0.15s\",\n};\n\nconst resetButtonStyles: React.CSSProperties = {\n backgroundColor: \"#2d2d44\",\n border: \"1px solid #3d3d5c\",\n borderRadius: 4,\n color: \"#c0c0c0\",\n padding: \"6px 12px\",\n fontSize: 11,\n cursor: \"pointer\",\n margin: \"8px 12px 12px\",\n width: \"calc(100% - 24px)\",\n transition: \"background-color 0.15s\",\n};\n\nconst fabStyles: React.CSSProperties = {\n width: 48,\n height: 48,\n borderRadius: \"50%\",\n backgroundColor: \"#4ade80\",\n border: \"none\",\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxShadow: \"0 4px 12px rgba(74, 222, 128, 0.4)\",\n transition: \"transform 0.15s, box-shadow 0.15s\",\n};\n\nfunction FlagIcon() {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z\" />\n <line x1=\"4\" y1=\"22\" x2=\"4\" y2=\"15\" />\n </svg>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n}\n\nfunction FlagRow({\n name,\n value,\n defaultValue,\n onToggle,\n onChange,\n}: {\n name: string;\n value: FlagValue;\n defaultValue: FlagValue;\n onToggle: () => void;\n onChange: (value: FlagValue) => void;\n}) {\n const isOverridden = value !== defaultValue;\n const isBoolean = typeof value === \"boolean\";\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n // Try to parse as number\n const num = Number(newValue);\n if (!isNaN(num) && newValue !== \"\") {\n onChange(num);\n } else {\n onChange(newValue);\n }\n };\n\n return (\n <div style={flagRowStyles}>\n <span style={flagNameStyles}>\n {name}\n {isOverridden && <span style={overrideBadgeStyles}>override</span>}\n </span>\n {isBoolean ? (\n <button\n style={value ? toggleActiveStyles : toggleStyles}\n onClick={onToggle}\n type=\"button\"\n aria-label={`Toggle ${name}`}\n >\n <span style={value ? toggleKnobActiveStyles : toggleKnobStyles} />\n </button>\n ) : (\n <input\n style={inputStyles}\n type=\"text\"\n value={String(value)}\n onChange={handleInputChange}\n />\n )}\n </div>\n );\n}\n\nfunction DevToolsInner({\n position,\n defaultOpen,\n}: {\n position: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\";\n defaultOpen: boolean;\n}) {\n const [isOpen, setIsOpen] = useState(defaultOpen);\n const flagState = useFlagState();\n const context = useFeatureFlagContext<FeatureFlags>();\n\n if (!flagState) {\n return null;\n }\n\n const { setFlag, resetFlags } = context;\n\n const { flags, defaultFlags } = flagState;\n const flagEntries = Object.entries(flags);\n const overrideCount = flagEntries.filter(\n ([key, value]) => value !== defaultFlags[key]\n ).length;\n\n const handleToggle = (key: string) => {\n const currentValue = flags[key];\n if (typeof currentValue === \"boolean\") {\n setFlag(key, !currentValue);\n }\n };\n\n const handleChange = (key: string, value: FlagValue) => {\n setFlag(key, value);\n };\n\n if (!isOpen) {\n return (\n <div style={{ ...baseStyles, ...positionStyles[position] }}>\n <button\n style={fabStyles}\n onClick={() => setIsOpen(true)}\n type=\"button\"\n aria-label=\"Open feature flags devtools\"\n >\n <span style={{ color: \"#1a1a2e\" }}>\n <FlagIcon />\n </span>\n </button>\n </div>\n );\n }\n\n return (\n <div style={{ ...baseStyles, ...positionStyles[position] }}>\n <div style={panelStyles}>\n <div style={headerStyles} onClick={() => setIsOpen(false)}>\n <span style={titleStyles}>\n <FlagIcon />\n Feature Flags\n {overrideCount > 0 && (\n <span style={badgeStyles}>{overrideCount}</span>\n )}\n </span>\n <button style={buttonStyles} type=\"button\" aria-label=\"Close\">\n <CloseIcon />\n </button>\n </div>\n <div style={contentStyles}>\n {flagEntries.map(([key, value]) => (\n <FlagRow\n key={key}\n name={key}\n value={value}\n defaultValue={defaultFlags[key]}\n onToggle={() => handleToggle(key)}\n onChange={(newValue) => handleChange(key, newValue)}\n />\n ))}\n </div>\n {overrideCount > 0 && (\n <button style={resetButtonStyles} onClick={resetFlags} type=\"button\">\n Reset all overrides\n </button>\n )}\n </div>\n </div>\n );\n}\n\nexport function DevTools({\n position = \"bottom-right\",\n defaultOpen = false,\n}: DevToolsProps) {\n // Don't render in production - check via global __DEV__ or fallback to always showing\n const isDev = typeof __DEV__ !== \"undefined\" ? __DEV__ : true;\n if (!isDev) {\n return null;\n }\n\n return <DevToolsInner position={position} defaultOpen={defaultOpen} />;\n}\n\nexport default DevTools;\n"],"mappings":"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","names":["value","useMemo","useMemo","useState","jsx","useState"]}
1
+ {"version":3,"sources":["../src/context.tsx","../src/hooks.ts","../src/devtools.tsx","../src/config.ts"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport type {\n FeatureFlagContextValue,\n FeatureFlagProviderProps,\n FeatureFlags,\n} from \"./types\";\n\nconst FeatureFlagContext = createContext<FeatureFlagContextValue | null>(null);\n\nconst STORAGE_KEY_DEFAULT = \"localflag:overrides\";\n\nfunction getStoredOverrides<T extends FeatureFlags>(\n storageKey: string\n): Partial<T> {\n if (typeof window === \"undefined\") return {};\n try {\n const stored = localStorage.getItem(storageKey);\n return stored ? JSON.parse(stored) : {};\n } catch {\n return {};\n }\n}\n\nfunction setStoredOverrides<T extends FeatureFlags>(\n storageKey: string,\n overrides: Partial<T>\n): void {\n if (typeof window === \"undefined\") return;\n try {\n if (Object.keys(overrides).length === 0) {\n localStorage.removeItem(storageKey);\n } else {\n localStorage.setItem(storageKey, JSON.stringify(overrides));\n }\n } catch {\n // Ignore storage errors\n }\n}\n\n// Event emitter for devtools communication\ntype Listener = () => void;\nconst listeners = new Set<Listener>();\nlet currentState: {\n flags: FeatureFlags;\n defaultFlags: FeatureFlags;\n descriptions: Record<string, string>;\n} | null = null;\n\nexport function subscribeToFlagChanges(listener: Listener): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getFlagState() {\n return currentState;\n}\n\nfunction notifyListeners() {\n listeners.forEach((listener) => listener());\n}\n\nexport function FeatureFlagProvider<T extends FeatureFlags>({\n children,\n defaultFlags,\n descriptions = {},\n storageKey = STORAGE_KEY_DEFAULT,\n persistOverrides = true,\n}: FeatureFlagProviderProps<T>) {\n const [overrides, setOverrides] = useState<Partial<T>>(() =>\n persistOverrides ? getStoredOverrides<T>(storageKey) : {}\n );\n\n const flags = useMemo(\n () => ({ ...defaultFlags, ...overrides }) as T,\n [defaultFlags, overrides]\n );\n\n // Update global state for devtools\n useEffect(() => {\n currentState = {\n flags,\n defaultFlags,\n descriptions: descriptions as Record<string, string>,\n };\n notifyListeners();\n }, [flags, defaultFlags, descriptions]);\n\n // Persist overrides to localStorage\n useEffect(() => {\n if (persistOverrides) {\n setStoredOverrides(storageKey, overrides);\n }\n }, [overrides, storageKey, persistOverrides]);\n\n const isEnabled = useCallback(\n (flagName: keyof T): boolean => {\n const value = flags[flagName];\n return Boolean(value);\n },\n [flags]\n );\n\n const getValue = useCallback(\n <K extends keyof T>(flagName: K): T[K] => {\n return flags[flagName];\n },\n [flags]\n );\n\n const setFlag = useCallback(<K extends keyof T>(flagName: K, value: T[K]) => {\n setOverrides((prev) => ({ ...prev, [flagName]: value }));\n }, []);\n\n const resetFlags = useCallback(() => {\n setOverrides({});\n }, []);\n\n const value = useMemo<FeatureFlagContextValue<T>>(\n () => ({\n flags,\n isEnabled,\n getValue,\n setFlag,\n resetFlags,\n }),\n [flags, isEnabled, getValue, setFlag, resetFlags]\n );\n\n return (\n <FeatureFlagContext.Provider value={value as unknown as FeatureFlagContextValue}>\n {children}\n </FeatureFlagContext.Provider>\n );\n}\n\nexport function useFeatureFlagContext<\n T extends FeatureFlags = FeatureFlags\n>(): FeatureFlagContextValue<T> {\n const context = useContext(FeatureFlagContext);\n if (!context) {\n throw new Error(\n \"useFeatureFlagContext must be used within a FeatureFlagProvider\"\n );\n }\n return context as unknown as FeatureFlagContextValue<T>;\n}\n\n// Hook for devtools to subscribe to flag changes\nexport function useFlagState() {\n return useSyncExternalStore(\n subscribeToFlagChanges,\n getFlagState,\n () => null\n );\n}\n","import { useMemo } from \"react\";\nimport { useFeatureFlagContext } from \"./context\";\nimport type { FeatureFlags } from \"./types\";\n\n/**\n * Hook to check if a feature flag is enabled (truthy)\n */\nexport function useFeatureFlag<T extends FeatureFlags = FeatureFlags>(\n flagName: keyof T\n): boolean {\n const { isEnabled } = useFeatureFlagContext<T>();\n return isEnabled(flagName);\n}\n\n/**\n * Hook to get the raw value of a feature flag\n */\nexport function useFeatureFlagValue<\n T extends FeatureFlags = FeatureFlags,\n K extends keyof T = keyof T\n>(flagName: K): T[K] {\n const { getValue } = useFeatureFlagContext<T>();\n return getValue(flagName);\n}\n\n/**\n * Hook to get all feature flags\n */\nexport function useFeatureFlags<T extends FeatureFlags = FeatureFlags>(): T {\n const { flags } = useFeatureFlagContext<T>();\n return flags;\n}\n\n/**\n * Hook to get flag controls (setFlag, resetFlags)\n */\nexport function useFeatureFlagControls<T extends FeatureFlags = FeatureFlags>() {\n const { setFlag, resetFlags } = useFeatureFlagContext<T>();\n return useMemo(() => ({ setFlag, resetFlags }), [setFlag, resetFlags]);\n}\n\n/**\n * Component wrapper that renders children only if flag is enabled\n */\nexport function FeatureFlag<T extends FeatureFlags = FeatureFlags>({\n flag,\n children,\n fallback = null,\n}: {\n flag: keyof T;\n children: React.ReactNode;\n fallback?: React.ReactNode;\n}): React.ReactNode {\n const enabled = useFeatureFlag<T>(flag);\n return enabled ? children : fallback;\n}\n","import { useState, useEffect } from \"react\";\nimport { useFlagState, useFeatureFlagContext } from \"./context\";\nimport type { FlagValue, FeatureFlags } from \"./types\";\n\ndeclare const __DEV__: boolean | undefined;\n\ntype Position = \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\";\n\ninterface DevToolsProps {\n position?: Position;\n defaultOpen?: boolean;\n /**\n * Keyboard shortcut to toggle the DevTools panel.\n * Set to `false` to disable the shortcut.\n * @default \"mod+shift+f\" (Cmd+Shift+F on Mac, Ctrl+Shift+F on Windows/Linux)\n */\n shortcut?: string | false;\n}\n\nconst POSITION_STORAGE_KEY = \"localflag:position\";\n\nconst positionStyles: Record<string, React.CSSProperties> = {\n \"bottom-right\": { bottom: 12, right: 12 },\n \"bottom-left\": { bottom: 12, left: 12 },\n \"top-right\": { top: 12, right: 12 },\n \"top-left\": { top: 12, left: 12 },\n};\n\nconst baseStyles: React.CSSProperties = {\n position: \"fixed\",\n zIndex: 99999,\n fontFamily:\n 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n fontSize: 12,\n lineHeight: 1.5,\n WebkitFontSmoothing: \"antialiased\",\n};\n\nconst panelStyles: React.CSSProperties = {\n backgroundColor: \"#0a0a0a\",\n border: \"1px solid rgba(255, 255, 255, 0.08)\",\n borderRadius: 10,\n boxShadow:\n \"0 0 0 1px rgba(0, 0, 0, 0.5), 0 16px 70px rgba(0, 0, 0, 0.6)\",\n overflow: \"hidden\",\n minWidth: 260,\n maxWidth: 320,\n maxHeight: \"60vh\",\n};\n\nconst headerStyles: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"10px 12px\",\n borderBottom: \"1px solid rgba(255, 255, 255, 0.06)\",\n cursor: \"pointer\",\n userSelect: \"none\",\n};\n\nconst titleStyles: React.CSSProperties = {\n color: \"rgba(255, 255, 255, 0.9)\",\n fontWeight: 500,\n fontSize: 11,\n letterSpacing: \"0.01em\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n};\n\nconst tabBarStyles: React.CSSProperties = {\n display: \"flex\",\n borderBottom: \"1px solid rgba(255, 255, 255, 0.06)\",\n padding: \"0 12px\",\n gap: 4,\n};\n\nconst tabStyles: React.CSSProperties = {\n padding: \"8px 10px\",\n fontSize: 11,\n fontWeight: 500,\n color: \"rgba(255, 255, 255, 0.5)\",\n backgroundColor: \"transparent\",\n border: \"none\",\n borderBottom: \"2px solid transparent\",\n cursor: \"pointer\",\n transition: \"color 0.15s ease\",\n fontFamily: \"inherit\",\n marginBottom: -1,\n};\n\nconst tabActiveStyles: React.CSSProperties = {\n ...tabStyles,\n color: \"rgba(255, 255, 255, 0.9)\",\n borderBottomColor: \"#fff\",\n};\n\nconst contentStyles: React.CSSProperties = {\n padding: 0,\n overflowY: \"auto\",\n maxHeight: \"calc(60vh - 84px)\",\n};\n\nconst flagRowStyles: React.CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"8px 12px\",\n borderBottom: \"1px solid rgba(255, 255, 255, 0.04)\",\n transition: \"background-color 0.15s ease\",\n gap: 12,\n};\n\nconst flagRowHoverStyles: React.CSSProperties = {\n backgroundColor: \"rgba(255, 255, 255, 0.03)\",\n};\n\nconst flagNameContainerStyles: React.CSSProperties = {\n flex: 1,\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: 2,\n minWidth: 0,\n};\n\nconst flagNameStyles: React.CSSProperties = {\n color: \"rgba(255, 255, 255, 0.7)\",\n fontSize: 12,\n fontWeight: 400,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 6,\n};\n\nconst flagDescriptionStyles: React.CSSProperties = {\n color: \"rgba(255, 255, 255, 0.4)\",\n fontSize: 10,\n fontWeight: 400,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n};\n\nconst toggleStyles: React.CSSProperties = {\n position: \"relative\",\n width: 28,\n height: 16,\n backgroundColor: \"rgba(255, 255, 255, 0.1)\",\n borderRadius: 8,\n cursor: \"pointer\",\n transition: \"background-color 0.15s ease\",\n flexShrink: 0,\n border: \"none\",\n padding: 0,\n};\n\nconst toggleActiveStyles: React.CSSProperties = {\n ...toggleStyles,\n backgroundColor: \"#fff\",\n};\n\nconst toggleKnobStyles: React.CSSProperties = {\n position: \"absolute\",\n top: 2,\n left: 2,\n width: 12,\n height: 12,\n backgroundColor: \"rgba(255, 255, 255, 0.5)\",\n borderRadius: \"50%\",\n transition: \"transform 0.15s ease, background-color 0.15s ease\",\n};\n\nconst toggleKnobActiveStyles: React.CSSProperties = {\n ...toggleKnobStyles,\n transform: \"translateX(12px)\",\n backgroundColor: \"#0a0a0a\",\n};\n\nconst inputStyles: React.CSSProperties = {\n backgroundColor: \"rgba(255, 255, 255, 0.05)\",\n border: \"1px solid rgba(255, 255, 255, 0.1)\",\n borderRadius: 5,\n color: \"rgba(255, 255, 255, 0.9)\",\n padding: \"3px 8px\",\n fontSize: 11,\n width: 72,\n outline: \"none\",\n transition: \"border-color 0.15s ease, background-color 0.15s ease\",\n};\n\nconst overrideDotStyles: React.CSSProperties = {\n width: 5,\n height: 5,\n borderRadius: \"50%\",\n backgroundColor: \"#3b82f6\",\n flexShrink: 0,\n};\n\nconst buttonStyles: React.CSSProperties = {\n backgroundColor: \"transparent\",\n border: \"none\",\n color: \"rgba(255, 255, 255, 0.4)\",\n cursor: \"pointer\",\n padding: 4,\n borderRadius: 4,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n transition: \"color 0.15s ease\",\n};\n\nconst resetButtonStyles: React.CSSProperties = {\n backgroundColor: \"rgba(255, 255, 255, 0.05)\",\n border: \"1px solid rgba(255, 255, 255, 0.08)\",\n borderRadius: 6,\n color: \"rgba(255, 255, 255, 0.6)\",\n padding: \"6px 10px\",\n fontSize: 11,\n fontWeight: 500,\n cursor: \"pointer\",\n margin: \"8px 12px 10px\",\n width: \"calc(100% - 24px)\",\n transition: \"background-color 0.15s ease, color 0.15s ease\",\n fontFamily: \"inherit\",\n};\n\nconst fabStyles: React.CSSProperties = {\n height: 32,\n paddingLeft: 10,\n paddingRight: 10,\n borderRadius: 8,\n backgroundColor: \"#0a0a0a\",\n border: \"1px solid rgba(255, 255, 255, 0.1)\",\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 6,\n boxShadow:\n \"0 0 0 1px rgba(0, 0, 0, 0.5), 0 4px 16px rgba(0, 0, 0, 0.4)\",\n transition: \"background-color 0.15s ease, border-color 0.15s ease\",\n fontFamily: \"inherit\",\n fontSize: 11,\n fontWeight: 500,\n color: \"rgba(255, 255, 255, 0.8)\",\n};\n\nconst badgeStyles: React.CSSProperties = {\n backgroundColor: \"#3b82f6\",\n color: \"#fff\",\n fontSize: 9,\n fontWeight: 600,\n minWidth: 14,\n height: 14,\n padding: \"0 4px\",\n borderRadius: 7,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\nconst optionsSectionStyles: React.CSSProperties = {\n padding: \"12px\",\n};\n\nconst optionsLabelStyles: React.CSSProperties = {\n fontSize: 10,\n fontWeight: 500,\n color: \"rgba(255, 255, 255, 0.5)\",\n textTransform: \"uppercase\",\n letterSpacing: \"0.05em\",\n marginBottom: 8,\n display: \"block\",\n};\n\nconst positionGridStyles: React.CSSProperties = {\n display: \"grid\",\n gridTemplateColumns: \"1fr 1fr\",\n gap: 6,\n};\n\nconst positionButtonStyles: React.CSSProperties = {\n padding: \"8px\",\n fontSize: 10,\n fontWeight: 500,\n color: \"rgba(255, 255, 255, 0.6)\",\n backgroundColor: \"rgba(255, 255, 255, 0.03)\",\n border: \"1px solid rgba(255, 255, 255, 0.08)\",\n borderRadius: 6,\n cursor: \"pointer\",\n transition: \"all 0.15s ease\",\n fontFamily: \"inherit\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 6,\n};\n\nconst positionButtonActiveStyles: React.CSSProperties = {\n ...positionButtonStyles,\n color: \"rgba(255, 255, 255, 0.9)\",\n backgroundColor: \"rgba(255, 255, 255, 0.1)\",\n borderColor: \"rgba(255, 255, 255, 0.2)\",\n};\n\nfunction FlagIcon({ size = 14 }: { size?: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z\" />\n <line x1=\"4\" y1=\"22\" x2=\"4\" y2=\"15\" />\n </svg>\n );\n}\n\nfunction ChevronIcon({ direction }: { direction: \"up\" | \"down\" }) {\n return (\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{\n transform: direction === \"up\" ? \"rotate(180deg)\" : \"rotate(0deg)\",\n transition: \"transform 0.15s ease\",\n }}\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n );\n}\n\nfunction PositionIcon({ position }: { position: Position }) {\n const dotPosition = {\n \"top-left\": { top: 2, left: 2 },\n \"top-right\": { top: 2, right: 2 },\n \"bottom-left\": { bottom: 2, left: 2 },\n \"bottom-right\": { bottom: 2, right: 2 },\n }[position];\n\n return (\n <div\n style={{\n width: 14,\n height: 14,\n border: \"1px solid currentColor\",\n borderRadius: 2,\n position: \"relative\",\n opacity: 0.7,\n }}\n >\n <div\n style={{\n position: \"absolute\",\n width: 4,\n height: 4,\n backgroundColor: \"currentColor\",\n borderRadius: 1,\n ...dotPosition,\n }}\n />\n </div>\n );\n}\n\nfunction FlagRow({\n name,\n value,\n defaultValue,\n description,\n onToggle,\n onChange,\n}: {\n name: string;\n value: FlagValue;\n defaultValue: FlagValue;\n description?: string;\n onToggle: () => void;\n onChange: (value: FlagValue) => void;\n}) {\n const [isHovered, setIsHovered] = useState(false);\n const isOverridden = value !== defaultValue;\n const isBoolean = typeof value === \"boolean\";\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n const num = Number(newValue);\n if (!isNaN(num) && newValue !== \"\") {\n onChange(num);\n } else {\n onChange(newValue);\n }\n };\n\n return (\n <div\n style={{\n ...flagRowStyles,\n ...(isHovered ? flagRowHoverStyles : {}),\n }}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n <div style={flagNameContainerStyles}>\n <span style={flagNameStyles}>\n {isOverridden && <span style={overrideDotStyles} />}\n {name}\n </span>\n {description && (\n <span style={flagDescriptionStyles}>{description}</span>\n )}\n </div>\n {isBoolean ? (\n <button\n style={value ? toggleActiveStyles : toggleStyles}\n onClick={onToggle}\n type=\"button\"\n aria-label={`Toggle ${name}`}\n >\n <span style={value ? toggleKnobActiveStyles : toggleKnobStyles} />\n </button>\n ) : (\n <input\n style={inputStyles}\n type=\"text\"\n value={String(value)}\n onChange={handleInputChange}\n onFocus={(e) => {\n e.target.style.borderColor = \"rgba(255, 255, 255, 0.2)\";\n e.target.style.backgroundColor = \"rgba(255, 255, 255, 0.08)\";\n }}\n onBlur={(e) => {\n e.target.style.borderColor = \"rgba(255, 255, 255, 0.1)\";\n e.target.style.backgroundColor = \"rgba(255, 255, 255, 0.05)\";\n }}\n />\n )}\n </div>\n );\n}\n\nfunction OptionsTab({\n position,\n onPositionChange,\n}: {\n position: Position;\n onPositionChange: (position: Position) => void;\n}) {\n const [hoveredPosition, setHoveredPosition] = useState<Position | null>(null);\n const positions: { value: Position; label: string }[] = [\n { value: \"top-left\", label: \"Top Left\" },\n { value: \"top-right\", label: \"Top Right\" },\n { value: \"bottom-left\", label: \"Bottom Left\" },\n { value: \"bottom-right\", label: \"Bottom Right\" },\n ];\n\n return (\n <div style={optionsSectionStyles}>\n <span style={optionsLabelStyles}>Position</span>\n <div style={positionGridStyles}>\n {positions.map(({ value, label }) => {\n const isActive = position === value;\n const isHovered = hoveredPosition === value;\n return (\n <button\n key={value}\n style={{\n ...(isActive ? positionButtonActiveStyles : positionButtonStyles),\n ...(isHovered && !isActive\n ? {\n backgroundColor: \"rgba(255, 255, 255, 0.05)\",\n borderColor: \"rgba(255, 255, 255, 0.12)\",\n }\n : {}),\n }}\n onClick={() => onPositionChange(value)}\n onMouseEnter={() => setHoveredPosition(value)}\n onMouseLeave={() => setHoveredPosition(null)}\n type=\"button\"\n >\n <PositionIcon position={value} />\n {label}\n </button>\n );\n })}\n </div>\n </div>\n );\n}\n\nfunction DevToolsInner({\n position: initialPosition,\n defaultOpen,\n shortcut,\n}: {\n position: Position;\n defaultOpen: boolean;\n shortcut: string | false;\n}) {\n const [isOpen, setIsOpen] = useState(defaultOpen);\n const [activeTab, setActiveTab] = useState<\"flags\" | \"options\">(\"flags\");\n const [position, setPosition] = useState<Position>(() => {\n if (typeof window !== \"undefined\") {\n const stored = localStorage.getItem(POSITION_STORAGE_KEY);\n if (stored && stored in positionStyles) {\n return stored as Position;\n }\n }\n return initialPosition;\n });\n const [fabHovered, setFabHovered] = useState(false);\n const [resetHovered, setResetHovered] = useState(false);\n const [headerHovered, setHeaderHovered] = useState(false);\n const flagState = useFlagState();\n const context = useFeatureFlagContext<FeatureFlags>();\n\n // Keyboard shortcut\n useEffect(() => {\n if (shortcut === false) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const isMod = e.metaKey || e.ctrlKey;\n if (isMod && e.shiftKey && e.key.toLowerCase() === \"f\") {\n e.preventDefault();\n setIsOpen((prev) => !prev);\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [shortcut]);\n\n useEffect(() => {\n if (typeof window !== \"undefined\") {\n localStorage.setItem(POSITION_STORAGE_KEY, position);\n }\n }, [position]);\n\n if (!flagState) {\n return null;\n }\n\n const { setFlag, resetFlags } = context;\n const { flags, defaultFlags, descriptions } = flagState;\n const flagEntries = Object.entries(flags);\n const overrideCount = flagEntries.filter(\n ([key, value]) => value !== defaultFlags[key]\n ).length;\n\n const handleToggle = (key: string) => {\n const currentValue = flags[key];\n if (typeof currentValue === \"boolean\") {\n setFlag(key, !currentValue);\n }\n };\n\n const handleChange = (key: string, value: FlagValue) => {\n setFlag(key, value);\n };\n\n if (!isOpen) {\n return (\n <div style={{ ...baseStyles, ...positionStyles[position] }}>\n <button\n style={{\n ...fabStyles,\n ...(fabHovered\n ? {\n backgroundColor: \"#141414\",\n borderColor: \"rgba(255, 255, 255, 0.15)\",\n }\n : {}),\n }}\n onClick={() => setIsOpen(true)}\n onMouseEnter={() => setFabHovered(true)}\n onMouseLeave={() => setFabHovered(false)}\n type=\"button\"\n aria-label=\"Open feature flags devtools\"\n >\n <FlagIcon size={12} />\n <span>Flags</span>\n {overrideCount > 0 && <span style={badgeStyles}>{overrideCount}</span>}\n </button>\n </div>\n );\n }\n\n return (\n <div style={{ ...baseStyles, ...positionStyles[position] }}>\n <div style={panelStyles}>\n <div\n style={{\n ...headerStyles,\n ...(headerHovered\n ? { backgroundColor: \"rgba(255, 255, 255, 0.02)\" }\n : {}),\n }}\n onClick={() => setIsOpen(false)}\n onMouseEnter={() => setHeaderHovered(true)}\n onMouseLeave={() => setHeaderHovered(false)}\n >\n <span style={titleStyles}>\n <FlagIcon size={12} />\n Feature Flags\n {overrideCount > 0 && <span style={badgeStyles}>{overrideCount}</span>}\n </span>\n <button\n style={{\n ...buttonStyles,\n ...(headerHovered ? { color: \"rgba(255, 255, 255, 0.6)\" } : {}),\n }}\n type=\"button\"\n aria-label=\"Close\"\n >\n <ChevronIcon direction=\"down\" />\n </button>\n </div>\n\n <div style={tabBarStyles}>\n <button\n style={activeTab === \"flags\" ? tabActiveStyles : tabStyles}\n onClick={(e) => {\n e.stopPropagation();\n setActiveTab(\"flags\");\n }}\n type=\"button\"\n >\n Flags\n </button>\n <button\n style={activeTab === \"options\" ? tabActiveStyles : tabStyles}\n onClick={(e) => {\n e.stopPropagation();\n setActiveTab(\"options\");\n }}\n type=\"button\"\n >\n Options\n </button>\n </div>\n\n {activeTab === \"flags\" ? (\n <>\n <div style={contentStyles}>\n {flagEntries.map(([key, value]) => (\n <FlagRow\n key={key}\n name={key}\n value={value}\n defaultValue={defaultFlags[key]}\n description={descriptions[key]}\n onToggle={() => handleToggle(key)}\n onChange={(newValue) => handleChange(key, newValue)}\n />\n ))}\n </div>\n {overrideCount > 0 && (\n <button\n style={{\n ...resetButtonStyles,\n ...(resetHovered\n ? {\n backgroundColor: \"rgba(255, 255, 255, 0.08)\",\n color: \"rgba(255, 255, 255, 0.8)\",\n }\n : {}),\n }}\n onClick={resetFlags}\n onMouseEnter={() => setResetHovered(true)}\n onMouseLeave={() => setResetHovered(false)}\n type=\"button\"\n >\n Reset overrides\n </button>\n )}\n </>\n ) : (\n <OptionsTab position={position} onPositionChange={setPosition} />\n )}\n </div>\n </div>\n );\n}\n\nexport function LocalFlagDevTools({\n position = \"bottom-right\",\n defaultOpen = false,\n shortcut = \"mod+shift+f\",\n}: DevToolsProps) {\n const isDev = typeof __DEV__ !== \"undefined\" ? __DEV__ : true;\n if (!isDev) {\n return null;\n }\n\n return (\n <DevToolsInner\n position={position}\n defaultOpen={defaultOpen}\n shortcut={shortcut}\n />\n );\n}\n\n/** @deprecated Use `LocalFlagDevTools` instead */\nexport const DevTools = LocalFlagDevTools;\n\nexport default LocalFlagDevTools;\n","import type { FlagValue } from \"./types\";\n\n/**\n * Helper function to define feature flags with type safety.\n * This is a type-only helper with no runtime logic - it simply returns the flags as-is.\n *\n * @example\n * ```ts\n * // flags.config.ts\n * import { defineFlags } from '@localflag/react';\n *\n * export const flags = defineFlags({\n * darkMode: false,\n * newCheckout: true,\n * maxRetries: 3,\n * });\n *\n * export type AppFlags = typeof flags;\n * ```\n */\nexport function defineFlags<T extends Record<string, FlagValue>>(flags: T): T {\n return flags;\n}\n\n/**\n * Flag configuration with value and optional description.\n */\nexport interface FlagConfig<T extends FlagValue = FlagValue> {\n value: T;\n description?: string;\n}\n\ntype FlagInput<T extends FlagValue = FlagValue> = T | FlagConfig<T>;\n\ntype ExtractFlagValue<T> = T extends FlagConfig<infer V> ? V : T;\n\ntype ExtractFlags<T extends Record<string, FlagInput>> = {\n [K in keyof T]: ExtractFlagValue<T[K]>;\n};\n\n/**\n * Helper function to define feature flags with descriptions.\n * Returns both the flags object and a descriptions object.\n *\n * @example\n * ```ts\n * // flags.config.ts\n * import { createFlags } from '@localflag/react';\n *\n * export const { flags, descriptions } = createFlags({\n * darkMode: { value: false, description: \"Enable dark theme\" },\n * newCheckout: { value: true, description: \"Use new checkout flow\" },\n * maxRetries: 3, // Simple value without description\n * });\n *\n * export type AppFlags = typeof flags;\n * ```\n */\nexport function createFlags<T extends Record<string, FlagInput>>(\n config: T\n): {\n flags: ExtractFlags<T>;\n descriptions: Partial<Record<keyof T, string>>;\n} {\n const flags = {} as Record<string, FlagValue>;\n const descriptions = {} as Record<string, string>;\n\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === \"object\" && value !== null && \"value\" in value) {\n flags[key] = value.value;\n if (value.description) {\n descriptions[key] = value.description;\n }\n } else {\n flags[key] = value as FlagValue;\n }\n }\n\n return {\n flags: flags as ExtractFlags<T>,\n descriptions: descriptions as Partial<Record<keyof T, string>>,\n };\n}\n"],"mappings":"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","names":["value","useMemo","useMemo","useState","useEffect","jsx","useState","useEffect"]}
package/package.json CHANGED
@@ -1,7 +1,27 @@
1
1
  {
2
2
  "name": "@localflag/react",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
+ "description": "Type-safe feature flags for React with built-in DevTools",
4
5
  "type": "module",
6
+ "license": "MIT",
7
+ "author": "Daniel Gietmann",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/localflag/localflag.git",
11
+ "directory": "packages/react"
12
+ },
13
+ "homepage": "https://localflag.io",
14
+ "bugs": {
15
+ "url": "https://github.com/localflag/localflag/issues"
16
+ },
17
+ "keywords": [
18
+ "react",
19
+ "feature-flags",
20
+ "feature-toggles",
21
+ "devtools",
22
+ "typescript",
23
+ "hooks"
24
+ ],
5
25
  "main": "./dist/index.js",
6
26
  "module": "./dist/index.js",
7
27
  "types": "./dist/index.d.ts",